feat: 电子药箱小程序 - 4Tab药品管理
- 电子药箱: 药品库存管理 + 手动录入 + 今日用药打卡 - 药品百科: 搜索 + 分类筛选 + 20种药品静态数据 - 惠教中心: 4条药品使用指南课程 - 我的: 家庭成员信息管理 - 自定义TabBar + navigation-bar组件 - SVG药品分类插图
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding: 20rpx 30rpx 60rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: #ffffff; border-radius: 24rpx; padding: 30rpx;
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.form-group { margin-bottom: 30rpx; }
|
||||
.form-label { font-size: 28rpx; font-weight: 600; color: #333; margin-bottom: 14rpx; }
|
||||
.required { color: #FF3B30; }
|
||||
|
||||
.form-input {
|
||||
width: 100%; height: 80rpx; background: #f7f8fa;
|
||||
border-radius: 14rpx; padding: 0 24rpx; font-size: 28rpx; box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-picker {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
width: 100%; height: 80rpx; background: #f7f8fa;
|
||||
border-radius: 14rpx; padding: 0 24rpx; font-size: 28rpx;
|
||||
box-sizing: border-box; color: #333;
|
||||
}
|
||||
|
||||
.form-picker.placeholder { color: #bbb; }
|
||||
.picker-arrow { font-size: 22rpx; color: #999; }
|
||||
|
||||
.form-textarea {
|
||||
width: 100%; height: 140rpx; background: #f7f8fa;
|
||||
border-radius: 14rpx; padding: 20rpx 24rpx; font-size: 28rpx; box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-row { display: flex; gap: 20rpx; }
|
||||
.form-row .half { flex: 1; }
|
||||
|
||||
.slot-group { display: flex; gap: 16rpx; }
|
||||
|
||||
.slot-chip {
|
||||
flex: 1; text-align: center; height: 72rpx; line-height: 72rpx;
|
||||
background: #f7f8fa; border-radius: 14rpx; font-size: 26rpx;
|
||||
color: #666; border: 2rpx solid transparent;
|
||||
}
|
||||
|
||||
.slot-chip.active {
|
||||
background: #e8f8ee; color: #07C160;
|
||||
border-color: #07C160; font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-area { margin-top: 40rpx; }
|
||||
|
||||
.save-btn {
|
||||
width: 100%; height: 90rpx; line-height: 90rpx;
|
||||
background: linear-gradient(135deg, #07C160, #06AD56);
|
||||
color: #fff; font-size: 32rpx; font-weight: 600;
|
||||
border-radius: 18rpx; border: none; margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 100%; height: 90rpx; line-height: 90rpx;
|
||||
background: #fff; color: #FF3B30; font-size: 32rpx;
|
||||
border-radius: 18rpx; border: 2rpx solid #FF3B30;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
interface MedicineForm {
|
||||
name: string;
|
||||
category: string;
|
||||
dosage: string;
|
||||
frequency: string;
|
||||
slots: string[];
|
||||
quantity: number;
|
||||
unit: string;
|
||||
expiryDate: string;
|
||||
notes: string;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'medicine_box_list';
|
||||
const FREQ_SLOTS: Record<string, string[]> = {
|
||||
'每日1次': ['早'],
|
||||
'每日2次': ['早', '晚'],
|
||||
'每日3次': ['早', '中', '晚'],
|
||||
'每日4次': ['早', '中', '晚', '睡前'],
|
||||
'每周1次': ['自定义'],
|
||||
'按需服用': ['按需'],
|
||||
};
|
||||
|
||||
Page({
|
||||
data: {
|
||||
editId: '',
|
||||
categories: ['抗生素', '解热镇痛', '心血管', '降糖药', '消化系统', '抗过敏', '维生素', '中成药', '外用', '其他'],
|
||||
frequencies: ['每日1次', '每日2次', '每日3次', '每日4次', '每周1次', '按需服用'],
|
||||
units: ['盒', '瓶', '片', '粒', '支', '包', '袋'],
|
||||
timeSlots: ['早', '中', '晚', '睡前', '按需'],
|
||||
categoryIndex: -1,
|
||||
freqIndex: -1,
|
||||
unitIndex: -1,
|
||||
today: '',
|
||||
form: {
|
||||
name: '',
|
||||
category: '',
|
||||
dosage: '',
|
||||
frequency: '',
|
||||
slots: [] as string[],
|
||||
quantity: 1,
|
||||
unit: '',
|
||||
expiryDate: '',
|
||||
notes: '',
|
||||
} as MedicineForm,
|
||||
},
|
||||
|
||||
onLoad(options: Record<string, string>) {
|
||||
const today = new Date();
|
||||
this.setData({
|
||||
today: `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`,
|
||||
editId: options.id || '',
|
||||
});
|
||||
|
||||
if (options.id) {
|
||||
const list: any[] = wx.getStorageSync(STORAGE_KEY) || [];
|
||||
const item = list.find((m: any) => m.id === options.id);
|
||||
if (item) {
|
||||
const form = {
|
||||
name: item.name,
|
||||
category: item.category,
|
||||
dosage: item.dosage,
|
||||
frequency: item.frequency,
|
||||
slots: item.slots || [],
|
||||
quantity: item.quantity,
|
||||
unit: item.unit,
|
||||
expiryDate: item.expiryDate,
|
||||
notes: item.notes || '',
|
||||
};
|
||||
this.setData({
|
||||
form,
|
||||
categoryIndex: this.data.categories.indexOf(item.category),
|
||||
freqIndex: this.data.frequencies.indexOf(item.frequency),
|
||||
unitIndex: this.data.units.indexOf(item.unit),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onFieldChange(e: WechatMiniprogram.Input) {
|
||||
const { field } = e.currentTarget.dataset;
|
||||
this.setData({ [`form.${field}`]: e.detail.value });
|
||||
},
|
||||
|
||||
onCategoryChange(e: WechatMiniprogram.PickerChange) {
|
||||
const idx = Number(e.detail.value);
|
||||
this.setData({ categoryIndex: idx, 'form.category': this.data.categories[idx] });
|
||||
},
|
||||
|
||||
onFreqChange(e: WechatMiniprogram.PickerChange) {
|
||||
const idx = Number(e.detail.value);
|
||||
const freq = this.data.frequencies[idx];
|
||||
const slots = FREQ_SLOTS[freq] || ['早', '中', '晚'];
|
||||
this.setData({ freqIndex: idx, 'form.frequency': freq, 'form.slots': slots });
|
||||
},
|
||||
|
||||
onUnitChange(e: WechatMiniprogram.PickerChange) {
|
||||
const idx = Number(e.detail.value);
|
||||
this.setData({ unitIndex: idx, 'form.unit': this.data.units[idx] });
|
||||
},
|
||||
|
||||
onDateChange(e: WechatMiniprogram.PickerChange) {
|
||||
this.setData({ 'form.expiryDate': e.detail.value });
|
||||
},
|
||||
|
||||
toggleSlot(e: WechatMiniprogram.TouchEvent) {
|
||||
const { slot } = e.currentTarget.dataset;
|
||||
const slots = [...this.data.form.slots];
|
||||
const idx = slots.indexOf(slot);
|
||||
if (idx > -1) {
|
||||
slots.splice(idx, 1);
|
||||
} else {
|
||||
slots.push(slot);
|
||||
}
|
||||
this.setData({ 'form.slots': slots });
|
||||
},
|
||||
|
||||
save() {
|
||||
const { form, editId } = this.data;
|
||||
if (!form.name.trim()) {
|
||||
wx.showToast({ title: '请输入药品名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (!form.category) {
|
||||
wx.showToast({ title: '请选择药品分类', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const list: any[] = wx.getStorageSync(STORAGE_KEY) || [];
|
||||
|
||||
if (editId) {
|
||||
const idx = list.findIndex((m: any) => m.id === editId);
|
||||
if (idx > -1) {
|
||||
list[idx] = { ...list[idx], ...form };
|
||||
}
|
||||
} else {
|
||||
list.unshift({
|
||||
id: Date.now().toString(),
|
||||
...form,
|
||||
addedAt: new Date().toISOString().split('T')[0],
|
||||
});
|
||||
}
|
||||
|
||||
wx.setStorageSync(STORAGE_KEY, list);
|
||||
wx.showToast({ title: editId ? '已更新' : '已添加', icon: 'success' });
|
||||
setTimeout(() => wx.navigateBack(), 800);
|
||||
},
|
||||
|
||||
cancel() {
|
||||
const { editId } = this.data;
|
||||
if (!editId) {
|
||||
wx.navigateBack();
|
||||
return;
|
||||
}
|
||||
wx.showModal({
|
||||
title: '确认删除',
|
||||
content: '将删除此药品及打卡记录',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const list: any[] = wx.getStorageSync(STORAGE_KEY) || [];
|
||||
wx.setStorageSync(STORAGE_KEY, list.filter((m: any) => m.id !== editId));
|
||||
const checkins = wx.getStorageSync('checkin_records') || {};
|
||||
delete checkins[editId];
|
||||
wx.setStorageSync('checkin_records', checkins);
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
setTimeout(() => wx.navigateBack(), 800);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
<view class="page">
|
||||
<navigation-bar title="{{editId ? '编辑药品' : '添加药品'}}" back="{{true}}" color="black" background="#ffffff" />
|
||||
|
||||
<view class="form-section">
|
||||
<view class="form-group">
|
||||
<view class="form-label">药品名称 <text class="required">*</text></view>
|
||||
<input class="form-input" placeholder="请输入药品名称" value="{{form.name}}" bindinput="onFieldChange" data-field="name" />
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">药品分类</view>
|
||||
<picker mode="selector" range="{{categories}}" value="{{categoryIndex}}" bindchange="onCategoryChange">
|
||||
<view class="form-picker {{form.category ? '' : 'placeholder'}}">
|
||||
{{form.category || '请选择分类'}}
|
||||
<text class="picker-arrow">▼</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-row">
|
||||
<view class="form-group half">
|
||||
<view class="form-label">单次用量</view>
|
||||
<input class="form-input" placeholder="如:1片" value="{{form.dosage}}" bindinput="onFieldChange" data-field="dosage" />
|
||||
</view>
|
||||
<view class="form-group half">
|
||||
<view class="form-label">服用频次</view>
|
||||
<picker mode="selector" range="{{frequencies}}" value="{{freqIndex}}" bindchange="onFreqChange">
|
||||
<view class="form-picker {{form.frequency ? '' : 'placeholder'}}">
|
||||
{{form.frequency || '请选择'}}
|
||||
<text class="picker-arrow">▼</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">服用时段</view>
|
||||
<view class="slot-group">
|
||||
<view wx:for="{{timeSlots}}" wx:key="*this" class="slot-chip {{form.slots.indexOf(item) > -1 ? 'active' : ''}}" data-slot="{{item}}" bindtap="toggleSlot">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-row">
|
||||
<view class="form-group half">
|
||||
<view class="form-label">库存数量</view>
|
||||
<input class="form-input" type="number" placeholder="数量" value="{{form.quantity}}" bindinput="onFieldChange" data-field="quantity" />
|
||||
</view>
|
||||
<view class="form-group half">
|
||||
<view class="form-label">单位</view>
|
||||
<picker mode="selector" range="{{units}}" value="{{unitIndex}}" bindchange="onUnitChange">
|
||||
<view class="form-picker {{form.unit ? '' : 'placeholder'}}">
|
||||
{{form.unit || '请选择'}}
|
||||
<text class="picker-arrow">▼</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">有效期至</view>
|
||||
<picker mode="date" value="{{form.expiryDate}}" start="{{today}}" bindchange="onDateChange">
|
||||
<view class="form-picker {{form.expiryDate ? '' : 'placeholder'}}">
|
||||
{{form.expiryDate || '请选择有效期'}}
|
||||
<text class="picker-arrow">📅</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">备注</view>
|
||||
<textarea class="form-textarea" placeholder="添加备注信息..." value="{{form.notes}}" bindinput="onFieldChange" data-field="notes" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-area">
|
||||
<button class="save-btn" bindtap="save">保存药品</button>
|
||||
<button class="cancel-btn" bindtap="cancel" wx:if="{{editId}}">删除此药品</button>
|
||||
</view>
|
||||
</view>
|
||||
Reference in New Issue
Block a user