后端: - 添加 articles 表迁移 + Entity + Service + Handler - 健康数据趋势 API (get_mini_trend) 注册路由 - article CRUD (list/get) + DTO 前端 (11个新页面 + 5个服务): - 预约挂号: 列表/创建向导/详情页 - 报告管理: 列表/详情页 - 随访管理: 任务列表/记录详情页 - 资讯文章: 文章详情页 - 个人中心: 就诊人管理/新增/我的报告/我的随访/用药提醒/设置 - 更新 app.config.ts 注册全部路由 - 更新 profile/article 页面为真实功能
34 KiB
HMS 患者小程序实施计划
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 基于 Taro 4 + React 19 构建微信小程序患者端,直连 erp-server 后端 API。
Architecture: 小程序代码位于 apps/miniprogram/,通过 Taro 编译为微信小程序。后端在 erp-auth 新增微信登录支持,erp-health 新增小程序所需 API。前端用 Zustand 管理状态,services 层封装 Taro.request 调用后端 /api/v1/ 端点。
Tech Stack: Taro 4, React 19, TypeScript 6, Zustand 5, SCSS, echarts-taro3-react, Rust/Axum/SeaORM (后端)
Spec: docs/superpowers/specs/2026-04-23-hms-miniprogram-design.md
Chunk 1: Phase 1 — 后端微信认证
Task 1: 创建 wechat_users 数据库迁移
Files:
-
Create:
crates/erp-server/migration/src/m20260423_000001_wechat_users.rs -
Step 1: 创建迁移文件
// m20260423_000001_wechat_users.rs
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationAction)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(WechatUsers::Table)
.if_not_exists()
.col(ColumnDef::new(WechatUsers::Id).uuid().not_null().primary_key())
.col(ColumnDef::new(WechatUsers::TenantId).uuid().not_null())
.col(ColumnDef::new(WechatUsers::Openid).string().not_null())
.col(ColumnDef::new(WechatUsers::UnionId).string())
.col(ColumnDef::new(WechatUsers::UserId).uuid().not_null())
.col(ColumnDef::new(WechatUsers::Phone).string())
.col(ColumnDef::new(WechatUsers::CreatedAt).timestamp_with_time_zone().not_null().default(Expr::current_timestamp()))
.col(ColumnDef::new(WechatUsers::UpdatedAt).timestamp_with_time_zone().not_null().default(Expr::current_timestamp()))
.col(ColumnDef::new(WechatUsers::DeletedAt).timestamp_with_time_zone())
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_wechat_users_openid")
.table(WechatUsers::Table)
.col(WechatUsers::Openid)
.col(WechatUsers::TenantId)
.unique()
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_index(Index::drop().name("idx_wechat_users_openid").table(WechatUsers::Table).to_owned()).await?;
manager.drop_table(Table::drop().table(WechatUsers::Table).to_owned()).await
}
}
#[derive(DeriveIden)]
enum WechatUsers {
Table,
Id,
TenantId,
Openid,
UnionId,
UserId,
Phone,
CreatedAt,
UpdatedAt,
DeletedAt,
}
- Step 2: 注册迁移
在 crates/erp-server/migration/src/lib.rs 的 migration 列表中添加新迁移。
- Step 3: 验证迁移
Run: cargo check -p erp-server
Expected: 编译通过
- Step 4: 提交
git add crates/erp-server/migration/src/m20260423_000001_wechat_users.rs crates/erp-server/migration/src/lib.rs
git commit -m "feat(auth): 添加 wechat_users 表迁移"
Task 2: 创建 wechat_users Entity
Files:
-
Create:
crates/erp-auth/src/entity/wechat_user.rs -
Modify:
crates/erp-auth/src/entity/mod.rs -
Step 1: 创建 Entity 文件
参照 crates/erp-auth/src/entity/user_token.rs 的模式,创建 wechat_user.rs,包含 Model、Relation、ActiveModelBehavior。
- Step 2: 在 mod.rs 注册
在 crates/erp-auth/src/entity/mod.rs 添加 pub mod wechat_user;。
- Step 3: 验证
Run: cargo check -p erp-auth
Expected: 编译通过
- Step 4: 提交
git add crates/erp-auth/src/entity/wechat_user.rs crates/erp-auth/src/entity/mod.rs
git commit -m "feat(auth): 添加 wechat_user SeaORM entity"
Task 3: 微信登录 DTO
Files:
-
Modify:
crates/erp-auth/src/dto.rs -
Step 1: 添加微信登录相关 DTO
在 crates/erp-auth/src/dto.rs 中新增:
// 微信登录请求
#[derive(Deserialize, ToSchema)]
pub struct WechatLoginReq {
pub code: String,
}
// 微信登录响应
#[derive(Serialize, ToSchema)]
pub struct WechatLoginResp {
pub bound: bool,
pub openid: String,
pub token: Option<LoginResp>, // 已绑定时返回
}
// 绑定手机号请求
#[derive(Deserialize, ToSchema)]
pub struct WechatBindPhoneReq {
pub openid: String,
pub encrypted_data: String,
pub iv: String,
}
- Step 2: 验证
Run: cargo check -p erp-auth
- Step 3: 提交
git add crates/erp-auth/src/dto.rs
git commit -m "feat(auth): 添加微信登录 DTO"
Task 4: 微信登录 Service
Files:
-
Create:
crates/erp-auth/src/service/wechat_service.rs -
Modify:
crates/erp-auth/src/service/mod.rs -
Step 1: 实现 wechat_service.rs
核心逻辑:
login(state, tenant_id, code): 用 code 调用微信 API 换 openid → 查 wechat_users 表 → 返回绑定状态bind_phone(state, tenant_id, req): 解密手机号 → 查找/创建 user → 创建 wechat_user 记录 → 签发 JWT- 微信 API 调用:
GET https://api.weixin.qq.com/sns/jscode2session?appid=...&secret=...&js_code={code}&grant_type=authorization_code
注意:appid 和 secret 从 AppConfig 中读取,需在 erp-server 配置中新增 wechat 段。
- Step 2: 在 mod.rs 注册
添加 pub mod wechat_service;
- Step 3: 验证
Run: cargo check -p erp-auth
- Step 4: 提交
git add crates/erp-auth/src/service/wechat_service.rs crates/erp-auth/src/service/mod.rs
git commit -m "feat(auth): 实现微信登录/绑定手机号 service"
Task 5: 微信登录 Handler + 路由注册
Files:
-
Create:
crates/erp-auth/src/handler/wechat_handler.rs -
Modify:
crates/erp-auth/src/handler/mod.rs -
Modify:
crates/erp-auth/src/lib.rs(路由注册) -
Step 1: 创建 handler
// wechat_handler.rs
pub async fn wechat_login<S>(State(state): State<AuthState>, ...) -> Result<Json<ApiResponse<WechatLoginResp>>, AppError>
pub async fn wechat_bind_phone<S>(State(state): State<AuthState>, ...) -> Result<Json<ApiResponse<LoginResp>>, AppError>
- Step 2: 注册公开路由
微信登录端点是公开的(无需 JWT),在 AuthModule::public_routes() 中添加:
-
POST /auth/wechat/login -
POST /auth/wechat/bind-phone -
Step 3: 在 AppConfig 中添加微信配置
在 crates/erp-server/src/config.rs 中新增:
pub wechat: WechatConfig,
pub struct WechatConfig { pub appid: String, pub secret: String }
- Step 4: 验证
Run: cargo check -p erp-server
- Step 5: 提交
git add crates/erp-auth/src/handler/wechat_handler.rs crates/erp-auth/src/handler/mod.rs crates/erp-auth/src/lib.rs crates/erp-server/src/config.rs
git commit -m "feat(auth): 添加微信登录 handler 和公开路由"
Task 6: 验证微信登录端到端
- Step 1: 启动后端
Run: cargo run -p erp-server
- Step 2: 测试端点
用 curl 测试:
curl -X POST http://localhost:3000/api/v1/auth/wechat/login -H "Content-Type: application/json" -d '{"code":"test_code"}'
Expected: 返回 { "success": true, "data": { "bound": false, "openid": "..." } } 或类似响应
- Step 3: 确认迁移执行
检查 wechat_users 表已创建:
PGPASSWORD=123123 "D:\postgreSQL\bin\psql.exe" -U postgres -h localhost -d erp -c "\dt wechat_users"
- Step 4: 提交确认
如果有修复:
git add -A && git commit -m "fix(auth): 微信登录端到端验证修复"
Chunk 2: Phase 1 — 小程序项目骨架 + 登录 + 首页
Task 7: 初始化 Taro 项目
- Step 1: 用 Taro CLI 初始化项目
cd apps
npx @tarojs/cli init miniprogram --template react-ts
选择:
-
框架:React
-
TypeScript:Yes
-
CSS 预处理:Sass
-
模板:默认模板
-
编译工具:Webpack5
-
Step 2: 安装核心依赖
cd apps/miniprogram
pnpm add zustand
pnpm add echarts echarts-taro3-react
- Step 3: 配置 project.config.json
设置 appid(先用测试号 touristappid)、ES6 转 ES5、增强编译等。
- Step 4: 验证
cd apps/miniprogram && pnpm dev:weapp
用微信开发者工具打开 dist/ 目录,确认空白小程序可运行。
- Step 5: 提交
git add apps/miniprogram/
git commit -m "feat(miniprogram): 初始化 Taro 4 + React 项目骨架"
Task 8: 全局样式 + 主题变量
Files:
-
Modify:
apps/miniprogram/src/app.scss -
Create:
apps/miniprogram/src/styles/variables.scss -
Create:
apps/miniprogram/src/styles/mixins.scss -
Step 1: 创建主题变量
src/styles/variables.scss:
$pri: #0891B2;
$pri-l: #E0F7FA;
$pri-d: #065A73;
$pri-surface: #ECFEFF;
$acc: #059669;
$acc-l: #D1FAE5;
$bg: #F0FDFA;
$card: #FFFFFF;
$tx: #134E4A;
$tx2: #6B7280;
$tx3: #94A3B8;
$bd: #E5E7EB;
$bd-l: #F3F4F6;
$dan: #DC2626;
$dan-l: #FEE2E2;
$wrn: #D97706;
$wrn-l: #FEF3C7;
$r: 12px;
$r-sm: 8px;
$r-lg: 16px;
- Step 2: 创建常用 mixins
src/styles/mixins.scss:
@mixin card {
background: $card;
border-radius: $r;
box-shadow: 0 2px 8px rgba(0,0,0,.06);
padding: 16px;
margin: 0 16px 12px;
}
- Step 3: 更新 app.scss
导入变量和全局基础样式(字体、背景色)。
- Step 4: 提交
git add apps/miniprogram/src/styles/ apps/miniprogram/src/app.scss
git commit -m "feat(miniprogram): 添加医疗清新主题样式变量"
Task 9: services/request.ts — API 请求层
Files:
-
Create:
apps/miniprogram/src/services/request.ts -
Step 1: 实现 request 封装
import Taro from '@tarojs/taro';
const BASE_URL = process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1';
interface ApiResponse<T> {
success: boolean;
data?: T;
message?: string;
}
async function getHeaders(): Promise<Record<string, string>> {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
const token = Taro.getStorageSync('access_token');
if (token) headers['Authorization'] = `Bearer ${token}`;
const patientId = Taro.getStorageSync('current_patient_id');
if (patientId) headers['X-Patient-Id'] = patientId;
const tenantId = Taro.getStorageSync('tenant_id');
if (tenantId) headers['X-Tenant-Id'] = tenantId;
return headers;
}
export async function request<T>(method: string, path: string, data?: unknown): Promise<T> {
const headers = await getHeaders();
const res = await Taro.request({ url: `${BASE_URL}${path}`, method, data, header: headers });
if (res.statusCode === 401) {
const refreshed = await tryRefreshToken();
if (refreshed) return request(method, path, data);
Taro.redirectTo({ url: '/pages/login/index' });
throw new Error('登录已过期');
}
const body = res.data as ApiResponse<T>;
if (!body.success) throw new Error(body.message || '请求失败');
return body.data as T;
}
async function tryRefreshToken(): Promise<boolean> {
const refreshToken = Taro.getStorageSync('refresh_token');
if (!refreshToken) return false;
try {
const res = await Taro.request({
url: `${BASE_URL}/auth/refresh`,
method: 'POST',
data: { refresh_token: refreshToken },
});
if (res.statusCode === 200 && res.data?.success) {
Taro.setStorageSync('access_token', res.data.data.access_token);
Taro.setStorageSync('refresh_token', res.data.data.refresh_token);
return true;
}
} catch {}
Taro.removeStorageSync('access_token');
Taro.removeStorageSync('refresh_token');
return false;
}
export const api = {
get: <T>(path: string) => request<T>('GET', path),
post: <T>(path: string, data?: unknown) => request<T>('POST', path, data),
put: <T>(path: string, data?: unknown) => request<T>('PUT', path, data),
delete: <T>(path: string) => request<T>('DELETE', path),
};
- Step 2: 提交
git add apps/miniprogram/src/services/request.ts
git commit -m "feat(miniprogram): 实现 API 请求层封装"
Task 10: services/auth.ts + auth store
Files:
-
Create:
apps/miniprogram/src/services/auth.ts -
Create:
apps/miniprogram/src/stores/auth.ts -
Step 1: auth service
// services/auth.ts
import { api } from './request';
export interface LoginResp {
bound: boolean;
openid: string;
token?: { access_token: string; refresh_token: string; user: UserInfo };
}
export interface UserInfo {
id: string; name: string; phone: string; avatar?: string; tenant_id: string;
}
export interface PatientInfo {
id: string; name: string; gender?: string; birthday?: string; relation: string;
}
export async function wechatLogin(code: string): Promise<LoginResp> {
return api.post('/auth/wechat/login', { code });
}
export async function wechatBindPhone(openid: string, encryptedData: string, iv: string) {
return api.post('/auth/wechat/bind-phone', { openid, encrypted_data: encryptedData, iv });
}
export async function getPatients() {
return api.get<PatientInfo[]>('/health/patients');
}
- Step 2: auth store
参照 Web 端 stores/auth.ts 模式,使用 Taro.getStorageSync / setStorageSync 替代 localStorage。
State: token, refreshToken, user, currentPatient, patients, loading
Actions: login(), bindPhone(), setCurrentPatient(), logout()
- Step 3: 提交
git add apps/miniprogram/src/services/auth.ts apps/miniprogram/src/stores/auth.ts
git commit -m "feat(miniprogram): 实现 auth service 和 store"
Task 11: 登录页
Files:
-
Create:
apps/miniprogram/src/pages/login/index.tsx -
Create:
apps/miniprogram/src/pages/login/index.scss -
Step 1: 实现登录页 UI
登录页包含:
-
品牌标识(Logo + "健康管理" 标题)
-
微信一键登录按钮(调用
wx.login→wechatLogin(code)) -
如果返回
bound: false,显示手机号授权按钮(使用<Button openType="getPhoneNumber">) -
登录成功后
Taro.switchTab({ url: '/pages/index/index' }) -
Step 2: 实现样式
医疗清新风格:青色渐变背景、白色卡片、圆角按钮。
- Step 3: 注册页面路由
在 src/app.config.ts 的 pages 数组中添加 /pages/login/index。
- Step 4: 提交
git add apps/miniprogram/src/pages/login/ apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现微信登录页面"
Task 12: 首页(静态数据)
Files:
-
Create:
apps/miniprogram/src/pages/index/index.tsx -
Create:
apps/miniprogram/src/pages/index/index.scss -
Create:
apps/miniprogram/src/components/HealthCard/index.tsx -
Modify:
apps/miniprogram/src/app.config.ts(TabBar 配置) -
Step 1: 配置 TabBar
在 app.config.ts 中定义 5 个 Tab:
tabBar: {
color: '#94A3B8',
selectedColor: '#0891B2',
backgroundColor: '#FFFFFF',
list: [
{ pagePath: 'pages/index/index', text: '首页', iconPath: 'assets/tab-home.png', selectedIconPath: 'assets/tab-home-active.png' },
{ pagePath: 'pages/health/index', text: '健康', iconPath: 'assets/tab-health.png', selectedIconPath: 'assets/tab-health-active.png' },
{ pagePath: 'pages/appointment/index', text: '预约', iconPath: 'assets/tab-appt.png', selectedIconPath: 'assets/tab-appt-active.png' },
{ pagePath: 'pages/article/index', text: '资讯', iconPath: 'assets/tab-article.png', selectedIconPath: 'assets/tab-article-active.png' },
{ pagePath: 'pages/profile/index', text: '我的', iconPath: 'assets/tab-profile.png', selectedIconPath: 'assets/tab-profile-active.png' },
],
}
- Step 2: 创建 Tab 图标
准备 10 个图标文件(5 普通 + 5 选中态),放到 src/assets/。
- Step 3: 实现 HealthCard 组件
健康指标卡片:接收 title, value, unit, status,显示渐变背景 + 数值 + 状态标签。
- Step 4: 实现首页
按设计规格布局:问候栏 + 今日健康(2×2 网格)+ 快捷服务(4 宫格)+ 即将到来 + 待办随访。 MVP 先用静态数据,后续 Phase 对接真实 API。
- Step 5: 创建其他 Tab 占位页
为 health/index、appointment/index、article/index、profile/index 创建简单占位页,显示页面名称。
- Step 6: 验证
pnpm dev:weapp
微信开发者工具中确认:登录流程可用、首页布局正确、Tab 切换正常。
- Step 7: 提交
git add apps/miniprogram/src/
git commit -m "feat(miniprogram): 实现首页布局 + TabBar + 登录流程"
Chunk 3: Phase 2 — 健康数据录入 + 趋势图
Task 13: 后端 — vital_signs 趋势查询 API
Files:
-
Modify:
crates/erp-health/src/handler/health_data_handler.rs -
Modify:
crates/erp-health/src/service/health_data_service.rs -
Modify:
crates/erp-health/src/dto/health_data_dto.rs -
Modify:
crates/erp-health/src/module.rs(路由) -
Step 1: 添加趋势查询 DTO
在 health_data_dto.rs 新增:
#[derive(Deserialize, ToSchema)]
pub struct TrendQueryParams {
pub indicator: String, // "blood_pressure_systolic" 等
pub start_date: Option<chrono::NaiveDate>,
pub end_date: Option<chrono::NaiveDate>,
pub range: Option<String>, // "7d", "30d", "90d"
}
#[derive(Serialize, ToSchema)]
pub struct TrendResp {
pub indicator: String,
pub data_points: Vec<DataPoint>,
}
#[derive(Serialize, ToSchema)]
pub struct DataPoint {
pub date: String,
pub value: f64,
}
- Step 2: 添加趋势查询 service 函数
在 health_data_service.rs 新增 get_vital_signs_trend(),基于已有的 get_indicator_timeseries() 封装,支持 range 参数自动计算日期范围。
- Step 3: 添加 handler
pub async fn get_trend<S>(...) -> Result<Json<ApiResponse<TrendResp>>, AppError>
- Step 4: 注册路由
在 module.rs 的 protected_routes() 中添加 GET /health/vital-signs/trend。
- Step 5: 验证
Run: cargo check -p erp-health
- Step 6: 提交
git add crates/erp-health/src/
git commit -m "feat(health): 添加 vital_signs 趋势查询 API"
Task 14: 小程序 — 健康数据录入页
Files:
-
Create:
apps/miniprogram/src/pages/health/index.tsx -
Create:
apps/miniprogram/src/pages/health/index.scss -
Create:
apps/miniprogram/src/pages/health/input/index.tsx -
Create:
apps/miniprogram/src/pages/health/input/index.scss -
Create:
apps/miniprogram/src/services/health.ts -
Step 1: health service
// services/health.ts
import { api } from './request';
export interface VitalSignInput {
indicator_type: string;
value: number;
measured_at?: string;
note?: string;
}
export interface TodaySummary {
blood_pressure?: { systolic: number; diastolic: number; status: string };
heart_rate?: { value: number; status: string };
blood_sugar?: { value: number; status: string };
weight?: { value: number; status: string };
}
export async function getTodaySummary() {
return api.get<TodaySummary>('/health/vital-signs?date=today');
}
export async function inputVitalSign(data: VitalSignInput) {
return api.post('/health/vital-signs', data);
}
export async function getTrend(indicator: string, range: string) {
return api.get<{ indicator: string; data_points: { date: string; value: number }[] }>(
`/health/vital-signs/trend?indicator=${indicator}&range=${range}`
);
}
- Step 2: 健康数据首页
展示今日健康数据概览(2×2 卡片网格)+ 最近趋势缩略图 + "录入数据" 按钮。
- Step 3: 数据录入页
表单:选择指标类型(Picker)→ 输入数值 → 选择测量时间(DateTimePicker)→ 备注(可选)→ 提交。
提交成功后 Taro.navigateBack() 并刷新上一页数据。
- Step 4: 注册页面路由
app.config.ts 中添加 pages/health/input/index。
- Step 5: 验证
微信开发者工具中确认录入表单可操作,提交后数据出现在健康首页。
- Step 6: 提交
git add apps/miniprogram/src/pages/health/ apps/miniprogram/src/services/health.ts apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现健康数据录入页面"
Task 15: 小程序 — 趋势图组件
Files:
-
Create:
apps/miniprogram/src/components/TrendChart/index.tsx -
Create:
apps/miniprogram/src/pages/health/trend/index.tsx -
Create:
apps/miniprogram/src/pages/health/trend/index.scss -
Step 1: TrendChart 组件
使用 echarts-taro3-react 渲染折线图。接收 data: { date: string; value: number }[] 和 title: string。
- Step 2: 趋势详情页
顶部:指标名称 + 时间范围切换(7天/30天/90天) 中部:TrendChart 折线图 底部:数据表格(日期 + 数值)
- Step 3: 注册路由
app.config.ts 添加 pages/health/trend/index。
- Step 4: 验证
切换时间范围,图表数据刷新。
- Step 5: 提交
git add apps/miniprogram/src/components/TrendChart/ apps/miniprogram/src/pages/health/trend/ apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现健康趋势图页面"
Task 16: health store + 首页数据联动
Files:
-
Create:
apps/miniprogram/src/stores/health.ts -
Modify:
apps/miniprogram/src/pages/index/index.tsx(对接真实数据) -
Step 1: health store
// stores/health.ts
import { create } from 'zustand';
import * as healthApi from '../services/health';
interface HealthState {
todaySummary: healthApi.TodaySummary | null;
loading: boolean;
refreshToday: () => Promise<void>;
}
export const useHealthStore = create<HealthState>((set) => ({
todaySummary: null,
loading: false,
refreshToday: async () => {
set({ loading: true });
try {
const data = await healthApi.getTodaySummary();
set({ todaySummary: data, loading: false });
} catch { set({ loading: false }); }
},
}));
- Step 2: 首页接入真实数据
替换首页静态数据,从 useHealthStore().todaySummary 读取。useDidShow 生命周期触发 refreshToday()。
- Step 3: 提交
git add apps/miniprogram/src/stores/health.ts apps/miniprogram/src/pages/index/
git commit -m "feat(miniprogram): 首页对接真实健康数据 API"
Chunk 4: Phase 3 — 预约挂号
Task 17: 后端 — 排班查询 API 适配
Files:
-
Modify:
crates/erp-health/src/handler/health_data_handler.rs或对应的排班 handler -
Modify:
crates/erp-health/src/dto/health_data_dto.rs -
Step 1: 确认排班 API 支持按科室/医生查询
检查现有 GET /health/doctor-schedules 是否支持 department_id 和 doctor_id 查询参数。如不支持,添加相应过滤。
- Step 2: 添加"可用时段"聚合
新增 DTO 和 handler:输入医生 + 日期范围 → 返回每日可用时段及剩余名额。
- Step 3: 验证
Run: cargo check -p erp-health
- Step 4: 提交
git add crates/erp-health/src/
git commit -m "feat(health): 排班查询支持科室/医生过滤 + 可用时段聚合"
Task 18: 小程序 — 预约服务 + 列表页
Files:
-
Create:
apps/miniprogram/src/services/appointment.ts -
Modify:
apps/miniprogram/src/pages/appointment/index.tsx -
Create:
apps/miniprogram/src/pages/appointment/index.scss -
Step 1: appointment service
// services/appointment.ts
import { api } from './request';
export interface Appointment {
id: string; doctor_name: string; department: string;
date: string; time_slot: string; status: string;
}
export async function listAppointments(page = 1) {
return api.get<{ data: Appointment[]; total: number }>(`/health/appointments?page=${page}`);
}
export async function createAppointment(data: {
doctor_id: string; schedule_id: string; patient_id: string;
}) {
return api.post('/health/appointments', data);
}
export async function cancelAppointment(id: string) {
return api.put(`/health/appointments/${id}/cancel`);
}
export async function getDoctorSchedules(doctorId: string, startDate: string, endDate: string) {
return api.get(`/health/doctor-schedules?doctor_id=${doctorId}&start_date=${startDate}&end_date=${endDate}`);
}
- Step 2: 预约列表页
显示预约记录列表(按时间倒序),每项包含医生名、科室、日期、时段、状态标签。支持下拉刷新 + 上拉加载更多。
- Step 3: 提交
git add apps/miniprogram/src/services/appointment.ts apps/miniprogram/src/pages/appointment/
git commit -m "feat(miniprogram): 实现预约列表页"
Task 19: 小程序 — 新建预约页
Files:
-
Create:
apps/miniprogram/src/pages/appointment/create/index.tsx -
Create:
apps/miniprogram/src/pages/appointment/create/index.scss -
Create:
apps/miniprogram/src/pages/appointment/detail/index.tsx -
Step 1: 新建预约页 — 步骤流程
4 步向导:
- 选择科室(Picker)
- 选择医生(列表,显示头像+姓名+职称+科室)
- 选择日期和时段(周视图日历 + 时段列表)
- 确认预约(显示摘要 + 提交按钮)
- Step 2: 预约详情页
展示预约详情 + 取消预约按钮 + 导航到体检中心(地图)。
- Step 3: 注册路由
app.config.ts 添加 pages/appointment/create/index 和 pages/appointment/detail/index。
- Step 4: 验证
完整走通:选科室 → 选医生 → 选时段 → 确认 → 查看详情 → 取消。
- Step 5: 提交
git add apps/miniprogram/src/pages/appointment/ apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现新建预约和预约详情页"
Chunk 5: Phase 4 — 报告查询 + 家庭管理
Task 20: 小程序 — 报告服务 + 列表/详情页
Files:
-
Create:
apps/miniprogram/src/services/report.ts -
Create:
apps/miniprogram/src/pages/report/index.tsx -
Modify:
apps/miniprogram/src/app.config.ts -
Step 1: report service
import { api } from './request';
export interface LabReport {
id: string; title: string; type: string; date: string; status: string;
indicators?: LabIndicator[];
}
export interface LabIndicator {
name: string; value: number; unit: string; reference_min: number; reference_max: number;
status: 'normal' | 'high' | 'low';
}
export async function listReports(page = 1) {
return api.get<{ data: LabReport[]; total: number }>(`/health/lab-reports?page=${page}`);
}
export async function getReportDetail(id: string) {
return api.get<LabReport>(`/health/lab-reports/${id}`);
}
- Step 2: 报告列表页
卡片列表:报告名称 + 类型标签 + 日期。下拉刷新 + 上拉分页。
- Step 3: 报告详情页
基本信息卡 + 指标列表(正常灰色、异常红色高亮 + 箭头)+ 附件预览区。
- Step 4: 提交
git add apps/miniprogram/src/services/report.ts apps/miniprogram/src/pages/report/
git commit -m "feat(miniprogram): 实现报告查询列表和详情页"
Task 21: 小程序 — 家庭管理(就诊人切换)
Files:
-
Create:
apps/miniprogram/src/components/FamilyPicker/index.tsx -
Create:
apps/miniprogram/src/pages/profile/family/index.tsx -
Create:
apps/miniprogram/src/pages/profile/family-add/index.tsx -
Modify:
apps/miniprogram/src/pages/profile/index.tsx -
Step 1: FamilyPicker 组件
顶部下拉选择器:显示当前就诊人姓名 + 头像,点击展开列表切换。切换后更新 auth store 的 currentPatient + Taro.setStorageSync('current_patient_id', id)。
- Step 2: 就诊人管理页
列表显示已添加的就诊人(本人 + 家属),支持添加新就诊人。
- Step 3: 添加就诊人页
表单:姓名 + 与本人关系(Picker)+ 性别 + 出生日期 + 身份证号(可选)。提交 POST /health/patients。
- Step 4: 首页/健康/预约页接入 FamilyPicker
在这些页面顶部添加 FamilyPicker,切换就诊人后重新拉取数据。
- Step 5: 注册路由并提交
git add apps/miniprogram/src/components/FamilyPicker/ apps/miniprogram/src/pages/profile/family/ apps/miniprogram/src/pages/profile/family-add/
git commit -m "feat(miniprogram): 实现家庭管理 + 就诊人切换"
Chunk 6: Phase 5 — 随访 + 资讯 + 用药提醒
Task 22: 后端 — articles 表 + API
Files:
-
Create:
crates/erp-server/migration/src/m20260423_000002_articles.rs -
Create:
crates/erp-health/src/entity/article.rs -
Create:
crates/erp-health/src/dto/article_dto.rs -
Create:
crates/erp-health/src/handler/article_handler.rs -
Create:
crates/erp-health/src/service/article_service.rs -
Step 1: 创建 articles 迁移
表结构:id, tenant_id, title, summary, content(富文本), cover_image, category, author, published_at, created_at, updated_at, deleted_at, version
- Step 2: Entity + DTO + Service + Handler
参照现有 health_data 的模式创建完整 CRUD。公开接口只需 list 和 detail。
-
Step 3: 注册路由和权限
-
Step 4: 验证
Run: cargo check -p erp-health
- Step 5: 提交
git add crates/erp-health/src/ crates/erp-server/migration/
git commit -m "feat(health): 添加健康资讯 articles 表和 API"
Task 23: 小程序 — 随访功能
Files:
-
Create:
apps/miniprogram/src/services/followup.ts -
Create:
apps/miniprogram/src/pages/followup/index.tsx -
Create:
apps/miniprogram/src/pages/followup/detail/index.tsx -
Step 1: followup service
-
Step 2: 随访任务列表页
按状态分组:待完成 / 已完成 / 已过期。每项显示任务名 + 截止日期 + 状态。
- Step 3: 随访详情/填写页
动态表单渲染(后端定义字段)→ 提交 → 标记完成。
-
Step 4: 首页待办随访接入真实数据
-
Step 5: 提交
git add apps/miniprogram/src/services/followup.ts apps/miniprogram/src/pages/followup/
git commit -m "feat(miniprogram): 实现随访任务列表和填写页"
Task 24: 小程序 — 健康资讯
Files:
-
Create:
apps/miniprogram/src/services/article.ts -
Modify:
apps/miniprogram/src/pages/article/index.tsx -
Create:
apps/miniprogram/src/pages/article/detail/index.tsx -
Step 1: article service
-
Step 2: 资讯列表页
缩略图 + 标题 + 摘要 + 时间,下拉刷新 + 分页。
- Step 3: 资讯详情页
使用 Taro RichText 渲染富文本内容。
- Step 4: 提交
git add apps/miniprogram/src/services/article.ts apps/miniprogram/src/pages/article/
git commit -m "feat(miniprogram): 实现健康资讯列表和详情页"
Task 25: 小程序 — 用药提醒
Files:
-
Create:
apps/miniprogram/src/pages/profile/medication/index.tsx -
Create:
apps/miniprogram/src/utils/medication-reminder.ts -
Step 1: 用药提醒工具
本地 storage 存储提醒规则:{ id, name, frequency, times: string[], enabled }。
- Step 2: 用药提醒管理页
列表展示已添加的提醒 + 添加/编辑/删除/开关。
- Step 3: 提交
git add apps/miniprogram/src/pages/profile/medication/ apps/miniprogram/src/utils/medication-reminder.ts
git commit -m "feat(miniprogram): 实现用药提醒管理页"
Chunk 7: Phase 6 — 打磨 + 真机测试 + 提审
Task 26: "我的" 页面完善
Files:
-
Modify:
apps/miniprogram/src/pages/profile/index.tsx -
Create:
apps/miniprogram/src/pages/profile/settings/index.tsx -
Step 1: 个人中心页
展示头像 + 姓名 + 手机号。功能入口:就诊人管理、我的报告、我的随访、用药提醒、设置。
- Step 2: 设置页
清除缓存、关于我们、隐私政策、退出登录。
- Step 3: 提交
git add apps/miniprogram/src/pages/profile/
git commit -m "feat(miniprogram): 完善个人中心页"
Task 27: 空状态 + 错误处理 + Loading
Files:
-
Create:
apps/miniprogram/src/components/EmptyState/index.tsx -
Create:
apps/miniprogram/src/components/ErrorState/index.tsx -
Create:
apps/miniprogram/src/components/Loading/index.tsx -
Step 1: 通用空状态组件
显示图标 + 文案 + 操作按钮(如"去预约")。
- Step 2: 通用错误组件
网络错误 / 服务器错误 / 无权限,带重试按钮。
- Step 3: 各页面接入
所有列表页添加空状态、错误状态、Loading 态。
- Step 4: 提交
git add apps/miniprogram/src/components/EmptyState/ apps/miniprogram/src/components/ErrorState/ apps/miniprogram/src/components/Loading/
git commit -m "feat(miniprogram): 添加空状态/错误/Loading 通用组件"
Task 28: 分包 + 性能优化
- Step 1: 配置分包
app.config.ts 中定义 subPackages:
health/— 健康数据相关页面appointment/— 预约相关report/— 报告相关followup/— 随访相关article/— 资讯相关
主包保留:首页 + 登录 + 我的 + 公共组件。
- Step 2: 图表库按需引入
echarts 只引入折线图模块,减小体积。
- Step 3: 提交
git add apps/miniprogram/src/app.config.ts
git commit -m "perf(miniprogram): 配置分包加载 + 图表按需引入"
Task 29: 真机测试 + 提审准备
- Step 1: 真机调试
通过微信开发者工具预览/真机调试,在多款真机上验证。
-
Step 2: 检查清单
-
登录流程正常(微信授权 + 手机号绑定)
-
健康数据可录入并查看趋势
-
预约可创建/取消
-
报告可查看
-
随访可填写
-
就诊人可切换
-
无白屏、无 JS 报错
-
包体积 < 2MB(主包)
-
隐私政策和用户协议已配置
-
Step 3: 构建生产版本
pnpm build:weapp
- Step 4: 提交最终版本
git add -A
git commit -m "release(miniprogram): v1.0.0 提审版本"
git push