HarmonyOS 药箱管理完整源码,库存与有效期模板化



实例:药箱管理(Medicine Cabinet)|技术:全量代码解读、操作剧本、库存+有效期模式抽象
一、本篇定位
前两篇分别讲了数据层与 UI 层,本篇完成三件事:
- 全量源码逐段解读——MedicineDao 与 MedicinePage 从头到尾过一遍;
- 运行效果演示——从新增药品到临期筛选的完整操作剧本;
- 模式抽象——"库存 + 有效期"通用模板(会过期的库存品),附两个改写场景。
二、完整源码:MedicineDao.ets
/**
* 药箱管理数据访问对象(DAO)
* 对应文章:29_药箱管理/29-1 建表与数据层设计、29-2 页面UI与操作实现、29-3 完整代码与运行效果
* 核心能力:药品档案 + 数量增减事务、批次有效期管理、临期筛选(日期函数)、用药记录
*/
import { relationalStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
/** 药品实体,对应 medicine 表结构 */
export interface Medicine {
id: number;
name: string;
spec: string;
category: string;
batch: string;
count: number;
unit: string;
expireDate: number; // 有效期至(0 = 不限)
storage: string;
note: string;
createdAt: number;
}
/** 用药记录实体,对应 dose_log 表结构 */
export interface DoseLog {
id: number;
medicineId: number;
change: number; // 正=入库 负=取出
reason: string;
createdAt: number;
}
const DOMAIN = 0x0001;
const TAG = 'MedicineDao';
/** 临期阈值(天) */
export const EXPIRE_WARN_DAYS = 90;
export class MedicineDao {
private static readonly TABLE = 'medicine';
private static readonly LOG_TABLE = 'dose_log';
private static store?: relationalStore.RdbStore;
// getStore / 行映射 / 增删改查 / 统计 / 种子数据(见上篇逐段解读)
}
逐段解读总表
| 代码段 | 职责 | 关键决策 |
|---|---|---|
| 实体接口 | Medicine / DoseLog | change 有符号编码方向 |
| EXPIRE_WARN_DAYS | 临期阈值常量 | 页面与 DAO 口径一致 |
| getStore | 单例 + 建 2 表 + 3 索引 | expire 索引服务临期查询 |
| insertMedicine | 新增 + 初始日志 | 总量来源可追溯 |
| changeCount | 事务增减 + 负库存校验 | 数量与日志原子,布尔返回 |
| deleteMedicine | 先删日志再删药品 | 杜绝孤儿日志 |
| queryExpiring / queryExpired | 日期范围筛选 | 边界严谨不重叠 |
| queryLogs | 变动日志倒序 | 审计追溯 |
| summary | 种类/总数/临期/过期 | 动态时间快照 |
| categoryStats | GROUP BY 分类 | 分布一屏 |
| initSeedData | 10 种药四态效期 | 演示全覆盖 |
三、完整源码:MedicinePage.ets
/**
* 药箱管理页面
* 对应文章:29_药箱管理/29-1 ~ 29-3
* 功能:临期/过期筛选 Tab + 药品卡片列表 + 新增药品 + 入库/取出 + 数量日志抽屉 + 分类统计
*/
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { MedicineDao, Medicine, DoseLog, EXPIRE_WARN_DAYS } from '../../database/MedicineDao';
@Entry
@Component
struct MedicinePage {
@State meds: Medicine[] = [];
@State filter: number = 0;
@State summaryText: string = '';
@State categoryText: string = '';
@State formVisible: boolean = false;
@State fName: string = '';
@State fSpec: string = '';
@State fCategory: string = '';
@State fCount: string = '';
@State fExpire: string = '';
@State detailVisible: boolean = false;
@State current: Medicine | null = null;
@State logs: DoseLog[] = [];
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
aboutToAppear(): void {
this.refresh();
}
// refresh/loadList/onAdd/onCountChange/openDetail/onDelete/statusInfo/fmtDate/fmtDateTime 见中篇
}
页面组织纪律回顾
- 状态量三组:列表态(meds/filter/summaryText/categoryText)、表单态(f*)、抽屉态(detailVisible/current/logs);
- 交互方法齐全:增(onAdd)、改数量(onCountChange)、查(openDetail)、删(onDelete)——CRUD 全覆盖;
- UI 零 SQL:效期徽标由 statusInfo 纯函数计算,数据来自 DAO。
四、接入工程三步
Step 1:放文件
MedicineDao.ets→entry/src/main/ets/database/MedicinePage.ets→entry/src/main/ets/pages/samples/
Step 2:注册路由(main_pages.json)
"pages/samples/MedicinePage",
Step 3:首页入口(Index.ets)
Button('🧴 29 药箱管理')
.fontSize(14)
.width('80%')
.onClick(() => {
this.getUIContext().getRouter().pushUrl({ url: 'pages/samples/MedicinePage' });
})
五、运行效果演示(操作剧本)
场景一:首次打开
| 步骤 | 界面表现 | 数据来源 |
|---|---|---|
| 1 | 标题栏:“🧴 药箱管理”,统计:“共 10 种 27 件 · 临期 1 · 已过期 1” | summary |
| 2 | 分类行:“感冒 9 · 肠胃 7 · 外伤 7 · 止痛退烧 2 · 保健 1 · 器材 1” | categoryStats |
| 3 | 列表按效期升序:健胃消食片(红"已过期 20 天")排最前,感冒灵颗粒(橙"临期 60 天")第二,其后正常绿徽标,温度计灰"不限有效期"最后 | queryAll(orderByAsc expire_date) |
| 4 | 每张卡片第三行:库存数 + 红-绿+ + 📋 | — |
场景二:临期筛选
点"临期"Tab → 只显示感冒灵颗粒(橙徽标"临期 60 天")。点"已过期"→ 只显示健胃消食片(红徽标"已过期 20 天")。点"全部"恢复。
场景三:快捷增减
- 布洛芬库存 2 → 点 + → toast"✅ 入库 1 盒",库存变 3;
- 点 - → toast"📤 取出 1 盒",库存回 2;
- 温度计库存 1 → 点 - → toast"⚠️ 库存不足"(0 不能负)。
场景四:日志追溯
点布洛芬的 📋 → 抽屉显示日志(倒序):
- “手动入库 · +1 盒 · 2026-06-15 14:30”(刚才的操作)
- “初始入库 · +2 盒 · 2026-06-15 14:28”(种子)
每次增减都被记录,来源可溯。
场景五:新增药品
点 + → 填"开瑞坦 / 10mg*6片/盒 / 过敏 / 1 / 2027-03-01" → 保存 → toast"✅ 药品已添加",列表出现(效期 2027-03-01 绿色徽标),统计"共 11 种"。
场景六:删除
打开任意药 📋 → 点"🗑 删除"→ 确认(提示日志一并删除)→ toast"🗑 已删除",统计同步。
场景七:数据持久性
杀应用重开 → 数据仍在。
六、模式抽象:"库存 + 有效期"通用模板
6.1 模型
┌───────────────────────────────────────────────┐
│ 库存品档案表(如 medicine) │
│ id / 名称规格 / 分类 / 数量(count) / 单位 │
│ / 有效期至(expire_date, 0=不限) / 存放位置 │
│ ── 有什么、有多少、到何时 │
└──────────────────────┬────────────────────────┘
│ 1:N
┌──────────────────────┴────────────────────────┐
│ 数量变动日志表(如 dose_log) │
│ id / 物品_id / change(±数量) / reason / 时间 │
│ ── 每次增减的可追溯记录(只追加不修改) │
└───────────────────────────────────────────────┘
6.2 七个约定
- 数量 + 有效期双维度:count 管"有多少",expire_date 管"到何时";
- 0 编码"无":expire_date=0 不限效期;count 从不负(负库存被校验拦截);
- 变更即日志:任何数量变化都写 dose_log(含初始入库),可追溯;
- 有符号变动:change 正=入、负=出,SUM 即净变化;
- 事务原子:数量与日志同写,缺一不可;
- 日期范围筛选:临期/过期用
expire_date BETWEEN/< now,边界严谨; - 阈值集中:EXPIRE_WARN_DAYS 一处定义,徽标与筛选同步。
6.3 两个改写场景
场景 A:食品保质期管理(冰箱/储物柜)
| 药箱实例 | 食品改写 |
|---|---|
| medicine | food(食品:名称/品牌/保质期至) |
| dose_log | consume_log(消费:吃掉/购入) |
| 效期徽标 | 临期食品提醒(先吃临期的) |
| 分类统计 | 冷藏/冷冻/干货分类分布 |
场景 B:化妆品/日用品效期管理
| 药箱实例 | 化妆品改写 |
|---|---|
| medicine | cosmetic(品名/规格/开封日期/保质期) |
| 临期阈值 | 开封后 6 个月(PAO 标识) |
| 数量日志 | 用空/补货记录 |
场景 C:仓库耗材/办公用品——数量事务 + 到期批次 + 领用日志,模板完全复用。
三个场景的共同点:"库存品 + 数量变动 + 有效期"三元结构。掌握 29,任何"会过期、有数量"的管理系统都能落地。
七、三篇内容总对照
| 篇章 | 核心知识点 | 一句话总结 |
|---|---|---|
| 29-1 建表与数据层设计 | 库存事务、0 编码、日期筛选 | 数量与效期双维度,变更即日志 |
| 29-2 页面UI与操作实现 | 效期四态徽标、快捷增减、日志抽屉 | 效期颜色化,增减一步到位 |
| 29-3 完整代码与运行效果 | 全量源码、操作剧本、库存模板 | 会过期的库存品,改表名即复用 |
八、FAQ(三篇汇总补遗)
Q1:29 和 06 库存进销存有什么区别?
A:06 进销存是"商品 + 出入库流水"的完整商贸模型(含单价、供应商、多仓库);29 药箱是"个人库存品 + 数量增减"的轻量模型,但新增了有效期维度。06 管"账",29 管"物品的时效"。若药箱要管单价进货,可合并两模型。
Q2:为什么日志的 reason 不是枚举?
A:家庭场景原因多样(新购入/服用/过期处理/送人),自由文本更灵活。若做强制规范(统计原因分布),可用枚举。自由文本优先,枚举在"需要聚合统计"时引入。
Q3:效期排序为什么升序?
A:orderByAsc('expire_date') 让临期/过期排最前——用户打开列表先看到"最紧急的"。这是按紧迫度排序,与 26/27 的按时间倒序(最新优先)不同。排序方向由业务关注点决定。
Q4:多种药品同分类为什么数量相加?
A:categoryStats 的 SUM(count) 是"该分类总件数",跨单位(盒+袋+支)只是粗略示意。精确统计按单位拆分。页面文案已弱化单位(“感冒 9”),接受近似。
Q5:能加服药提醒吗?
A:可以,但提醒属于"定时任务"范畴(@kit.BackgroundTasksKit 的提醒代理或闹钟),超出本系列 SQLite 主题。建议单独实例(30 美食菜谱之后的选题)或扩展 05 习惯打卡的提醒机制。
Q6:temperature 药品(如胰岛素)需要冷藏记录吗?
A:可加 storage 字段存"冷藏 2-8℃"等条件,本实例 storage 已存在。温度监控属 IoT 范畴,非 SQLite 主题。
九、结语
三篇文章完整走完了药箱管理的"设计 → 实现 → 复用"闭环:
- -1 教你库存建模:数量 + 有效期双维度、变更即日志、日期范围筛选;
- -2 教你交互落地:效期四态徽标、快捷增减、日志抽屉追溯;
- -3 教你模式复用:三元结构模板,改表名即覆盖食品、化妆品、耗材。
更多推荐



所有评论(0)