# medication-record
**Repository Path**: MengXY/medication-record
## Basic Information
- **Project Name**: medication-record
- **Description**: 纯前端本地用药记录工具
- **Primary Language**: HTML/CSS
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 0
- **Created**: 2015-05-07
- **Last Updated**: 2026-05-31
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
用药记录 (Medication Record)
Pad 横屏用药记录工具 · 本地存储 · 无需登录 · 开箱即用
## 介绍
一款面向家庭的 Pad 横屏用药记录工具。数据完全存储在浏览器本地,无法多设备同步访问,开箱即用。
**核心场景**:将 Pad 放在家中固定位置(如客厅、餐桌),家人经过时即可随手记录用药情况。适合家里有老人、慢性病患者需要长期服药,多个家庭成员需分别记录各自的用药。
- 每天吃了没、吃了多少次、什么时间吃的,一目了然。
- 退出浏览器或关闭 Pad,数据不会丢失。
- 更换设备需通过浏览器导出/导入 IndexedDB 数据。
## 功能特性
- **👨👩👧👦 多用户支持** — 家庭每个成员可创建独立的用药人,互不干扰,顶部栏可快速切换
- **💊 药品管理** — 添加、编辑、软删除药品卡片,支持丰富的服用周期配置
- **📅 日历视图** — 在日历上查看每天各药品的服用状态(黄点待服、绿点已服)
- **✅ 记录勾选** — 点击当天用药记录的勾选框标记已服用,记录精确到分钟的实际服药时间
- **📊 历史追溯** — 已勾选的服药记录永久保存,切换日期可查看历史用药情况
- **🔄 软删除机制** — 删除药品后,历史服药记录依然保留可查
- **🎨 颜色标识** — 每种药品可设置独立颜色,在日历和卡片中快速区分
- **📟 Pad 横屏适配** — 布局针对 Pad 横屏优化,药品卡片横向滚动 + 大尺寸日历一览无余
- **🔒 纯本地存储** — 数据仅保存在当前浏览器 IndexedDB 中,不经过任何服务器,无法多设备同步访问
## 服用周期类型
| 周期 | 每日次数 | 说明 |
|------|---------|------|
| 每天1次 | 1 | 每日服用一次 |
| 每天2次 | 2 | 早晚各一次 |
| 每天3次 | 3 | 早中晚各一次 |
| 每2天1次 | 1 | 间隔一天服用 |
| 每3天1次 | 1 | 间隔两天服用 |
| 每周1次 | 1 | 每周固定一天 |
| 每月1次 | 1 | 每月固定日期 |
## 技术栈
| 类别 | 技术 | 用途 |
|------|------|------|
| 框架 | [Vue 3](https://vuejs.org/) + [Composition API](https://vuejs.org/guide/composition-api-setup) | 响应式 UI |
| 语言 | [TypeScript](https://www.typescriptlang.org/) | 类型安全 |
| 构建 | [Vite](https://vitejs.dev/) | 极速开发构建 |
| 路由 | [Vue Router](https://router.vuejs.org/) | 页面导航 |
| 状态管理 | [Pinia](https://pinia.vuejs.org/) | 全局状态 |
| UI 组件 | [Vant 4](https://vant-ui.github.io/) | 移动端组件库 |
| 数据持久化 | [IndexedDB](https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API) (idb) | 浏览器本地存储 |
| 部署 | Nginx + SSH2 | 生产环境部署 |
## 项目结构
```
medication-record/
├── src/
│ ├── components/ # 组件
│ │ ├── HomeHeader.vue # 顶部导航栏(用户切换 + 添加用户)
│ │ ├── MedicationCard.vue # 药品卡片(横向滚动)
│ │ ├── CalendarDay.vue # 日历日期格子
│ │ ├── AddMedicationModal.vue # 添加/编辑药品弹窗
│ │ ├── EditMedicationModal.vue# 编辑药品弹窗(含删除)
│ │ ├── MedicationRecordModal.vue # 用药记录弹窗(勾选服药)
│ │ └── MedicationCalendar.vue # 完整日历页(补录功能)
│ ├── views/ # 页面
│ │ ├── HomeView.vue # 首页(药品卡片 + 日历)
│ │ ├── CreateUserView.vue # 首次创建用药人
│ │ └── UserManagementView.vue # 用药人管理
│ ├── stores/
│ │ └── app.ts # Pinia 全局状态
│ ├── utils/
│ │ └── db.ts # IndexedDB 数据模型与工具函数
│ ├── router/
│ │ └── index.ts # 路由配置
│ ├── App.vue # 根组件
│ └── main.ts # 入口文件
├── deploy.cjs # 一键部署脚本
├── deploy.config.example.cjs # 部署配置文件示例(复制为 deploy.config.cjs 使用)
├── package.json
└── vite.config.ts
```
## 数据模型
所有数据存储在浏览器 **IndexedDB** 中,数据库名 `medication-record-db`,包含三个对象仓库:
### Users(用药人)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 主键 |
| name | string | 用药人姓名 |
| createdAt | number | 创建时间戳 |
### Medications(药品)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 主键 |
| userId | string | 所属用药人 ID |
| name | string | 药品名称 |
| startTime | number | 开始服用日期 |
| cycleType | enum | 服用周期类型 |
| color | string | 标识颜色 |
| deleted? | true | 软删除标记 |
| createdAt | number | 创建时间戳 |
### Records(服药记录)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 主键 |
| medicationId | string | 药品 ID |
| date | number | 服药日期(当天 00:00 时间戳) |
| times | number[] | 已服用的次数索引,如 [0, 1] |
| actualTimes | Record | timeIndex → 实际服用时间戳映射 |
| createdAt | number | 创建时间戳 |
## 核心实现思路
### 服药周期计算
通过 `shouldTakeMedication()` 函数计算某药品在某天是否需要服用。核心算法是计算目标日期与开始日期之间的天数差,再根据周期类型取模判断:
```typescript
const diffDays = Math.floor((targetDate - startDate) / (1000 * 60 * 60 * 24));
switch (medication.cycleType) {
case 'daily_1': // 每天 → 总是返回 true
case 'every_2_days': // 每2天 → diffDays % 2 === 0
case 'every_3_days': // 每3天 → diffDays % 3 === 0
case 'weekly': // 每周 → diffDays % 7 === 0
case 'monthly': // 每月 → targetDate.getDate() === startDate.getDate()
}
```
### 服药记录勾选
在 `toggleRecord()` 中,勾选/取消勾选的核心逻辑:
1. 查找该药品当天的已有记录
2. **勾选**:将 timeIndex 加入 times 数组,并记录当前时间戳到 actualTimes
3. **取消**:从 times 数组移除 timeIndex,删除对应 actualTimes;若 times 为空则删除整条记录
### 软删除
删除药品时并非真实从数据库移除,而是将 `deleted` 标记为 `true`:
- 首页药品滚动列表**过滤掉** deleted 的药品
- 日历**仅显示**已有实际服药记录的已删除药品日期
- 新增同名药品时,若存在软删除记录则**合并更新**,恢复所有历史服药数据
## 快速开始
### 环境要求
- Node.js >= 20.19.0 或 >= 22.12.0
### 安装与运行
```bash
# 克隆项目
git clone
cd medication-record
# 安装依赖
npm install
# 本地开发(热更新)
npm run dev
# 生产构建
npm run build
# 预览构建产物
npm run preview
```
### 使用流程
```
1. 首次访问 → 输入姓名创建用药人
2. 首页点击 "+" 添加药品 → 填写药品名称、开始日期、服用周期、颜色
3. 在日历上点击某天的日期 → 弹出当天用药记录弹窗
4. 点击勾选框 → 标记该次药已服用,记录实际服药时间
5. 如需再次取消勾选 → 再次点击即可取消
6. 顶部栏可切换不同用药人
7. 点击导航栏 "+" 按钮添加新用药人
```
## 部署
### 一键部署脚本
```bash
# 安装部署依赖
npm install ssh2-sftp-client
# 方式一:使用配置文件(推荐)
# 复制示例配置文件,填入实际服务器信息
cp deploy.config.example.cjs deploy.config.cjs
# 编辑 deploy.config.cjs,填入服务器地址、用户名、密码
# 然后直接运行:
node deploy.cjs
# 方式二:交互式输入
node deploy.cjs
```
> 配置优先级:`deploy.config.cjs` > 环境变量 (`DEPLOY_HOST` / `DEPLOY_USER` / `DEPLOY_PASS`) > 交互式输入
>
> `deploy.config.cjs` 已在 `.gitignore` 中,不会被提交到 Git,避免凭证泄露
### 手动部署
```bash
# 构建
npm run build
# 将 dist/ 目录上传到服务器 /www/web/medication-record/
```
### 在线地址
[https://medication.esugar.cn](https://medication.esugar.cn)
## 数据安全与存储
- **纯本地存储** — 所有数据存储在浏览器本地 IndexedDB,不上传任何服务器
- **单设备使用** — 数据与浏览器绑定,无法多设备同步访问(设计初衷即为固定设备使用)
- **数据持久性** — 关闭浏览器或重启 Pad,数据不丢失
- **浏览器数据清除** — 若清除浏览器数据(Cookie / 缓存 / IndexedDB),数据将被删除,请提前做好备份
- **更换设备** — 旧数据在新设备上不可见,需自行通过浏览器开发者工具导出 IndexedDB 数据
## License
[MIT](LICENSE)
Copyright (c) 2026