--- title: 微信小程序(患者端) updated: 2026-05-08 status: active tags: [miniprogram, taro, wechat, patient] --- # 微信小程序(患者端) > 从 [[index]] 导航。关联: [[infrastructure]] [[erp-server]] [[erp-health]] ## 1. 设计决策 - **Taro 4.2 + React 18** — 跨平台小程序框架,React 生态 - **Zustand 状态管理** — 与 Web 前端一致的 store 模式 - **微信登录流程** — `Taro.login()` → code → 后端 `jscode2session` → openid → JWT - **build 时注入环境变量** — `process.env` 在小程序运行时不存在,必须通过 `defineConstants` 编译时替换 - **不使用浏览器 Web API** — `btoa`/`atob` 等在小程序中不可用,用 Taro 原生 API 替代 - **Zod 输入验证** — 健康数据录入使用 Zod schema 验证 ### 版本 Taro 4.2 / React 18 / TypeScript / Zustand 5 / Sass / Zod / ECharts 6(按需引入) > **依赖统一(2026-04-27):** 移除 `echarts-taro3-react`(内嵌 Taro 3.0.8 + React 16,导致 webpack 模块 ID 不一致 → `Cannot read property 'call' of undefined`)。改为自定义 `EcCanvas` 组件 + `echarts/core` 按需引入。`taro.js` chunk 从 152KB 降至 133KB。 ### 1.1 设计系统:温润东方风 > 自 2026-04-27 起采用。所有新增/修改页面必须遵循此设计系统,保持视觉一致性。 > > 设计系统源文件:`apps/miniprogram/src/styles/variables.scss` + `mixins.scss` + `tokens.scss` **设计哲学**:Kenya Hara 式东方极简。温润米底 + 单一赤土橙贯穿全场,留白呼吸感优先。 #### 色彩 | 角色 | 变量名 | 色值 | 用途 | |------|--------|------|------| | 强调色 | `$pri` | `#C4623A` | 按钮、链接、活跃 tab、品牌色 | | 强调浅 | `$pri-l` | `#F0DDD4` | 图标底色、高亮背景 | | 强调深 | `$pri-d` | `#8B3E1F` | 渐变终点、暗色变体 | | 背景 | `$bg` | `#F5F0EB` | 页面底色(warm cream) | | 卡片 | `$card` | `#FFFFFF` | 白色卡片 | | 辅助底 | `$surface-alt` | `#EDE8E2` | 次级表面、分隔 | | 主文字 | `$tx` | `#2D2A26` | warm black,标题和正文 | | 次文字 | `$tx2` | `#7A756E` | 辅助说明、标签 | | 淡文字 | `$tx3` | `#A8A29E` | 占位、时间戳、箭头 | | 边框 | `$bd` / `$bd-l` | `#E8E2DC` / `#F0EBE5` | 分隔线、卡片边框 | | 成功 | `$acc` / `$acc-l` | `#5B7A5E` / `#E8F0E8` | sage green 正常状态 | | 警告 | `$wrn` / `$wrn-l` | `#C4873A` / `#FFF3E0` | warm amber 偏高/待确认 | | 危险 | `$dan` / `$dan-l` | `#B54A4A` / `#FDEAEA` | muted red 异常/删除 | #### 字体 | 用途 | 字体栈 | 说明 | |------|--------|------| | 标题 / 数据数字 | `Georgia, Times New Roman, serif` | 衬线体,传递专业与温度。用 `@include serif-number` | | 正文 / 辅助 | `-apple-system, PingFang SC, sans-serif` | 系统无衬线,清晰可读 | | 章节标题 | `@include section-title` | 30rpx / bold / serif,统一样式 | #### 圆角 / 阴影 / 间距 | 元素 | 圆角 | 阴影 | |------|------|------| | 卡片 | `$r` 12px | `$shadow-md` (0 2px 12px rgba(45,42,38,0.08)) | | 小元素(标签、输入框) | `$r-sm` 8px | `$shadow-sm` (0 1px 4px rgba(45,42,38,0.04)) | | 浮层、弹窗 | `$r-lg` 16px | `$shadow-lg` (0 8px 32px rgba(45,42,38,0.12)) | | 水平间距 | 页面两侧 24rpx,卡片内 24-28rpx | | | 垂直间距 | 区块间 24rpx,卡片内标题与内容 20rpx | | #### 组件规范 **按钮**:主按钮 `$pri` 实色 + 白字 + 圆角 12px + 阴影。次按钮 `$pri` 边框透明底。禁用 `$surface-alt` 底 + `$tx3` 字。 **状态标签**:用 `@include tag(背景色, 文字色)` 生成。不用粗边框、不用 emoji 状态图标。颜色映射:正常 → `$acc`/`$acc-l`,偏高 → `$wrn`/`$wrn-l`,异常 → `$dan`/`$dan-l`,默认 → `$tx2`/`$bd-l`。 **健康数据卡片**:2×2 网格,衬线大数字居中,小标签 + 状态标签在底部。数据用 `@include serif-number` 等宽数字。 **列表项**:白色卡片容器,每行 14-24rpx padding,`$bd-l` 底部分隔。右侧箭头用 `›` 字符。无左侧彩色 border accent。 **图标**:禁止用 emoji 作图标。用线性 SVG 或首字衬线体作为图标占位。图标底色用 `$pri-l`。 **禁止事项**:紫色渐变 / emoji 作图标 / 圆角卡片+左彩色 border / 无意义渐变背景 / 装饰性 icon 遍地配。 #### Design Token 系统 > 自 2026-05-09 起全面接入。68 个 SCSS 文件(59 页面 + 9 组件)全部使用 `var(--tk-*)` 引用字号和结构值。 > > 源文件:`apps/miniprogram/src/styles/tokens.scss` **架构原理**:通过 CSS 自定义属性(Custom Properties)实现单一真相源。`page {}` 定义正常值,`.elder-mode {}` 覆写关怀值,所有页面通过 `var(--tk-*)` 引用,关怀模式自动级联生效,无需逐页维护覆写。 **Taro 兼容性**:Taro 编译器正确处理 CSS 自定义属性中的 px→rpx 转换(如 `--tk-font-h1: 26px` → `--tk-font-h1: 52rpx`)。 ##### 字号 Token(10 级) | Token | 正常值 | 关怀值 | 倍率 | 语义 | 覆盖场景 | |-------|--------|--------|------|------|----------| | `--tk-font-hero` | 48px | 56px | ×1.17 | 装饰图标、空状态字符 | 大型装饰元素 | | `--tk-font-h1` | 26px | 30px | ×1.15 | 页面/区块标题 | 99 处 | | `--tk-font-h2` | 24px | 28px | ×1.17 | 副标题、日期 | 86 处 | | `--tk-font-body-lg` | 28px | 34px | ×1.21 | 大正文、按钮 | 110 处 | | `--tk-font-body` | 22px | 30px | ×1.36 | 正文、标签 | 92 处 | | `--tk-font-body-sm` | 16px | 22px | ×1.38 | 中等正文、列表项 | 26 处 | | `--tk-font-num` | 30px | 34px | ×1.13 | 数值显示 | 56 处 | | `--tk-font-num-lg` | 34px | 40px | ×1.18 | 大数值、统计 | 21 处 | | `--tk-font-cap` | 13px | 18px | ×1.38 | 说明文字、时间戳 | 54 处 | | `--tk-font-micro` | 11px | 17px | ×1.55 | 角标、标签 | 22 处 | ##### 结构 Token | Token | 正常值 | 关怀值 | 语义 | |-------|--------|--------|------| | `--tk-line-height` | 1.5 | 1.7 | 行高 | | `--tk-touch-min` | 48px | 56px | 最小触控区域 | | `--tk-btn-primary-h` | 56px | 64px | 主按钮高度 | | `--tk-text-secondary` | #78716C | #5A554F | 辅助文字颜色(关怀模式提升对比度) | ##### 使用规则 **新增页面必须使用 token**,禁止硬编码 `font-size: Npx`: ```scss // 正确 .title { font-size: var(--tk-font-h1); } .desc { font-size: var(--tk-font-cap); color: var(--tk-text-secondary); } .number { font-size: var(--tk-font-num); @include serif-number; } // 错误 .title { font-size: 26px; } // 禁止 ``` **特殊值例外**(仅允许以下场景硬编码): - `72px` — 积分余额大数字(装饰性,不走 token) - `80px` — 错误状态图标(组件内部装饰) - `21px` — 长辈模式预览页(特殊 UI) ##### 关怀模式(Elder Mode) 通过 Zustand store (`stores/ui.ts`) 的 `DisplayMode` 控制。`useElderClass()` hook 在根 View 添加 `.elder-mode` class,触发所有 token 的关怀值级联。 `elder-mode.scss`(~120 行)仅保留结构布局覆写(grid 列数、padding、gap),所有字号/颜色覆写已通过 token 级联自动处理。 ## 2. 关键文件 + 数据流 ### 核心文件 | 文件 | 职责 | |------|------| | `apps/miniprogram/config/index.ts` | Taro 构建配置(defineConstants 注入环境变量) | | `apps/miniprogram/src/services/request.ts` | HTTP 请求封装(401 自动刷新、错误处理) | | `apps/miniprogram/src/services/auth.ts` | 微信登录/绑定手机号 API | | `apps/miniprogram/src/stores/auth.ts` | 认证状态(login/bindPhone/restore) | | `apps/miniprogram/src/utils/secure-storage.ts` | token 安全存储(XOR + Base64 混淆) | | `apps/miniprogram/project.config.json` | 微信开发者工具配置(AppID、urlCheck) | ### 微信登录流程 ``` 用户点击"微信一键登录" ↓ Taro.login() → 临时 code ↓ POST /auth/wechat/login { code } ↓ 后端调用微信 jscode2session → 获取 openid + session_key ↓ 查询 wechat_users 表 ├── 已绑定 → 返回 { bound: true, token: JWT } → 跳转首页 └── 未绑定 → 返回 { bound: false, openid } → 显示"授权手机号"按钮 ↓ 用户授权手机号 POST /auth/wechat/bind-phone { openid, encrypted_data, iv } ↓ 后端解密手机号 → 创建/关联用户 → 返回 JWT → 跳转首页 ``` ### 页面结构(59 个页面,6 个主包 + 10 个子包) #### TabBar 页面(3 个) | 页面路径 | 说明 | |----------|------| | `pages/index/index` | 首页(今日健康、快捷服务) | | `pages/health/index` | 健康上报(Tab 页) | | `pages/messages/index` | 消息中心(Tab 页) | #### 患者端主页面 | 页面路径 | 说明 | |----------|------| | `pages/login/index` | 登录页(微信登录 + 协议勾选) | | `pages/consultation/index` | 咨询列表 | | `pages/consultation/detail/index` | 咨询详情 | | `pages/mall/index` | 积分商城 | | `pages/profile/index` | 个人中心 | | `pages/appointment/index` | 预约列表 | | `pages/appointment/create/index` | 预约挂号 | | `pages/appointment/detail/index` | 预约详情 | | `pages/legal/user-agreement` | 用户服务协议 | | `pages/legal/privacy-policy` | 隐私政策 | #### 健康子包(pkg-health,4 个) | 页面路径 | 说明 | |----------|------| | `pages/pkg-health/trend/index` | 健康趋势(体征数据折线图) | | `pages/pkg-health/input/index` | 健康数据录入(Zod 验证) | | `pages/pkg-health/daily-monitoring/index` | 日常监测数据 | | `pages/pkg-health/alerts/index` | 告警列表 | #### 医生端子包(doctor,16 个) | 页面路径 | 说明 | |----------|------| | `pages/doctor/index` | 医护首页/工作台 | | `pages/doctor/patients/index` | 患者列表 | | `pages/doctor/patients/detail/index` | 患者详情 | | `pages/doctor/consultation/index` | 咨询管理 | | `pages/doctor/consultation/detail/index` | 咨询详情 | | `pages/doctor/followup/index` | 随访管理 | | `pages/doctor/followup/detail/index` | 随访详情 | | `pages/doctor/report/index` | 报告管理 | | `pages/doctor/report/detail/index` | 报告详情 | | `pages/doctor/alerts/index` | 告警管理 | | `pages/doctor/alerts/detail/index` | 告警详情 | | `pages/doctor/action-inbox/index` | 待办事项 | | `pages/doctor/dialysis/index` | 透析管理 | | `pages/doctor/dialysis/detail/index` | 透析详情 | | `pages/doctor/dialysis/create/index` | 创建透析记录 | | `pages/doctor/prescription/index` | 处方管理 | | `pages/doctor/prescription/detail/index` | 处方详情 | | `pages/doctor/prescription/create/index` | 开具处方 | #### 商城子包(pkg-mall,3 个) | 页面路径 | 说明 | |----------|------| | `pages/pkg-mall/exchange/index` | 积分兑换 | | `pages/pkg-mall/orders/index` | 积分订单 | | `pages/pkg-mall/detail/index` | 商品详情 | #### 个人中心子包(pkg-profile,12 个) | 页面路径 | 说明 | |----------|------| | `pages/pkg-profile/family/index` | 家庭成员管理 | | `pages/pkg-profile/family-add/index` | 添加家庭成员 | | `pages/pkg-profile/reports/index` | 我的报告 | | `pages/pkg-profile/followups/index` | 我的随访 | | `pages/pkg-profile/medication/index` | 用药记录 | | `pages/pkg-profile/settings/index` | 设置 | | `pages/pkg-profile/dialysis-records/index` | 透析记录 | | `pages/pkg-profile/dialysis-records/detail/index` | 透析记录详情 | | `pages/pkg-profile/dialysis-prescriptions/index` | 透析处方 | | `pages/pkg-profile/dialysis-prescriptions/detail/index` | 透析处方详情 | | `pages/pkg-profile/consents/index` | 知情同意 | | `pages/pkg-profile/health-records/index` | 健康档案 | | `pages/pkg-profile/diagnoses/index` | 诊断记录 | #### 其他子包(9 个) | 页面路径 | 说明 | |----------|------| | `pages/ai-report/list/index` | AI 分析报告列表 | | `pages/ai-report/detail/index` | AI 分析报告详情 | | `pages/article/index` | 健康资讯列表 | | `pages/article/detail/index` | 文章详情 | | `pages/report/detail/index` | 健康报告详情 | | `pages/followup/detail/index` | 随访详情 | | `pages/events/index` | 线下活动 | | `pages/device-sync/index` | 设备数据同步 | ### 服务层(10+ 个文件) | 文件 | 覆盖 | |------|------| | `request.ts` | HTTP 封装(401 刷新、错误处理) | | `auth.ts` | 微信登录/绑定手机号 | | `health.ts` | 体征数据/健康趋势 | | `patient.ts` | 患者信息/家庭成员 | | `appointment.ts` | 预约挂号/详情/取消 | | `followup.ts` | 随访任务/详情 | | `article.ts` | 健康文章 | | `report.ts` | 健康报告 | | `analytics.ts` | 数据分析 | | `wechat-templates.ts` | 微信模板消息 ID | ### 组件(10 个) | 组件 | 用途 | |------|------| | `EcCanvas` | ECharts Canvas 包装(Taro 4 兼容,按需引入 echarts/core) | | `EmptyState` | 空状态占位 | | `ErrorBoundary` | 错误边界捕获 | | `ErrorState` | 错误状态展示 | | `FamilyPicker` | 家庭成员选择器 | | `HealthCard` | 健康数据卡片 | | `Loading` | 加载状态 | | `StepIndicator` | 步骤指示器 | | `TrendChart` | 趋势图表(基于 EcCanvas) | | `WeekCalendar` | 周日历组件 | ### 集成契约 | 方向 | 模块 | 接口 | 触发时机 | |------|------|------|---------| | 调用 → | [[erp-server]] | `POST /auth/wechat/login` | 微信登录 | | 调用 → | [[erp-server]] | `POST /auth/wechat/bind-phone` | 手机号绑定 | | 调用 → | [[erp-health]] | `/api/v1/health/*` | 健康数据查询 | | 调用 → | [[erp-server]] | `/api/v1/auth/refresh` | Token 刷新 | ## 3. 代码逻辑 ### 环境变量注入(关键) 小程序运行时没有 `process.env`,必须在 `config/index.ts` 中通过 `defineConstants` 编译时替换: ```typescript defineConstants: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.TARO_APP_API_URL': JSON.stringify(process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1'), 'process.env.TARO_APP_ENCRYPTION_KEY': JSON.stringify(process.env.TARO_APP_ENCRYPTION_KEY || ''), }, ``` ### Token 安全存储 `secure-storage.ts` 使用 XOR 混淆 + Base64 编码存储 token: - **不使用 `btoa`/`atob`** — 小程序环境不支持 - 使用 `Taro.arrayBufferToBase64` / `Taro.base64ToArrayBuffer` 替代 - 加密密钥通过 `TARO_APP_ENCRYPTION_KEY` 环境变量注入 ### 请求封装(request.ts) - 401 自动尝试 refresh token,失败后跳转登录页 - 错误响应格式: `{ error: string, message: string }`(无 `success` 字段) - 成功响应格式: `{ success: true, data: T }` - 当前 URL 拼接方式构建查询参数(待重构为 params 对象) ### 健康数据录入验证 使用 Zod schema 验证用户输入的体征数据(血压、心率、体重、血糖),确保数据类型和范围合法。 ## 4. 构建与调试 ### 构建 ```bash cd apps/miniprogram && pnpm build:weapp # 生产构建 cd apps/miniprogram && npx taro build --type weapp # 等效 ``` ### 微信开发者工具配置 | 配置项 | 值 | 说明 | |--------|-----|------| | AppID | `wx20f4ef9cc2ec66c5` | 真实微信 AppID | | urlCheck | `false` | 不校验合法域名(开发模式) | | miniprogramRoot | `dist/` | 编译输出目录 | ### 后端微信配置 `crates/erp-server/config/default.toml`: ```toml [wechat] appid = "wx20f4ef9cc2ec66c5" secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" ``` ## 5. 活跃问题 + 陷阱 ### 历史教训 | 问题 | 根因 | 修复 | |------|------|------| | 页面空白 `ReferenceError: process is not defined` | `process.env` 在运行时不存在 | `defineConstants` 编译时替换 | | 登录成功但前端报失败 `btoa is not defined` | `secure-storage.ts` 使用 Web API | 改用 `Taro.arrayBufferToBase64` | | 微信登录 500 `wechat_users.created_by 不存在` | 迁移创建的表缺少标准字段 | `ALTER TABLE` 补列 | | 401 循环重定向 | 首页未登录时 `request.ts` 反复 redirectTo | 检查当前路径避免重复跳转 | | `Cannot read property 'call' of undefined` | `echarts-taro3-react` 内嵌 Taro 3.0.8 + React 16,与 Taro 4.2 + React 18 产生双实例,webpack 模块 ID 不匹配 | 移除 `echarts-taro3-react`,改为自定义 `EcCanvas` + `echarts/core` 按需引入 | ### 待优化 | 问题 | 级别 | 说明 | |------|------|------| | ~~Auth token 日志输出~~ | ~~P0~~ 已解决 | terser `drop_console` 移除生产日志 | | ~~Token 刷新竞态~~ | ~~P0~~ 已解决 | `refreshPromise` 单例 + GET 请求去重 | | ~~ECharts 全量引入~~ | ~~P2~~ 已解决 | 分包后 echarts 514KB 仅在趋势页按需加载 | | 积分商城降级 UI | P0 | 未关联患者档案时 Tab 页空白,需引导用户建档 | | daily-monitoring 无 Zod 验证 | P1 | 对齐 health/input 的验证标准 | | 文章列表返回草稿 | P1 | 患者端应只展示 `published` 状态文章 | | URL 拼接构建查询参数 | P2 | `request.ts` 已支持 `buildQuery(params)` 但内部使用 | | 生产配置 | P2 | `urlCheck`/`minified` 需区分环境 | ### 2026-04-30 审计发现 **三端对齐差距**(小程序 API 覆盖 76 个,Web 235 个,后端 328 个): | 发现 | 严重性 | 说明 | |------|--------|------| | ~~晚间血压数据永久丢失~~ | ~~CRITICAL~~ **已修复** | 新增 `blood_pressure_evening` indicator_type,小程序录入页 + 日常监测页 + 后端 service + 集成测试均已覆盖 | | ~~透析管理完全无入口~~ | ~~HIGH~~ **已修复** | 医生端新增 dialysis 3 页面 + prescription 3 页面,患者端新增 dialysis-records 2 页面 + dialysis-prescriptions 2 页面 | | ~~知情同意完全无入口~~ | ~~HIGH~~ **已修复** | 患者端新增 `pkg-profile/consents` 页面 | | 体温/血氧未映射 | MEDIUM | `body_temperature`/`spo2` 无 indicator_type | | ~~健康记录小程序无入口~~ | ~~MEDIUM~~ **已修复** | 患者端新增 `pkg-profile/health-records` 页面 | | ~~诊断记录小程序无入口~~ | ~~MEDIUM~~ **已修复** | 患者端新增 `pkg-profile/diagnoses` 页面 | | 小程序无自动化测试 | HIGH | 已建立 MCP 自动化审计流程,4 角色 236 次探测通过率 96.2% | **功能域完成度**(小程序端): | 功能域 | 完成度 | 关键差距 | |--------|--------|---------| | 咨询管理 | 95% | 无导出(预期) | | 预约管理 | 90% | 基本对齐 | | 随访管理 | 70% | 仅列表+创建 | | 患者管理 | 85% | 无删除(预期) | | 健康数据 | 80% | ~~丢失晚间血压~~ 已修复 | | 告警系统 | 80% | 查看+处理+医护端管理 | | 透析管理 | 70% | 医护端管理+患者端查看记录/处方 | | 知情同意 | 60% | 患者端查看,无签署流程 | | 健康档案 | 60% | 患者端可查看健康档案和诊断 | | 统计仪表盘 | 30% | 仅医护端 3 个统计 | | AI 分析 | 30% | 仅历史查看 | ### 注意事项 - `Taro.login()` 的 code 一次性使用,每次调用会返回新 code - `session_key` 缓存 5 分钟(TTL),过期需重新登录 - 微信开发者工具中 `getPhoneNumber` 需要真机调试或使用测试号 - Redis 不可达时限流降级为 fail-open,不影响登录 ## 6. MCP 联调(微信开发者工具自动化) > 通过 MCP (Model Context Protocol) 工具直接操控微信开发者工具中的小程序模拟器,实现页面导航、元素交互、数据读取等操作。 > > **当前使用自建 MCP 服务器** `@hms/weapp-mcp`(`tools/weapp-mcp/`),基于 `@weapp-vite/miniprogram-automator@1.1.0`(社区活跃维护 fork)。 > 原有的 `@yfme/weapp-dev-mcp` 在 DevTools 2.01.2510290 版本后不兼容,已弃用。 ### 6.1 前置条件 1. **后端运行** — `cargo run` 启动 `http://localhost:3000` 2. **小程序以 dev 模式构建** — 必须使用 `NODE_ENV=development` 构建(详见 §6.4) ```bash cd apps/miniprogram echo 'TARO_APP_API_URL=http://localhost:3000/api/v1 TARO_APP_ENCRYPTION_KEY=' > .env NODE_ENV=development npx taro build --type weapp ``` 3. **微信开发者工具已登录** — 确保已扫码登录(CLI 命令需要登录态) 4. **微信开发者工具打开项目** — 手动打开,加载 `apps/miniprogram/dist/` 5. **自动化端口已开启** — `project.config.json` 中 `"automationAudits": true` > ⚠️ **为什么必须 dev 模式?** 生产构建(`pnpm build:weapp`)设置 `NODE_ENV=production`,`secure-storage.ts` 的 `decrypt()` 在密钥为空时会抛异常。dev 模式允许空密钥走明文存储。 ### 6.2 启动调试环境 > **铁律:只允许一个 DevTools 实例运行。** 多实例会导致 App 级别自动化命令全部超时(无法恢复,必须全部关闭重开)。 #### 方式 A:Launcher.launch() 自动启动(推荐) 通过 `@weapp-vite/miniprogram-automator` 的 `Launcher` 自动启动 DevTools 并开启自动化端口,无需手动操作。 ```bash # 步骤 1:确认没有残留的 DevTools 进程 tasklist | findstr wechatdevtools # 如有残留:taskkill /F /IM wechatdevtools.exe # 步骤 2:MCP 的 inject_auth 或 connect 工具会自动调用 Launcher.launch() # 或者手动测试: cd tools/weapp-mcp node --input-type=module -e " import { Launcher } from '@weapp-vite/miniprogram-automator'; const mp = await new Launcher().launch({ cliPath: 'D:/微信web开发者工具/cli.bat', projectPath: 'G:/hms/apps/miniprogram/dist', }); console.log('OK:', (await mp.systemInfo()).model); // 不要 disconnect,保持连接供 MCP 使用 " # 自动在端口 9420 开启自动化 ``` #### 方式 B:手动启动 + CLI 开端口 ```bash # 步骤 1:确认没有残留的 DevTools 进程 taskkill /F /IM wechatdevtools.exe # 步骤 2:手动打开微信开发者工具,加载 apps/miniprogram/dist/ # 等待模拟器完全加载,确认无报错 # 步骤 3:通过 CLI 开启自动化端口(端口 9420) "D:/微信web开发者工具/cli.bat" auto --project G:/hms/apps/miniprogram/dist --auto-port 9420 # 看到 "✔ auto" 即成功 # 步骤 4:验证端口已开启 netstat -ano | findstr 9420 # 应看到 LISTENING 状态 ``` 成功后可通过 `mcp__weapp-local__connect` 连接。 ### 6.3 常用 MCP 操作(weapp-local 工具集) | 操作 | MCP 工具 | 说明 | |------|----------|------| | 连接 | `connect` | 连接自动化端口,返回系统信息 | | 断开 | `disconnect` | 断开连接 | | 查看当前页面 | `current_page` | 返回路径、尺寸,可选包含 data | | 导航到页面 | `navigate` | `navigateTo` / `switchTab` / `reLaunch` / `redirectTo` / `navigateBack` | | 查找元素 | `get_element` | CSS 选择器,支持 `[index=N]` 语法 | | 查找多个元素 | `get_elements` | CSS 选择器数组 | | 点击元素 | `tap` | CSS 选择器定位后点击 | | 输入文本 | `input` | CSS 选择器 + 值 | | 滚动 | `scroll` | scroll-view 的 CSS 选择器 + x/y 坐标 | | 读取页面数据 | `page_data` | 获取当前页面 data 对象(可选路径) | | 设置页面数据 | `page_data` | JSON data 参数 → setData | | 读取组件数据 | `element_data` | 组件 CSS 选择器 + 可选路径 | | 设置组件数据 | `element_data` | CSS 选择器 + JSON data → setData | | 调用页面方法 | `page_call_method` | 调用当前页面上暴露的方法 | | 执行 JS | `evaluate` | 在 AppService 上下文执行 JavaScript | | 调用 wx API | `call_wx` | 如 `getStorageSync`、`setStorageSync` | | Mock wx API | `mock_wx` | Mock 一个 wx API 返回指定结果 | | 恢复 wx API | `restore_wx` | 恢复被 mock 的 wx API | | 获取 WXML | `get_wxml` | 获取元素的 WXML 结构 | | 截图 | `screenshot` | ⚠️ 当前版本超时,见 §6.6 | | 日志 | `get_logs` | 获取 console 日志和异常 | | 等待元素 | `wait_for` | 轮询等待选择器匹配 | | 注入认证 | `inject_auth` | 一键获取后端 token + 注入 storage + 跳转首页 | | 审计页面 | `audit_page` | 导航 → 检查加载 → 收集错误 → 健康报告 | | 批量审计 | `audit_all` | 批量审计所有小程序页面 | ### 6.4 绕过微信登录 MCP 无法模拟微信 OAuth(`Taro.login()` 返回的 code 走真实微信 `jscode2session` 接口,DevTools 模拟器会返回 mock code 导致后端 500)。 #### 方案一:inject_auth 一键注入(推荐) **原理:** 以 dev 模式重编译(空加密密钥),通过 MCP 的 `inject_auth` 工具自动完成:获取 admin token → evaluate 注入 storage → reLaunch 首页。 ``` 1. 准备:确保已按 §6.1 以 dev 模式构建,且已连接 MCP(connect) 2. 调用 inject_auth 工具(默认参数即可): - 自动 POST /auth/login 获取 admin token - 通过 evaluate() 注入 access_token / refresh_token / user_data / tenant_id / patient_id - reLaunch 到 /pages/index/index - 等待 2 秒后返回当前页面路径 3. 成功后即可正常操作各页面 ``` **关键 ID(默认值):** - `tenant_id`: `019d80da-7a2c-7820-b0a3-3d5266a3a324` - `patient_id`: `019dcd34-bc4d-72c1-8c19-77ce1f4839d6` #### 方案二:手动分步注入 ```bash # 1. 获取 admin token curl -X POST http://localhost:3000/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"Admin@2026"}' # 2. 通过 MCP call_wx 逐个写入 storage # 3. 通过 MCP navigate reLaunch 到首页 ``` > ⚠️ 长字符串 token 通过 `call_wx` 传输可能截断。建议用 `inject_auth` 工具(内部用 evaluate 直接执行,无传输问题)。 ### 6.5 TabBar 页面列表 以下页面是 TabBar 页面,必须用 `switchTab` 导航(不能用 `navigateTo`): - `pages/index/index` — 首页 - `pages/health/index` — 健康数据 - `pages/messages/index` — 消息中心 > 注意:咨询、商城、个人中心虽然显示为 Tab 样式,但实际不是 TabBar 页面(使用 `reLaunch` 导航)。 ### 6.6 已知限制 | 问题 | 原因 | 替代方案 | |------|------|----------| | **screenshot 超时** | `App.captureScreenshot` 在 DevTools 2.01.2510290 不响应 | 用 `get_element` + `page_data` 获取 UI 状态 | | **多实例导致全挂** | 两个 DevTools 实例抢端口,App 域命令全部超时且不可恢复 | `taskkill /F /IM wechatdevtools.exe` 后重开 | | **navigateTo 超出 10 页栈** | 小程序 webview 限制最多 10 层页面栈 | 批量测试用 `reLaunch` 逐页导航 | | **CLI 需要登录** | `cli.bat auto` 要求 DevTools 已扫码登录 | 在 DevTools 中先扫码登录 | | **SummerCompiler 报错** | `project.config.json` 缺少必要字段 | 已配置 `packNpmManually`/`packNpmRelationList`/`ignoreUploadUnusedFiles`/`condition` | | **require() 在 evaluate 不可用** | webpack 用数字 ID 注册模块 | 直接用 `wx.setStorageSync` / `wx.getStorageSync` | | **auth 重定向** | request interceptor 检测 401 后跳转 login 并清空 storage | 确保 token 有效,reLaunch 后等待 2-3 秒 | | **生产构建 decrypt 抛异常** | 空密钥 + `NODE_ENV=production` 时 `decrypt()` 直接 throw | 使用 `NODE_ENV=development` 构建 | ### 6.7 MCP 服务器架构 ``` tools/weapp-mcp/ ├── src/ │ ├── index.ts # MCP 服务器入口,25 个工具注册 │ └── automator.ts # 连接管理器(@weapp-vite/miniprogram-automator) ├── package.json # @hms/weapp-mcp, 依赖 @weapp-vite/miniprogram-automator@1.1.0 └── tsconfig.json ``` `.mcp.json` 配置: ```json { "weapp-local": { "command": "node", "args": ["tools/weapp-mcp/dist/index.js"], "env": { "WEAPP_WS_ENDPOINT": "ws://localhost:9420", "WEAPP_CLI_PATH": "D:/微信web开发者工具/cli.bat", "WEAPP_PROJECT_PATH": "G:/hms/apps/miniprogram/dist", "HMS_API_URL": "http://localhost:3000/api/v1" } } } ``` 修改后需 `cd tools/weapp-mcp && npm run build` 重新编译。 ### 6.8 自动化审计脚本 | 脚本 | 用途 | 运行 | |------|------|------| | `tools/weapp-mcp/scripts/audit-pages.mjs` | **多角色分批审计**(推荐) | `node scripts/audit-pages.mjs --role admin` | | `apps/miniprogram/inject-auth.cjs` | 注入明文 token(dev 模式) | `node inject-auth.cjs` | | `apps/miniprogram/e2e-final.cjs` | 完整 E2E 链路验证(11 UI + 10 API) | `node e2e-final.cjs` | #### 分批审计脚本用法 ```bash # 在 tools/weapp-mcp/ 目录下运行 cd tools/weapp-mcp # 审计指定角色(admin/doctor/nurse/operator) node scripts/audit-pages.mjs --role admin # 自定义批次大小(默认 10 页/批,每批重启 DevTools 防止多实例超时) node scripts/audit-pages.mjs --role doctor --batch-size 8 # 输出: # - 控制台实时显示每页状态(OK / LOGIN_REDIRECT / ERROR) # - JSON 报告保存到 docs/qa/role-test-results/MP-{role}-audit.json ``` **设计要点:** - 每批 N 页后自动 `taskkill` 所有 DevTools 进程并重新 `Launcher.launch()`,避免多实例导致 App 级命令超时 - 通过 `mp.evaluate()` 注入 storage(`wx.setStorageSync`),写入 `access_token`/`refresh_token`/`user_data`/`user_roles`/`tenant_id`/`current_patient_id` - 测试用户密码均为 `Admin@2026`(不是 `Test@2026`) > ⚠️ 旧脚本 `audit-pages.cjs` / `audit-detail-pages.cjs` 基于 `miniprogram-automator`(已弃用),请使用新的 `audit-pages.mjs`。 ### 6.9 审计结果 #### 2026-05-08 多角色自动化审计(59 页面 × 4 角色) 使用分批审计脚本对全部 59 个页面进行 4 角色全面审计(236 次页面探测): | 角色 | OK | LOGIN_REDIRECT | ERROR | 通过率 | |------|-----|----------------|-------|--------| | admin(患者) | 57 | 0 | 2 | **96.6%** | | doctor(医生) | 58 | 1 | 0 | **98.3%** | | nurse(护士) | 57 | 0 | 2 | **96.6%** | | operator(运营) | 55 | 0 | 4 | **93.2%** | | **合计** | **227** | **1** | **8** | **96.2%** | **关键结论:** - 8 个 ERROR 全部是 DevTools 模拟器随机 timeout,非产品缺陷 - 1 个 LOGIN_REDIRECT 是 doctor 首页首次加载时序问题 - 所有 59 个页面在所有角色下均可正常渲染(排除测试工具因素) - 测试用户密码均为 `Admin@2026`(之前误用 `Test@2026` 导致大量 LOGIN_REDIRECT) 报告文件:`docs/qa/role-test-results/MP-{role}-audit.json` #### 2026-04-27 初次审计(40 页面,单角色) 通过 MCP 自动化工具对全部 40 个页面进行渲染审计: | 类别 | 数量 | 结果 | |------|------|------| | TabBar 页面 | 5 | 5/5 OK | | 患者端子页面 | 24 | 24/24 OK | | 详情页(假 ID) | 11 | 11/11 优雅降级 | | **合计** | **40** | **40/40 全部通过** | 详细审计报告见 `docs/discussions/2026-04-27-miniprogram-audit-report.md`。 ## 7. 变更记录 | 日期 | 变更 | |------|------| | 2026-05-09 | **Design Token 全面接入**:68 SCSS 文件(59 页面 + 9 组件)全面迁移 `font-size: Npx` → `var(--tk-*)`;重写 `tokens.scss` 校准 10 级字号 + 4 结构 token 匹配实际设计值;更新 `mixins.scss` 4 个 mixin 引用 token;清理 12 个页面的本地 mixin 重复定义;`elder-mode.scss` 从 530 行缩减至 ~120 行(删除所有字号/颜色覆写,仅保留结构布局);634 token 引用 / 3 个特殊硬编码;新增 §1.1 Design Token 系统文档 | | 2026-05-08 | **多角色自动化审计**:4 角色(admin/doctor/nurse/operator)× 59 页面 = 236 次探测,综合通过率 96.2%;更新 §2 页面结构为 59 页面完整列表(含医生端 dialysis/prescription/action-inbox + 患者端 dialysis-records/prescriptions/consents/health-records/diagnoses);更新 §5 审计发现(透析/知情同意/诊断/健康记录标记为已修复);更新 §6.5 TabBar 为 3 个;新增 §6.8 分批审计脚本;更新 §6.9 多角色审计结果 | | 2026-05-08 | **MCP 联调全面重写**:自建 MCP 服务器 `@hms/weapp-mcp` 替代 `@yfme/weapp-dev-mcp`;基于 `@weapp-vite/miniprogram-automator@1.1.0`;新增 §6.2 启动步骤(登录+单实例铁律);更新工具列表为 weapp-local 25 个工具;新增 inject_auth 一键注入;新增 §6.7 MCP 服务器架构说明;多实例冲突、CLI 登录、SummerCompiler 等已知限制 | | 2026-05-01 | 审计发现更新:CRITICAL 晚间血压丢失 / HIGH 透析+知情同意完全空白 / 功能域完成度矩阵 | | 2026-04-28 | **全面性能优化**:分包加载(6 分包,主包 517KB→275KB,vendors 192KB→36KB);GET 请求去重+60s TTL 缓存;points store 集中积分/签到状态;todaySummary 60s TTL;7 组件 React.memo;TrendChart 双重渲染修复;restoreAuth 提升 App 级别;prod terser drop_console;crypto-js 按需引入 | | 2026-04-27 | **移除 echarts-taro3-react**:内嵌 Taro 3 + React 16 导致 webpack 模块加载失败,改为自定义 `EcCanvas` 组件 + `echarts/core` 按需引入;更新版本说明 + 历史教训 + 组件列表 | | 2026-04-27 | **MCP 联调全面更新**:§6.1 增加 dev 构建前置条件,§6.4 重写为明文 token 注入法(评估两种方案),§6.6 补充 7 条已知限制,新增 §6.7 审计脚本说明 + §6.8 实测审计结果(40/40 页面通过);§5 补充 4 条审计发现 | | 2026-04-27 | 新增 §6 MCP 联调章节:连接、操作列表、绕过登录、已知限制、e2e 脚本 | | 2026-04-26 | 全面更新:40 页面(含 9 个医护端页面)、15 目录、5 个 Tab 页、积分商城、线下活动 | | 2026-04-25 | 全面更新:20 页面、10 服务、9 组件、Zod 验证、加密密钥外部化说明 | | 2026-04-24 | 创建小程序 wiki 页面,记录登录流程、环境配置、历史陷阱 |