From 6ff9070eca1c6a984968987217c0fff9987ce0a9 Mon Sep 17 00:00:00 2001 From: czz <862977248@qq.com> Date: Fri, 8 May 2026 11:22:59 +0800 Subject: [PATCH] feat: add mine page and secondary pages --- .../2026-05-08-mine-page-implementation.md | 84 ++++ src/app.config.ts | 14 +- src/components/secondary-page/index.scss | 128 ++++++ src/components/secondary-page/index.tsx | 56 +++ src/pages/devices/index.config.ts | 3 + src/pages/devices/index.scss | 3 + src/pages/devices/index.tsx | 29 ++ src/pages/feedback/index.config.ts | 3 + src/pages/feedback/index.scss | 3 + src/pages/feedback/index.tsx | 28 ++ src/pages/follow-us/index.config.ts | 3 + src/pages/follow-us/index.scss | 3 + src/pages/follow-us/index.tsx | 25 + src/pages/index/index.tsx | 6 + src/pages/mine/index.config.ts | 4 + src/pages/mine/index.scss | 429 ++++++++++++++++++ src/pages/mine/index.tsx | 180 ++++++++ src/pages/profile/index.config.ts | 3 + src/pages/profile/index.scss | 3 + src/pages/profile/index.tsx | 26 ++ src/pages/repair/index.config.ts | 3 + src/pages/repair/index.scss | 3 + src/pages/repair/index.tsx | 28 ++ src/pages/settings/index.config.ts | 3 + src/pages/settings/index.scss | 3 + src/pages/settings/index.tsx | 31 ++ src/pages/support/index.config.ts | 3 + src/pages/support/index.scss | 3 + src/pages/support/index.tsx | 24 + src/pages/videos/index.config.ts | 3 + src/pages/videos/index.scss | 3 + src/pages/videos/index.tsx | 24 + 32 files changed, 1163 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/plans/2026-05-08-mine-page-implementation.md create mode 100644 src/components/secondary-page/index.scss create mode 100644 src/components/secondary-page/index.tsx create mode 100644 src/pages/devices/index.config.ts create mode 100644 src/pages/devices/index.scss create mode 100644 src/pages/devices/index.tsx create mode 100644 src/pages/feedback/index.config.ts create mode 100644 src/pages/feedback/index.scss create mode 100644 src/pages/feedback/index.tsx create mode 100644 src/pages/follow-us/index.config.ts create mode 100644 src/pages/follow-us/index.scss create mode 100644 src/pages/follow-us/index.tsx create mode 100644 src/pages/mine/index.config.ts create mode 100644 src/pages/mine/index.scss create mode 100644 src/pages/mine/index.tsx create mode 100644 src/pages/profile/index.config.ts create mode 100644 src/pages/profile/index.scss create mode 100644 src/pages/profile/index.tsx create mode 100644 src/pages/repair/index.config.ts create mode 100644 src/pages/repair/index.scss create mode 100644 src/pages/repair/index.tsx create mode 100644 src/pages/settings/index.config.ts create mode 100644 src/pages/settings/index.scss create mode 100644 src/pages/settings/index.tsx create mode 100644 src/pages/support/index.config.ts create mode 100644 src/pages/support/index.scss create mode 100644 src/pages/support/index.tsx create mode 100644 src/pages/videos/index.config.ts create mode 100644 src/pages/videos/index.scss create mode 100644 src/pages/videos/index.tsx diff --git a/docs/superpowers/plans/2026-05-08-mine-page-implementation.md b/docs/superpowers/plans/2026-05-08-mine-page-implementation.md new file mode 100644 index 0000000..1856301 --- /dev/null +++ b/docs/superpowers/plans/2026-05-08-mine-page-implementation.md @@ -0,0 +1,84 @@ +# Mine Page Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 为小程序新增“我的”主页面和其二级页面骨架,完成页面注册、页面跳转和深色视觉落地。 + +**Architecture:** 保持当前项目的轻量结构,直接在 `src/pages/` 下新增页面目录,不引入状态管理或新 UI 框架。主页面使用本地 mock 数据渲染,二级页面复用统一的深色卡片布局和轻量占位文案,验证以 `npm run build:weapp` 为主。 + +**Tech Stack:** Taro 4、React 18、TypeScript、SCSS + +--- + +### Task 1: 注册页面与整理导航入口 + +**Files:** +- Modify: `src/app.config.ts` +- Modify: `src/pages/index/index.tsx` + +- [ ] 在 `src/app.config.ts` 中注册 `mine` 主页面及 `profile`、`settings`、`support`、`devices`、`repair`、`feedback`、`videos`、`follow-us` 二级页面 +- [ ] 将首页底部导航中的“我的”入口改为跳转到 `pages/mine/index` +- [ ] 保留首页其余入口当前占位逻辑,不扩展其他 Tab 页面 + +### Task 2: 实现“我的”主页面 + +**Files:** +- Create: `src/pages/mine/index.config.ts` +- Create: `src/pages/mine/index.tsx` +- Create: `src/pages/mine/index.scss` + +- [ ] 新建“我的”主页面配置,设置页面标题 +- [ ] 在 `index.tsx` 中定义本地 mock 用户数据、功能列表数据和页面跳转逻辑 +- [ ] 实现顶部用户信息卡、右侧快捷入口、功能列表和版本检查提示 +- [ ] 保持代码对新手友好,复杂映射处仅补充必要注释 + +### Task 3: 实现二级页面骨架 + +**Files:** +- Create: `src/pages/profile/index.config.ts` +- Create: `src/pages/profile/index.tsx` +- Create: `src/pages/profile/index.scss` +- Create: `src/pages/settings/index.config.ts` +- Create: `src/pages/settings/index.tsx` +- Create: `src/pages/settings/index.scss` +- Create: `src/pages/support/index.config.ts` +- Create: `src/pages/support/index.tsx` +- Create: `src/pages/support/index.scss` +- Create: `src/pages/devices/index.config.ts` +- Create: `src/pages/devices/index.tsx` +- Create: `src/pages/devices/index.scss` +- Create: `src/pages/repair/index.config.ts` +- Create: `src/pages/repair/index.tsx` +- Create: `src/pages/repair/index.scss` +- Create: `src/pages/feedback/index.config.ts` +- Create: `src/pages/feedback/index.tsx` +- Create: `src/pages/feedback/index.scss` +- Create: `src/pages/videos/index.config.ts` +- Create: `src/pages/videos/index.tsx` +- Create: `src/pages/videos/index.scss` +- Create: `src/pages/follow-us/index.config.ts` +- Create: `src/pages/follow-us/index.tsx` +- Create: `src/pages/follow-us/index.scss` + +- [ ] 为每个二级页面补齐标题、卡片布局和占位内容 +- [ ] 保持二级页面视觉与主页面一致,但布局更简洁 +- [ ] 确保所有从“我的”页发起的跳转都能进入对应页面 + +### Task 4: 样式收口与验证 + +**Files:** +- Modify: `src/app.scss` +- Modify: `src/pages/index/index.tsx` +- Modify: `src/pages/mine/index.scss` +- Modify: `src/pages/profile/index.scss` +- Modify: `src/pages/settings/index.scss` +- Modify: `src/pages/support/index.scss` +- Modify: `src/pages/devices/index.scss` +- Modify: `src/pages/repair/index.scss` +- Modify: `src/pages/feedback/index.scss` +- Modify: `src/pages/videos/index.scss` +- Modify: `src/pages/follow-us/index.scss` + +- [ ] 如有必要,补充少量全局基础样式,避免和现有页面冲突 +- [ ] 运行 `npm run build:weapp` +- [ ] 检查构建输出,确认页面注册、TS 代码和 SCSS 都能通过编译 diff --git a/src/app.config.ts b/src/app.config.ts index 8fb4fc0..735d4a8 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -1,5 +1,17 @@ export default defineAppConfig({ - pages: ["pages/index/index", "pages/message/index"], + pages: [ + "pages/index/index", + "pages/message/index", + "pages/mine/index", + "pages/profile/index", + "pages/settings/index", + "pages/support/index", + "pages/devices/index", + "pages/repair/index", + "pages/feedback/index", + "pages/videos/index", + "pages/follow-us/index" + ], window: { navigationBarTitleText: "新手小程序", navigationBarBackgroundColor: "#1AAD19", diff --git a/src/components/secondary-page/index.scss b/src/components/secondary-page/index.scss new file mode 100644 index 0000000..fd5b297 --- /dev/null +++ b/src/components/secondary-page/index.scss @@ -0,0 +1,128 @@ +.secondary-page { + position: relative; + min-height: 100vh; + padding: 32rpx 24rpx 48rpx; + box-sizing: border-box; + background: linear-gradient(180deg, #1e2432 0%, #171d29 100%); + overflow: hidden; +} + +.secondary-page__halo { + position: absolute; + border-radius: 50%; + pointer-events: none; +} + +.secondary-page__halo--large { + top: -80rpx; + right: -44rpx; + width: 320rpx; + height: 320rpx; + background: + radial-gradient(circle, rgba(54, 228, 170, 0.08) 0 24%, transparent 25% 44%, rgba(109, 121, 154, 0.16) 45% 62%, transparent 63%), + radial-gradient(circle at center, rgba(255, 255, 255, 0.06), transparent 72%); +} + +.secondary-page__halo--small { + top: 180rpx; + right: 90rpx; + width: 160rpx; + height: 120rpx; + background: radial-gradient(circle at center, rgba(255, 255, 255, 0.05), transparent 68%); +} + +.secondary-page__hero { + position: relative; + z-index: 1; + margin-bottom: 28rpx; + padding: 32rpx 30rpx; + border-radius: 28rpx; + background: rgba(39, 46, 64, 0.95); + box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.03); +} + +.secondary-page__eyebrow { + display: block; + margin-bottom: 12rpx; + color: #40deb4; + font-size: 22rpx; + letter-spacing: 4rpx; +} + +.secondary-page__title { + display: block; + color: #f6f8fb; + font-size: 42rpx; + font-weight: 600; +} + +.secondary-page__description { + display: block; + margin-top: 14rpx; + color: #9aa5bb; + font-size: 24rpx; + line-height: 1.7; +} + +.secondary-page__content { + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + gap: 20rpx; +} + +.secondary-page__card { + padding: 28rpx; + border-radius: 24rpx; + background: rgba(39, 46, 64, 0.94); + box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.03); +} + +.secondary-page__card-title { + display: block; + margin-bottom: 20rpx; + color: #eef3fb; + font-size: 28rpx; + font-weight: 600; +} + +.secondary-page__list { + display: flex; + flex-direction: column; + gap: 2rpx; +} + +.secondary-page__list-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 20rpx 0; + border-bottom: 1rpx solid rgba(255, 255, 255, 0.06); +} + +.secondary-page__list-item:last-child { + padding-bottom: 0; + border-bottom: 0; +} + +.secondary-page__list-label { + color: #edf1f7; + font-size: 26rpx; +} + +.secondary-page__list-value { + color: #7c879d; + font-size: 22rpx; +} + +.secondary-page__footer-tip { + position: relative; + z-index: 1; + display: block; + margin-top: 22rpx; + color: #7d88a1; + font-size: 22rpx; + line-height: 1.7; + text-align: center; +} diff --git a/src/components/secondary-page/index.tsx b/src/components/secondary-page/index.tsx new file mode 100644 index 0000000..4ee6118 --- /dev/null +++ b/src/components/secondary-page/index.tsx @@ -0,0 +1,56 @@ +import { Text, View } from "@tarojs/components"; +import "./index.scss"; + +export type SecondaryPageItem = { + label: string; + value?: string; +}; + +export type SecondaryPageSection = { + title: string; + items: SecondaryPageItem[]; +}; + +type SecondaryPageProps = { + eyebrow: string; + title: string; + description: string; + sections: SecondaryPageSection[]; + footerTip?: string; +}; + +export default function SecondaryPage(props: SecondaryPageProps) { + const { eyebrow, title, description, sections, footerTip } = props; + + return ( + + + + + + {eyebrow} + {title} + {description} + + + + {sections.map((section) => ( + + {section.title} + + + {section.items.map((item) => ( + + {item.label} + {item.value || "待接入"} + + ))} + + + ))} + + + {footerTip ? {footerTip} : null} + + ); +} diff --git a/src/pages/devices/index.config.ts b/src/pages/devices/index.config.ts new file mode 100644 index 0000000..0e36edb --- /dev/null +++ b/src/pages/devices/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "我的设备" +}); diff --git a/src/pages/devices/index.scss b/src/pages/devices/index.scss new file mode 100644 index 0000000..2971b8b --- /dev/null +++ b/src/pages/devices/index.scss @@ -0,0 +1,3 @@ +.devices-page { + display: block; +} diff --git a/src/pages/devices/index.tsx b/src/pages/devices/index.tsx new file mode 100644 index 0000000..4126261 --- /dev/null +++ b/src/pages/devices/index.tsx @@ -0,0 +1,29 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "设备概览", + items: [ + { label: "已绑定设备", value: "0 台" }, + { label: "设备共享管理", value: "待接入" }, + { label: "添加设备", value: "可复用首页绑定能力" } + ] + }, + { + title: "当前状态", + items: [{ label: "设备列表", value: "暂无已绑定设备" }] + } +]; + +export default function DevicesPage() { + return ( + + ); +} diff --git a/src/pages/feedback/index.config.ts b/src/pages/feedback/index.config.ts new file mode 100644 index 0000000..b4f3e1f --- /dev/null +++ b/src/pages/feedback/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "问题反馈" +}); diff --git a/src/pages/feedback/index.scss b/src/pages/feedback/index.scss new file mode 100644 index 0000000..065f769 --- /dev/null +++ b/src/pages/feedback/index.scss @@ -0,0 +1,3 @@ +.feedback-page { + display: block; +} diff --git a/src/pages/feedback/index.tsx b/src/pages/feedback/index.tsx new file mode 100644 index 0000000..cead7e3 --- /dev/null +++ b/src/pages/feedback/index.tsx @@ -0,0 +1,28 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "反馈入口", + items: [ + { label: "提交问题" }, + { label: "提交建议" }, + { label: "上传截图", value: "待接入上传能力" } + ] + }, + { + title: "记录查询", + items: [{ label: "反馈记录", value: "后续展示处理进度" }] + } +]; + +export default function FeedbackPage() { + return ( + + ); +} diff --git a/src/pages/follow-us/index.config.ts b/src/pages/follow-us/index.config.ts new file mode 100644 index 0000000..e8bd5eb --- /dev/null +++ b/src/pages/follow-us/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "关注我们" +}); diff --git a/src/pages/follow-us/index.scss b/src/pages/follow-us/index.scss new file mode 100644 index 0000000..d95b565 --- /dev/null +++ b/src/pages/follow-us/index.scss @@ -0,0 +1,3 @@ +.follow-us-page { + display: block; +} diff --git a/src/pages/follow-us/index.tsx b/src/pages/follow-us/index.tsx new file mode 100644 index 0000000..fab3c62 --- /dev/null +++ b/src/pages/follow-us/index.tsx @@ -0,0 +1,25 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "官方入口", + items: [ + { label: "官方公众号", value: "待补充二维码" }, + { label: "官方网站", value: "待补充地址" }, + { label: "联系方式", value: "待补充电话或邮箱" }, + { label: "社区入口", value: "待补充链接" } + ] + } +]; + +export default function FollowUsPage() { + return ( + + ); +} diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index b45b8d3..6c2d139 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -28,6 +28,7 @@ const navItems: TabbarItem[] = [ { key: "message", label: "消息", active: false, badge: true }, { key: "mine", label: "我的", active: false } ]; + const mockBluetoothDevices: DeviceCandidate[] = [ { id: "mock-thermo-01", name: "体征监测设备 A1", source: "mock" }, { id: "mock-thermo-02", name: "体征监测设备 B2", source: "mock" } @@ -124,6 +125,11 @@ export default function Index() { return; } + if (item.key === "mine") { + Taro.navigateTo({ url: "/pages/mine/index" }); + return; + } + showToast(`${item.label}功能待接入`); }; diff --git a/src/pages/mine/index.config.ts b/src/pages/mine/index.config.ts new file mode 100644 index 0000000..4f9b3ca --- /dev/null +++ b/src/pages/mine/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationStyle: "custom", + navigationBarTitleText: "我的" +}); diff --git a/src/pages/mine/index.scss b/src/pages/mine/index.scss new file mode 100644 index 0000000..f53b738 --- /dev/null +++ b/src/pages/mine/index.scss @@ -0,0 +1,429 @@ +.mine-page { + position: relative; + min-height: 100vh; + padding: calc(var(--top-safe-height, 0px) + 34rpx) 24rpx 170rpx; + box-sizing: border-box; + background: linear-gradient(180deg, #1f2534 0%, #171d29 100%); + overflow: hidden; +} + +.mine-page__halo { + position: absolute; + border-radius: 50%; + pointer-events: none; +} + +.mine-page__halo--large { + top: -110rpx; + right: -34rpx; + width: 360rpx; + height: 360rpx; + background: + radial-gradient(circle, rgba(54, 228, 170, 0.06) 0 24%, transparent 25% 42%, rgba(110, 121, 151, 0.14) 43% 62%, transparent 63%), + radial-gradient(circle at center, rgba(255, 255, 255, 0.05), transparent 72%); +} + +.mine-page__halo--small { + top: 90rpx; + right: 88rpx; + width: 150rpx; + height: 110rpx; + background: radial-gradient(circle at center, rgba(255, 255, 255, 0.04), transparent 70%); +} + +.mine-page__header-card { + position: relative; + z-index: 1; + display: flex; + justify-content: space-between; + padding: 34rpx 28rpx 30rpx; + margin-bottom: 22rpx; + border-radius: 0 0 28rpx 28rpx; + background: rgba(39, 46, 64, 0.96); + box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.03); +} + +.mine-page__header-main { + display: flex; + align-items: center; + flex: 1; + min-width: 0; +} + +.mine-page__avatar { + display: flex; + align-items: center; + justify-content: center; + width: 104rpx; + height: 104rpx; + margin-right: 24rpx; + border-radius: 50%; + background: linear-gradient(180deg, rgba(170, 177, 192, 0.55) 0%, rgba(131, 141, 159, 0.75) 100%); +} + +.mine-page__avatar-placeholder { + position: relative; + width: 56rpx; + height: 56rpx; +} + +.mine-page__avatar-head, +.mine-page__avatar-body { + position: absolute; + left: 50%; + background: rgba(238, 240, 244, 0.72); + transform: translateX(-50%); +} + +.mine-page__avatar-head { + top: 0; + width: 24rpx; + height: 24rpx; + border-radius: 50%; +} + +.mine-page__avatar-body { + bottom: 0; + width: 48rpx; + height: 26rpx; + border-radius: 26rpx 26rpx 20rpx 20rpx; +} + +.mine-page__identity { + display: flex; + flex: 1; + flex-direction: column; + min-width: 0; +} + +.mine-page__name { + color: #f4f7fb; + font-size: 34rpx; + font-weight: 600; +} + +.mine-page__phone { + margin-top: 10rpx; + color: #c2cad9; + font-size: 24rpx; +} + +.mine-page__vip-row { + display: flex; + align-items: center; + margin-top: 12rpx; + gap: 10rpx; +} + +.mine-page__vip-badge { + color: #f4a55c; + font-size: 22rpx; + font-style: italic; +} + +.mine-page__vip-text, +.mine-page__vip-count { + color: #11d1ba; + font-size: 20rpx; +} + +.mine-page__header-actions { + display: flex; + flex-direction: column; + align-items: flex-end; + padding-left: 18rpx; + padding-right: calc(var(--menu-safe-width, 0px) + 6rpx); +} + +.mine-page__icon-actions { + display: flex; + align-items: center; + gap: 16rpx; +} + +.mine-page__circle-action { + position: relative; + width: 34rpx; + height: 34rpx; + opacity: 0.9; +} + +.mine-page__circle-action::before, +.mine-page__circle-action::after { + position: absolute; + content: ""; +} + +.mine-page__circle-action--support::before { + inset: 7rpx 4rpx 10rpx; + border: 2rpx solid rgba(255, 255, 255, 0.84); + border-bottom: 0; + border-radius: 20rpx 20rpx 0 0; +} + +.mine-page__circle-action--support::after { + left: 12rpx; + right: 12rpx; + bottom: 6rpx; + height: 10rpx; + border-left: 2rpx solid rgba(255, 255, 255, 0.84); + border-right: 2rpx solid rgba(255, 255, 255, 0.84); + border-radius: 0 0 10rpx 10rpx; +} + +.mine-page__circle-action--settings::before { + inset: 8rpx; + border: 2rpx solid rgba(255, 255, 255, 0.84); + border-radius: 50%; +} + +.mine-page__circle-action--settings::after { + inset: 3rpx; + border: 2rpx dashed rgba(255, 255, 255, 0.56); + border-radius: 50%; +} + +.mine-page__profile-link { + margin-top: 56rpx; + color: #1ac9c0; + font-size: 24rpx; +} + +.mine-page__menu-card { + position: relative; + z-index: 1; + border-radius: 24rpx; + background: rgba(39, 46, 64, 0.96); + box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.03); + overflow: hidden; +} + +.mine-page__menu-item { + display: flex; + align-items: center; + min-height: 96rpx; + padding: 0 24rpx; + border-top: 1rpx solid rgba(255, 255, 255, 0.05); +} + +.mine-page__menu-item--first { + border-top: 0; +} + +.mine-page__menu-icon { + position: relative; + width: 28rpx; + height: 28rpx; + margin-right: 18rpx; +} + +.mine-page__menu-icon::before, +.mine-page__menu-icon::after { + position: absolute; + content: ""; + border-color: #19d3be; +} + +.mine-page__menu-icon--device::before { + inset: 4rpx; + border: 2rpx solid #19d3be; + border-radius: 6rpx; +} + +.mine-page__menu-icon--device::after { + left: 6rpx; + right: 6rpx; + top: 12rpx; + height: 2rpx; + background: #19d3be; +} + +.mine-page__menu-icon--repair::before { + left: 4rpx; + top: 12rpx; + width: 20rpx; + height: 2rpx; + background: #19d3be; + transform: rotate(45deg); +} + +.mine-page__menu-icon--repair::after { + left: 10rpx; + top: 6rpx; + width: 10rpx; + height: 14rpx; + border: 2rpx solid #19d3be; + border-left: 0; + border-bottom: 0; + transform: rotate(45deg); +} + +.mine-page__menu-icon--feedback::before { + inset: 4rpx; + border: 2rpx solid #19d3be; + border-radius: 50%; +} + +.mine-page__menu-icon--feedback::after { + left: 12rpx; + top: 8rpx; + width: 2rpx; + height: 12rpx; + background: #19d3be; + box-shadow: 0 10rpx 0 #19d3be; +} + +.mine-page__menu-icon--video::before { + inset: 5rpx 3rpx; + border: 2rpx solid #19d3be; + border-radius: 6rpx; +} + +.mine-page__menu-icon--video::after { + top: 9rpx; + right: 7rpx; + width: 0; + height: 0; + border-top: 5rpx solid transparent; + border-bottom: 5rpx solid transparent; + border-left: 8rpx solid #19d3be; +} + +.mine-page__menu-icon--follow::before { + left: 4rpx; + top: 8rpx; + width: 10rpx; + height: 16rpx; + border: 2rpx solid #19d3be; + border-right: 0; + border-radius: 12rpx 0 0 12rpx; +} + +.mine-page__menu-icon--follow::after { + right: 4rpx; + top: 8rpx; + width: 10rpx; + height: 16rpx; + border: 2rpx solid #19d3be; + border-left: 0; + border-radius: 0 12rpx 12rpx 0; +} + +.mine-page__menu-icon--version::before { + inset: 4rpx; + border: 2rpx solid #19d3be; + border-radius: 50%; +} + +.mine-page__menu-icon--version::after { + left: 12rpx; + top: 8rpx; + width: 2rpx; + height: 8rpx; + background: #19d3be; + box-shadow: 0 12rpx 0 #19d3be; +} + +.mine-page__menu-label { + flex: 1; + color: #edf2f7; + font-size: 30rpx; +} + +.mine-page__menu-trailing { + display: flex; + align-items: center; + gap: 12rpx; +} + +.mine-page__menu-value { + color: #707b92; + font-size: 24rpx; +} + +.mine-page__menu-arrow { + color: #7d869d; + font-size: 26rpx; +} + +.mine-page__tabbar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 5; + display: flex; + justify-content: space-around; + padding: 16rpx 24rpx calc(20rpx + env(safe-area-inset-bottom)); + background: rgba(30, 35, 48, 0.98); + box-shadow: 0 -8rpx 24rpx rgba(4, 8, 20, 0.34); +} + +.mine-page__tabbar-item { + display: flex; + flex-direction: column; + align-items: center; + min-width: 88rpx; +} + +.mine-page__tabbar-icon { + position: relative; + width: 34rpx; + height: 34rpx; + margin-bottom: 8rpx; + border-radius: 10rpx; + border: 2rpx solid #8f97a9; + opacity: 0.86; +} + +.mine-page__tabbar-icon::before, +.mine-page__tabbar-icon::after { + position: absolute; + content: ""; +} + +.mine-page__tabbar-icon::before { + left: 8rpx; + right: 8rpx; + top: 10rpx; + height: 2rpx; + background: #8f97a9; +} + +.mine-page__tabbar-icon::after { + left: 8rpx; + right: 8rpx; + bottom: 10rpx; + height: 2rpx; + background: #8f97a9; +} + +.mine-page__tabbar-icon--active { + border-color: #36e4aa; + background: rgba(54, 228, 170, 0.12); +} + +.mine-page__tabbar-icon--active::before, +.mine-page__tabbar-icon--active::after { + background: #36e4aa; +} + +.mine-page__tabbar-badge { + position: absolute; + top: -4rpx; + right: -4rpx; + width: 10rpx; + height: 10rpx; + border-radius: 50%; + background: #ff4d4f; + box-shadow: 0 0 0 4rpx rgba(30, 35, 48, 0.98); +} + +.mine-page__tabbar-label { + color: #b0b6c4; + font-size: 20rpx; +} + +.mine-page__tabbar-label--active { + color: #36e4aa; +} diff --git a/src/pages/mine/index.tsx b/src/pages/mine/index.tsx new file mode 100644 index 0000000..3faf952 --- /dev/null +++ b/src/pages/mine/index.tsx @@ -0,0 +1,180 @@ +import { Text, View } from "@tarojs/components"; +import Taro from "@tarojs/taro"; +import type { CSSProperties } from "react"; +import { useEffect, useState } from "react"; +import "./index.scss"; + +type UserProfile = { + userName: string; + phone: string; + vip: boolean; + avatar: string; + deviceCount: number; +}; + +type FeatureItem = { + key: string; + label: string; + value?: string; + icon: string; + url?: string; +}; + +const userProfile: UserProfile = { + userName: "张天爱", + phone: "135****2598", + vip: true, + avatar: "", + deviceCount: 0 +}; + +const featureItems: FeatureItem[] = [ + { key: "devices", label: "我的设备", value: "已绑定 0 台", icon: "device", url: "/pages/devices/index" }, + { key: "repair", label: "设备报修", icon: "repair", url: "/pages/repair/index" }, + { key: "feedback", label: "问题反馈", icon: "feedback", url: "/pages/feedback/index" }, + { key: "videos", label: "教学视频", icon: "video", url: "/pages/videos/index" }, + { key: "follow-us", label: "关注我们", icon: "follow", url: "/pages/follow-us/index" }, + { key: "version", label: "当前版本", value: "V1.0.2504.12", icon: "version" } +]; + +const tabItems = [ + { key: "home", label: "首页" }, + { key: "report", label: "报告" }, + { key: "assistant", label: "小e" }, + { key: "message", label: "消息", badge: true }, + { key: "mine", label: "我的", active: true } +]; + +export default function MinePage() { + const [topSafeHeight, setTopSafeHeight] = useState(0); + const [menuSafeWidth, setMenuSafeWidth] = useState(0); + + useEffect(() => { + const windowInfo = typeof Taro.getWindowInfo === "function" ? Taro.getWindowInfo() : Taro.getSystemInfoSync(); + const menuButtonRect = + typeof Taro.getMenuButtonBoundingClientRect === "function" ? Taro.getMenuButtonBoundingClientRect() : null; + const safeTop = menuButtonRect?.bottom || windowInfo.statusBarHeight || 0; + const safeRight = menuButtonRect ? windowInfo.windowWidth - menuButtonRect.left : 0; + + setTopSafeHeight(safeTop); + setMenuSafeWidth(safeRight); + }, []); + + const showToast = (title: string) => { + Taro.showToast({ + title, + icon: "none" + }); + }; + + const openPage = (url: string) => { + Taro.navigateTo({ url }); + }; + + const handleFeatureClick = (item: FeatureItem) => { + if (item.url) { + openPage(item.url); + return; + } + + showToast("当前已是最新版本"); + }; + + const handleTabClick = (key: string, label: string) => { + if (key === "mine") { + return; + } + + if (key === "home") { + const pages = Taro.getCurrentPages(); + + if (pages.length > 1) { + Taro.navigateBack(); + } else { + Taro.redirectTo({ url: "/pages/index/index" }); + } + + return; + } + + showToast(`${label}功能待接入`); + }; + + const pageStyle = { + "--top-safe-height": `${topSafeHeight}px`, + "--menu-safe-width": `${menuSafeWidth}px` + } as CSSProperties; + + return ( + + + + + + + + {userProfile.avatar ? ( + + ) : ( + + + + + )} + + + + {userProfile.userName} + 账号: {userProfile.phone} + + VIP + {userProfile.vip ? "已开通VIP会员" : "暂未开通VIP会员"} + 0 + + + + + + + openPage("/pages/support/index")} /> + openPage("/pages/settings/index")} /> + + + openPage("/pages/profile/index")}> + 个人信息 + + + + + + {featureItems.map((item, index) => ( + handleFeatureClick(item)} + > + + + {item.label} + + + {item.value ? {item.value} : null} + > + + + ))} + + + + {tabItems.map((item) => ( + handleTabClick(item.key, item.label)}> + + {item.badge ? : null} + + {item.label} + + ))} + + + ); +} diff --git a/src/pages/profile/index.config.ts b/src/pages/profile/index.config.ts new file mode 100644 index 0000000..186f3e0 --- /dev/null +++ b/src/pages/profile/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "个人信息" +}); diff --git a/src/pages/profile/index.scss b/src/pages/profile/index.scss new file mode 100644 index 0000000..719270f --- /dev/null +++ b/src/pages/profile/index.scss @@ -0,0 +1,3 @@ +.profile-page { + display: block; +} diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx new file mode 100644 index 0000000..cdc847e --- /dev/null +++ b/src/pages/profile/index.tsx @@ -0,0 +1,26 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "资料编辑", + items: [ + { label: "头像", value: "支持后续替换" }, + { label: "昵称", value: "张天爱" }, + { label: "手机号", value: "135****2598" }, + { label: "修改密码", value: "待接入" } + ] + } +]; + +export default function ProfilePage() { + return ( + + ); +} diff --git a/src/pages/repair/index.config.ts b/src/pages/repair/index.config.ts new file mode 100644 index 0000000..26a3152 --- /dev/null +++ b/src/pages/repair/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "设备报修" +}); diff --git a/src/pages/repair/index.scss b/src/pages/repair/index.scss new file mode 100644 index 0000000..0cef517 --- /dev/null +++ b/src/pages/repair/index.scss @@ -0,0 +1,3 @@ +.repair-page { + display: block; +} diff --git a/src/pages/repair/index.tsx b/src/pages/repair/index.tsx new file mode 100644 index 0000000..1e7dd2e --- /dev/null +++ b/src/pages/repair/index.tsx @@ -0,0 +1,28 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "报修申请", + items: [ + { label: "提交报修申请" }, + { label: "上传故障图片", value: "待接入上传能力" }, + { label: "填写设备问题", value: "待接入表单" } + ] + }, + { + title: "售后进度", + items: [{ label: "维修进度查询", value: "后续接接口" }] + } +]; + +export default function RepairPage() { + return ( + + ); +} diff --git a/src/pages/settings/index.config.ts b/src/pages/settings/index.config.ts new file mode 100644 index 0000000..a55f8e7 --- /dev/null +++ b/src/pages/settings/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "设置" +}); diff --git a/src/pages/settings/index.scss b/src/pages/settings/index.scss new file mode 100644 index 0000000..23d29ef --- /dev/null +++ b/src/pages/settings/index.scss @@ -0,0 +1,3 @@ +.settings-page { + display: block; +} diff --git a/src/pages/settings/index.tsx b/src/pages/settings/index.tsx new file mode 100644 index 0000000..52646c1 --- /dev/null +++ b/src/pages/settings/index.tsx @@ -0,0 +1,31 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "通用设置", + items: [ + { label: "通知设置" }, + { label: "权限管理" }, + { label: "清理缓存" } + ] + }, + { + title: "账号相关", + items: [ + { label: "关于我们" }, + { label: "退出登录", value: "待二次确认" } + ] + } +]; + +export default function SettingsPage() { + return ( + + ); +} diff --git a/src/pages/support/index.config.ts b/src/pages/support/index.config.ts new file mode 100644 index 0000000..98bfb12 --- /dev/null +++ b/src/pages/support/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "帮助与客服" +}); diff --git a/src/pages/support/index.scss b/src/pages/support/index.scss new file mode 100644 index 0000000..2c59e96 --- /dev/null +++ b/src/pages/support/index.scss @@ -0,0 +1,3 @@ +.support-page { + display: block; +} diff --git a/src/pages/support/index.tsx b/src/pages/support/index.tsx new file mode 100644 index 0000000..1958ec3 --- /dev/null +++ b/src/pages/support/index.tsx @@ -0,0 +1,24 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "客服入口", + items: [ + { label: "在线客服", value: "工作日 9:00 - 18:00" }, + { label: "联系售后", value: "支持提交工单" }, + { label: "帮助中心", value: "常见问题待补充" } + ] + } +]; + +export default function SupportPage() { + return ( + + ); +} diff --git a/src/pages/videos/index.config.ts b/src/pages/videos/index.config.ts new file mode 100644 index 0000000..a0ee045 --- /dev/null +++ b/src/pages/videos/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: "教学视频" +}); diff --git a/src/pages/videos/index.scss b/src/pages/videos/index.scss new file mode 100644 index 0000000..0f5de77 --- /dev/null +++ b/src/pages/videos/index.scss @@ -0,0 +1,3 @@ +.videos-page { + display: block; +} diff --git a/src/pages/videos/index.tsx b/src/pages/videos/index.tsx new file mode 100644 index 0000000..1efd3a3 --- /dev/null +++ b/src/pages/videos/index.tsx @@ -0,0 +1,24 @@ +import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import "./index.scss"; + +const sections: SecondaryPageSection[] = [ + { + title: "视频分类", + items: [ + { label: "设备使用教程", value: "3 个占位视频" }, + { label: "安装教程", value: "2 个占位视频" }, + { label: "健康指导视频", value: "持续补充" } + ] + } +]; + +export default function VideosPage() { + return ( + + ); +}