Prompt file imported from lxKylin/yucheng-mini (
.codex/prompts/M11.prompt.md). Copyright stays with the author.
M11 · MedicineComposer(统一新增/编辑 Sheet)
任务概述
你是一名熟练的 Taro 4 + React 18 + TypeScript + Zustand + @taroify/core 专家。请按照以下规范,逐步完成 M11 MedicineComposer 统一新增 / 编辑 Sheet 的全部开发内容。完成每个子任务后自行验证,确认无误再进入下一个。
本模块承接 M10 / M10a 的统一 Medicine 数据层:用一个 MedicineComposer 替代旧 ReminderForm,覆盖“新增开药提醒”“新增药品”“编辑药品 / 提醒”三类场景。reminderEnabled 开关控制开药提醒配置区展开或折叠;药品基础信息始终保存。
参考资料
- 开发计划:
docs/3.开发计划.md的「V1.1 药箱模块开发计划」与「M11 · MedicineComposer(统一新增/编辑 Sheet)」 - UI 参考:
ui7.0/index.htmlcomposerContent():统一新增 / 编辑表单结构、提醒开关、实时预算提示attachComposerInteractions():提醒开关、固定时间字段、预算提示联动- FAB 行为:提醒页默认新增开药提醒,药箱页默认新增药品
- 前置模块:
- M10:已建立
Medicine/DerivedMedicine类型、常量和药箱选择器 - M10a:已完成旧 UI 字段回归,
ReminderForm仍可用但本模块要迁移入口
- M10:已建立
当前项目状态
src/types/index.ts已定义:MedicineDerivedMedicineMedicineFormDosageUnitMedicineSchedule
src/constants/index.ts已定义:MEDICINE_FORM_OPTIONSDOSAGE_UNIT_OPTIONSSCHEDULE_OPTIONSTIMES_PER_DAY_OPTIONSINTERVAL_OPTIONSBEFORE_OPTIONSDEFAULT_INTERVALDEFAULT_BEFOREDEFAULT_REMIND_TIME
src/store/reminderStore.ts仍保留旧 API 名:addReminder(payload)updateReminder(id, payload)getById(id)getDerivedList()getAllDerivedMedicines()
src/hooks/useReminders.ts已提供:useDerivedList()useDerivedById(id)useAllDerivedMedicines()useReminderActions()
src/components/ReminderForm/是旧提醒表单,本模块新增MedicineComposer后,首页 / 列表 / 我的页的新增编辑入口应改为使用MedicineComposersrc/hooks/useReminderSheet.ts当前只有detail | form模式,标题仍是“新增提醒 / 编辑提醒”,需要适配统一表单命名与默认提醒开关src/pages/medicines/index.tsx当前仍是占位页,M12 才实现完整药箱页;本模块不开发药箱列表 UI
技术约束
- 保持函数式组件 + Hooks + TypeScript 类型安全
- 优先使用
@taroify/core组件;小程序 Picker / DatePicker / TimePicker 可继续使用@tarojs/components的Picker - 不新增云数据库集合,不修改云函数扫描逻辑,不重写 M9 云开发认证和订阅消息
- 不实现 M12 药箱 Tab UI、
MedicineInventoryCard、药箱搜索筛选 - 不实现库存估算,不引入
stockPerPrescription、estimatedStock、stockDays等字段 - 不删除
ReminderForm,但本模块完成后新增 / 编辑入口不应再依赖它 - 保持旧已开药闭环、暂停 / 启用、删除、详情 Sheet 不回归
- 修改保持小步精准,不顺手重构无关页面或样式
本模块新增 / 修改文件
src/
├── components/
│ └── MedicineComposer/
│ ├── index.tsx ← 新建:统一新增 / 编辑表单
│ └── index.scss ← 新建:表单样式
├── hooks/
│ └── useReminderSheet.ts ← 修改:统一表单标题和 defaultReminderEnabled 支持
└── pages/
├── home/
│ └── index.tsx ← 修改:替换 ReminderForm 为 MedicineComposer,默认开启提醒
├── list/
│ └── index.tsx ← 修改:替换 ReminderForm 为 MedicineComposer,默认开启提醒
└── profile/
└── index.tsx ← 修改:如仍存在旧 ReminderForm 入口,同步替换为 MedicineComposer
如发现 src/pages/medicines/index.tsx 已经有新增入口或临时按钮,可接入 MedicineComposer 且设置 defaultReminderEnabled=false;如果仍是占位页,不要为了 M11 扩展药箱页。
子任务 1:新建 MedicineComposer 组件
新建:
src/components/MedicineComposer/index.tsxsrc/components/MedicineComposer/index.scss
1.1 Props
interface MedicineComposerProps {
medicineId?: string;
defaultReminderEnabled?: boolean;
onSuccess: (medicine?: Medicine) => void;
onCancel: () => void;
}
说明:
- 不需要在组件内部再包一层
BottomSheet,继续由页面上的BottomSheet承载 medicineId有值时为编辑模式,通过useDerivedById(medicineId)回填defaultReminderEnabled仅在新增模式生效:- 首页 / 提醒列表:传
true - 药箱新增入口:传
false
- 首页 / 提醒列表:传
onSuccess在新增或更新成功后调用,页面负责关闭 Sheet
1.2 表单状态
组件内部维护完整 Medicine 表单值,至少包含:
interface FormValues {
name: string;
spec: string;
form: MedicineForm;
expiryDate: string;
note: string;
dosagePerUse: number;
dosageUnit: DosageUnit;
timesPerDay: number;
scheduleTiming: MedicineSchedule;
scheduleTime: string;
reminderEnabled: boolean;
currentPrescriptionDate: string;
intervalDays: number;
remindAdvanceDays: number;
remindTime: string;
}
默认值:
name:''spec:''form:'tablet'expiryDate:''note:''dosagePerUse:1dosageUnit:'片'timesPerDay:1scheduleTiming:'饭后'scheduleTime:'08:00'reminderEnabled:defaultReminderEnabled ?? falsecurrentPrescriptionDate:today()intervalDays:DEFAULT_INTERVALremindAdvanceDays:loadSettings().defaultBefore || DEFAULT_BEFOREremindTime:loadSettings().defaultTime || DEFAULT_REMIND_TIME
编辑模式回填所有字段,尤其是:
expiryDateformdosagePerUsedosageUnittimesPerDayscheduleTimingscheduleTimereminderEnabledcurrentPrescriptionDateintervalDaysremindAdvanceDaysremindTimenote
子任务 2:实现表单分区与交互
2.1 基础信息区
字段:
- 药品名称:
Input,必填 - 规格:
Input - 剂型:
Picker,使用MEDICINE_FORM_OPTIONS - 过期时间:
Picker mode="date",允许为空 - 备注:
Textarea
2.2 服药信息区
字段:
- 每次用量:数字输入或 Stepper,最小值建议
0.5或1,避免负数 - 单位:
Picker,使用DOSAGE_UNIT_OPTIONS - 每日次数:数字输入或 Stepper,范围
1–6 - 服用时机:
Picker,使用SCHEDULE_OPTIONS - 具体时间点:仅当
scheduleTiming === '固定时间'时展示,使用Picker mode="time",默认08:00
交互要求:
- 非“固定时间”时隐藏具体时间点,并在提交 payload 中将
scheduleTime置为'' - 切换回“固定时间”时,如果没有时间值,回填
08:00
2.3 开药提醒区
顶部使用开关控件展示:
- 标题:“开药提醒”
- 辅助文案:“开启后会进入首页风险中心和提醒列表”
- 开关状态绑定
reminderEnabled
当 reminderEnabled=false:
- 提醒配置区折叠
- 提交后保存为纯药箱药品
currentPrescriptionDate/intervalDays/remindAdvanceDays/remindTime仍写入稳定默认值,不能留空导致派生报错
当 reminderEnabled=true:
展开字段:
- 最近开药日期:
Picker mode="date" - 开药间隔:数字输入 + 快捷按钮,使用
INTERVAL_OPTIONS - 提前提醒天数:
Picker或快捷按钮,使用BEFORE_OPTIONS - 提醒时刻:
Picker mode="time"
实时预算提示:
预计下次开药日期:YYYY-MM-DD · 提醒日:MM-DD HH:mm
实现要求:
- 使用
calcNextDate()/calcRemindDate()计算 - 字段变化时即时更新
intervalDays非法时展示“请输入 1-365 之间的开药间隔”
子任务 3:提交、校验与 Store 写入
3.1 校验
提交时必须校验:
- 药品名称不能为空,失败 Toast:“请填写药品名称”
dosagePerUse必须大于 0,否则 Toast:“每次用量需大于 0”timesPerDay范围为1–6,否则 Toast:“每日次数需在 1-6 次之间”reminderEnabled=true时:intervalDays范围为1–365currentPrescriptionDate不能为空remindTime不能为空
3.2 新增 payload
新增时调用 addReminder(),传入完整 Medicine 字段:
{
name,
spec,
form,
expiryDate,
note,
dosagePerUse,
dosageUnit,
timesPerDay,
scheduleTiming,
scheduleTime,
reminderEnabled,
currentPrescriptionDate,
intervalDays,
remindAdvanceDays,
remindTime,
status: REMINDER_STATUS.ACTIVE,
prescriptionHistory: reminderEnabled ? [currentPrescriptionDate] : [],
lastWechatReminderDate: '',
lastWechatReminderAt: ''
}
Toast:
reminderEnabled=true:开药提醒已创建reminderEnabled=false:药品已保存到药箱
3.3 编辑 payload
编辑时调用 updateReminder(medicineId, payload)。
要求:
- 更新所有表单字段
- 如果从
reminderEnabled=false切换为true,且prescriptionHistory为空,不强制补历史;只确保currentPrescriptionDate稳定存在 - 如果从
reminderEnabled=true切换为false,不要清空开药配置和历史记录,只更新reminderEnabled=false - Toast:
药品已更新
3.4 成功回调
新增 / 编辑成功后:
- 调用
onSuccess(savedMedicine?) - 页面关闭 BottomSheet
- 不在组件内部直接操作页面路由
子任务 4:替换页面入口
4.1 修改 src/hooks/useReminderSheet.ts
让旧 hook 可以承载统一表单:
- 保留
detail | form模式,避免大范围改动 - 新增
defaultReminderEnabled状态 openCreate(defaultReminderEnabled = true)支持传入默认提醒开关openEdit(id)保持编辑模式,默认提醒开关由已有药品决定- 标题改为:
- 详情:
提醒详情 - 编辑:
编辑药品 - 新增且
defaultReminderEnabled=true:新增开药提醒 - 新增且
defaultReminderEnabled=false:新增药品
- 详情:
handleFormSuccess()继续关闭 Sheet,并在新增后刷新 form key
4.2 修改首页 src/pages/home/index.tsx
- 移除
ReminderFormimport,改用MedicineComposer - Sheet 中
sheetMode === 'form'时渲染MedicineComposer FloatingAddReminder点击仍调用openCreate(true)MedicineComposer传入:key={formKey}medicineId={editReminderId}defaultReminderEnabled={true}onSuccess={handleFormSuccess}onCancel={closeSheet}
4.3 修改提醒列表 src/pages/list/index.tsx
同首页:
- 替换
ReminderForm为MedicineComposer - 新增入口默认
openCreate(true) - 编辑入口仍由详情页
onEdit={openEdit}触发
4.4 修改我的页 src/pages/profile/index.tsx
如果我的页仍存在旧 ReminderForm 新增入口:
- 替换为
MedicineComposer - 如果该入口语义是“新增提醒”,传
defaultReminderEnabled=true - 如果语义是“新增药品备忘”,传
defaultReminderEnabled=false
不要借 M11 改写我的页通知、授权或统计模块。
子任务 5:样式要求
src/components/MedicineComposer/index.scss 参考现有 ReminderForm 和 ui7.0 的表单风格。
要求:
- Sheet 内表单纵向布局,字段间距清晰
- 分区使用轻量标题,不要做大面积装饰卡片
- 控件点击区域不小于 44px
- 提醒开关、快捷按钮、Picker 展示状态要清楚
reminderEnabled=false时提醒区折叠后页面不留下大块空白- 固定时间字段显示 / 隐藏不造成布局跳动异常
- 文案不要溢出按钮或标签容器
- 色彩沿用现有主题变量和页面风格,不引入新的大色系
子任务 6:回归检查
完成后至少检查:
6.1 静态扫描
运行:
rg -n "ReminderForm" src/pages src/components src/hooks
期望:
src/components/ReminderForm/自身可存在- 首页 / 列表 / 我的页不再 import 或渲染
ReminderForm
再运行:
rg -n "MedicineComposer|defaultReminderEnabled|openCreate" src/pages src/hooks src/components
确认入口和默认提醒开关传递正确。
6.2 TypeScript 检查
优先运行:
pnpm exec tsc --noEmit
如项目当前没有独立 tsc 配置或存在环境限制,运行项目等效检查,并在最终说明中写清楚。
6.3 启动检查
运行:
pnpm dev:weapp
看到编译成功或无 M11 新增错误后即可停止。
验收标准
组件
-
src/components/MedicineComposer/index.tsx和index.scss存在 - 新增模式默认值正确,首页 / 列表打开时
reminderEnabled=true - 编辑模式能回填所有 Medicine 字段,包括
expiryDate、剂型、用量、单位、每日次数、固定时间点、提醒开关和提醒配置 - 非固定时间隐藏
scheduleTime字段,固定时间显示并保存 - 开关关闭时提醒区折叠,保存后
reminderEnabled=false - 开关开启时提醒区展开,实时预算提示随日期、间隔、提前天数、提醒时间更新
数据
- 新增
reminderEnabled=false的药品能保存完整 Medicine 字段 - 新增
reminderEnabled=true的药品能进入首页 / 提醒列表 -
reminderEnabled=false的药品不会进入首页风险中心和提醒列表 - 编辑时切换提醒开关不会清空历史记录或造成日期派生异常
- 名称、用量、每日次数、开药间隔的非法输入会 Toast 提示并阻止提交
页面入口
- 首页新增按钮打开
MedicineComposer,标题为“新增开药提醒” - 列表新增按钮打开
MedicineComposer,标题为“新增开药提醒” - 从详情点击编辑打开
MedicineComposer,标题为“编辑药品” - 我的页若存在旧新增 / 编辑入口,已替换为
MedicineComposer - 旧
ReminderForm不再被首页 / 列表 / 我的页作为新增编辑入口使用
回归
- 已开药、逾期已开药日期确认、暂停 / 启用、删除、详情查看不回归
- 首页和提醒列表仍只展示提醒视角数据
-
pnpm exec tsc --noEmit或等效 TypeScript 检查无 M11 新增错误 -
pnpm dev:weapp可启动,无 M11 引入的编译错误
开发提示
- 先实现
MedicineComposer,再替换页面入口,最后做静态扫描和编译检查 - 可以复用旧
ReminderForm的输入事件类型、日期预算计算和基础样式思路,但不要把旧表单继续作为页面入口 - Store 的
addReminder已会补齐默认值,但MedicineComposer仍应提交清晰、完整的业务字段,方便后续 M12 药箱页直接消费 - M11 不开发药箱页列表;
reminderEnabled=false的可见性主要通过数据层选择器和后续 M12 验收承接
