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,105 @@
|
||||
.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);
|
||||
}
|
||||
|
||||
.avatar-row {
|
||||
display: flex; align-items: flex-start; margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.avatar-label {
|
||||
font-size: 28rpx; font-weight: 600; color: #333;
|
||||
width: 100rpx; padding-top: 10rpx; flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar-options {
|
||||
flex: 1; display: flex; flex-wrap: wrap; gap: 14rpx;
|
||||
}
|
||||
|
||||
.avatar-opt {
|
||||
width: 68rpx; height: 68rpx;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: #f7f8fa; border-radius: 50%; font-size: 34rpx;
|
||||
border: 3rpx solid transparent;
|
||||
}
|
||||
|
||||
.avatar-opt.selected {
|
||||
border-color: #4A90D9; background: #e8f0fa; transform: scale(1.1);
|
||||
}
|
||||
|
||||
.form-group { margin-bottom: 28rpx; }
|
||||
.form-label { font-size: 28rpx; font-weight: 600; color: #333; margin-bottom: 14rpx; }
|
||||
|
||||
.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; }
|
||||
.arrow { font-size: 22rpx; color: #999; }
|
||||
|
||||
.form-row { display: flex; gap: 20rpx; }
|
||||
.form-row .half { flex: 1; }
|
||||
|
||||
.gender-row { display: flex; gap: 14rpx; }
|
||||
|
||||
.gender-btn {
|
||||
flex: 1; text-align: center; height: 80rpx; line-height: 80rpx;
|
||||
background: #f7f8fa; border-radius: 14rpx; font-size: 26rpx;
|
||||
color: #666; border: 2rpx solid transparent;
|
||||
}
|
||||
|
||||
.gender-btn.active.male {
|
||||
background: #e8f0fa; color: #4A90D9; border-color: #4A90D9; font-weight: 600;
|
||||
}
|
||||
|
||||
.gender-btn.active.female {
|
||||
background: #fce8ec; color: #E85D75; border-color: #E85D75; font-weight: 600;
|
||||
}
|
||||
|
||||
.blood-row { display: flex; gap: 14rpx; }
|
||||
|
||||
.blood-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;
|
||||
}
|
||||
|
||||
.blood-chip.active {
|
||||
background: #fff0e8; color: #FF6B35; border-color: #FF6B35; font-weight: 600;
|
||||
}
|
||||
|
||||
.divider {
|
||||
text-align: center; font-size: 26rpx; color: #999;
|
||||
padding: 10rpx 0 24rpx; margin-bottom: 24rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 100%; height: 140rpx; background: #f7f8fa;
|
||||
border-radius: 14rpx; padding: 20rpx 24rpx; font-size: 28rpx; box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn-area { margin-top: 40rpx; }
|
||||
|
||||
.save-btn {
|
||||
width: 100%; height: 90rpx; line-height: 90rpx;
|
||||
background: linear-gradient(135deg, #4A90D9, #357ABD);
|
||||
color: #fff; font-size: 32rpx; font-weight: 600;
|
||||
border-radius: 18rpx; border: none;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
const MEMBER_KEY = 'family_member';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
avatars: ['👤', '👨', '👩', '👴', '👵', '👶', '🧒', '👦', '👧'],
|
||||
relations: ['本人', '配偶', '父亲', '母亲', '儿子', '女儿', '爷爷', '奶奶', '外公', '外婆', '其他'],
|
||||
bloodTypes: ['A', 'B', 'AB', 'O', '未知'],
|
||||
relationIdx: -1,
|
||||
today: '',
|
||||
form: {
|
||||
avatar: '👤',
|
||||
name: '',
|
||||
gender: '',
|
||||
birthday: '',
|
||||
relation: '',
|
||||
bloodType: '',
|
||||
allergies: '',
|
||||
chronicDiseases: '',
|
||||
longTermMeds: '',
|
||||
notes: '',
|
||||
},
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const today = new Date();
|
||||
this.setData({
|
||||
today: `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`,
|
||||
});
|
||||
|
||||
const member = wx.getStorageSync(MEMBER_KEY);
|
||||
if (member && member.name) {
|
||||
this.setData({
|
||||
form: { ...this.data.form, ...member },
|
||||
relationIdx: this.data.relations.indexOf(member.relation),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
pickAvatar(e: WechatMiniprogram.TouchEvent) {
|
||||
this.setData({ 'form.avatar': e.currentTarget.dataset.avatar });
|
||||
},
|
||||
|
||||
onField(e: WechatMiniprogram.Input) {
|
||||
this.setData({ [`form.${e.currentTarget.dataset.field}`]: e.detail.value });
|
||||
},
|
||||
|
||||
onRelationChange(e: WechatMiniprogram.PickerChange) {
|
||||
const idx = Number(e.detail.value);
|
||||
this.setData({ relationIdx: idx, 'form.relation': this.data.relations[idx] });
|
||||
},
|
||||
|
||||
onBirthdayChange(e: WechatMiniprogram.PickerChange) {
|
||||
this.setData({ 'form.birthday': e.detail.value });
|
||||
},
|
||||
|
||||
pickGender(e: WechatMiniprogram.TouchEvent) {
|
||||
this.setData({ 'form.gender': e.currentTarget.dataset.gender });
|
||||
},
|
||||
|
||||
pickBlood(e: WechatMiniprogram.TouchEvent) {
|
||||
this.setData({ 'form.bloodType': e.currentTarget.dataset.bt });
|
||||
},
|
||||
|
||||
save() {
|
||||
if (!this.data.form.name.trim()) {
|
||||
wx.showToast({ title: '请输入姓名', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.setStorageSync(MEMBER_KEY, this.data.form);
|
||||
wx.showToast({ title: '已保存', icon: 'success' });
|
||||
setTimeout(() => wx.navigateBack(), 800);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
<view class="page">
|
||||
<navigation-bar title="编辑家庭成员" back="{{true}}" color="black" background="#ffffff" />
|
||||
|
||||
<view class="form-section">
|
||||
<!-- 头像选择 -->
|
||||
<view class="avatar-row">
|
||||
<view class="avatar-label">头像</view>
|
||||
<view class="avatar-options">
|
||||
<view wx:for="{{avatars}}" wx:key="*this" class="avatar-opt {{form.avatar === item ? 'selected' : ''}}" data-avatar="{{item}}" bindtap="pickAvatar">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-row">
|
||||
<view class="form-group half">
|
||||
<view class="form-label">姓名</view>
|
||||
<input class="form-input" placeholder="请输入姓名" value="{{form.name}}" bindinput="onField" data-field="name" />
|
||||
</view>
|
||||
<view class="form-group half">
|
||||
<view class="form-label">关系</view>
|
||||
<picker mode="selector" range="{{relations}}" value="{{relationIdx}}" bindchange="onRelationChange">
|
||||
<view class="form-picker {{form.relation ? '' : 'placeholder'}}">{{form.relation || '请选择'}}<text class="arrow">▼</text></view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-row">
|
||||
<view class="form-group half">
|
||||
<view class="form-label">性别</view>
|
||||
<view class="gender-row">
|
||||
<view class="gender-btn {{form.gender === '男' ? 'active male' : ''}}" data-gender="男" bindtap="pickGender">♂ 男</view>
|
||||
<view class="gender-btn {{form.gender === '女' ? 'active female' : ''}}" data-gender="女" bindtap="pickGender">♀ 女</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group half">
|
||||
<view class="form-label">出生日期</view>
|
||||
<picker mode="date" value="{{form.birthday}}" end="{{today}}" bindchange="onBirthdayChange">
|
||||
<view class="form-picker {{form.birthday ? '' : 'placeholder'}}">{{form.birthday || '请选择'}}<text class="arrow">📅</text></view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">血型</view>
|
||||
<view class="blood-row">
|
||||
<view wx:for="{{bloodTypes}}" wx:key="*this" class="blood-chip {{form.bloodType === item ? 'active' : ''}}" data-bt="{{item}}" bindtap="pickBlood">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="divider">📋 健康信息</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">过敏史</view>
|
||||
<input class="form-input" placeholder="如:青霉素过敏、花粉过敏" value="{{form.allergies}}" bindinput="onField" data-field="allergies" />
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">慢性病史</view>
|
||||
<input class="form-input" placeholder="如:高血压、糖尿病" value="{{form.chronicDiseases}}" bindinput="onField" data-field="chronicDiseases" />
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">长期用药</view>
|
||||
<input class="form-input" placeholder="如:氨氯地平5mg/日" value="{{form.longTermMeds}}" bindinput="onField" data-field="longTermMeds" />
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<view class="form-label">备注</view>
|
||||
<textarea class="form-textarea" placeholder="其他需要记录的信息…" value="{{form.notes}}" bindinput="onField" data-field="notes" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-area">
|
||||
<button class="save-btn" bindtap="save">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 120rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
padding: 50rpx 40rpx 40rpx;
|
||||
background: linear-gradient(135deg, #4A90D9, #357ABD);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 140rpx; height: 140rpx;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.2); border-radius: 50%;
|
||||
font-size: 70rpx; margin-bottom: 20rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.name { font-size: 38rpx; font-weight: bold; color: #fff; margin-bottom: 8rpx; }
|
||||
|
||||
.relation {
|
||||
font-size: 24rpx; color: rgba(255, 255, 255, 0.75);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 6rpx 22rpx; border-radius: 20rpx; margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
padding: 14rpx 50rpx;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #fff; font-size: 26rpx; border-radius: 30rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.edit-btn:active { background: rgba(255, 255, 255, 0.35); }
|
||||
|
||||
/* Info cards */
|
||||
.content { padding: 30rpx; }
|
||||
|
||||
.info-card {
|
||||
background: #ffffff; border-radius: 20rpx; padding: 24rpx 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 28rpx; font-weight: 600; color: #333;
|
||||
padding-bottom: 20rpx; border-bottom: 1rpx solid #f0f0f0; margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex; align-items: center;
|
||||
padding: 18rpx 0;
|
||||
border-bottom: 1rpx solid #f8f8f8;
|
||||
}
|
||||
|
||||
.info-row:last-child { border-bottom: none; }
|
||||
|
||||
.info-label {
|
||||
font-size: 26rpx; color: #999; width: 140rpx; flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1; font-size: 28rpx; color: #333;
|
||||
}
|
||||
|
||||
.info-value.empty { color: #ccc; }
|
||||
|
||||
/* Empty */
|
||||
.empty-state {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
padding: 100rpx 40rpx;
|
||||
}
|
||||
|
||||
.empty-icon { font-size: 120rpx; margin-bottom: 24rpx; }
|
||||
.empty-title { font-size: 32rpx; font-weight: 600; color: #666; margin-bottom: 12rpx; }
|
||||
.empty-desc { font-size: 26rpx; color: #aaa; text-align: center; margin-bottom: 30rpx; }
|
||||
|
||||
.go-add-btn {
|
||||
padding: 22rpx 60rpx;
|
||||
background: linear-gradient(135deg, #4A90D9, #357ABD);
|
||||
color: #fff; font-size: 28rpx; font-weight: 600; border-radius: 36rpx;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
interface FamilyMember {
|
||||
avatar: string;
|
||||
name: string;
|
||||
gender: string;
|
||||
birthday: string;
|
||||
relation: string;
|
||||
bloodType: string;
|
||||
allergies: string;
|
||||
chronicDiseases: string;
|
||||
longTermMeds: string;
|
||||
notes: string;
|
||||
}
|
||||
|
||||
const MEMBER_KEY = 'family_member';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
member: {} as FamilyMember,
|
||||
},
|
||||
onLoad() {},
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
this.getTabBar().setData({ selected: 3 });
|
||||
}
|
||||
this.loadMember();
|
||||
},
|
||||
loadMember() {
|
||||
const member = wx.getStorageSync(MEMBER_KEY) || {};
|
||||
this.setData({ member });
|
||||
},
|
||||
goEdit() {
|
||||
wx.navigateTo({ url: '/pages/tab-bar/profile/edit/index' });
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
<view class="page">
|
||||
<navigation-bar title="我的" back="{{false}}" color="white" background="#357ABD" />
|
||||
|
||||
<!-- 家庭成员卡片 -->
|
||||
<view class="header">
|
||||
<view class="avatar">{{member.avatar || '👤'}}</view>
|
||||
<view class="name">{{member.name || '点击添加家庭成员'}}</view>
|
||||
<view class="relation" wx:if="{{member.relation}}">{{member.relation}}</view>
|
||||
<view class="edit-btn" bindtap="goEdit">{{member.name ? '编辑信息' : '添加信息'}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 成员详细信息 -->
|
||||
<view class="content" wx:if="{{member.name}}">
|
||||
<view class="info-card">
|
||||
<view class="info-row">
|
||||
<view class="info-label">姓名</view>
|
||||
<view class="info-value">{{member.name}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">性别</view>
|
||||
<view class="info-value">{{member.gender || '未填写'}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">出生日期</view>
|
||||
<view class="info-value">{{member.birthday || '未填写'}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">关系</view>
|
||||
<view class="info-value">{{member.relation || '未填写'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-card">
|
||||
<view class="card-title">📋 健康信息</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">血型</view>
|
||||
<view class="info-value">{{member.bloodType || '未填写'}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">过敏史</view>
|
||||
<view class="info-value {{member.allergies ? '' : 'empty'}}">{{member.allergies || '无'}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">慢性病史</view>
|
||||
<view class="info-value {{member.chronicDiseases ? '' : 'empty'}}">{{member.chronicDiseases || '无'}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">长期用药</view>
|
||||
<view class="info-value {{member.longTermMeds ? '' : 'empty'}}">{{member.longTermMeds || '无'}}</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<view class="info-label">备注</view>
|
||||
<view class="info-value {{member.notes ? '' : 'empty'}}">{{member.notes || '无'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:else>
|
||||
<view class="empty-icon">👨👩👧👦</view>
|
||||
<view class="empty-title">还未添加家庭成员</view>
|
||||
<view class="empty-desc">添加家庭成员信息,方便管理全家用药</view>
|
||||
<view class="go-add-btn" bindtap="goEdit">添加家庭成员</view>
|
||||
</view>
|
||||
</view>
|
||||
Reference in New Issue
Block a user