|
|
@@ -0,0 +1,1003 @@
|
|
|
+<template>
|
|
|
+ <CustomNav title="用药管理" leftType="back" />
|
|
|
+ <view class="page-container">
|
|
|
+ <view class="medication-list" v-if="medications.length > 0">
|
|
|
+ <view
|
|
|
+ class="medication-card"
|
|
|
+ v-for="medication in medications"
|
|
|
+ :key="medication.id"
|
|
|
+ >
|
|
|
+ <view class="card-header">
|
|
|
+ <view class="medication-info">
|
|
|
+ <text class="medication-name">{{ medication.name }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-body">
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">剂量:</text>
|
|
|
+ <text class="value">{{ medication.dosage }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">频率:</text>
|
|
|
+ <text class="value">{{ medication.frequency }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">时间:</text>
|
|
|
+ <text class="value">{{ medication.time }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="label">备注:</text>
|
|
|
+ <text class="value">{{ medication.note || '无' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-footer">
|
|
|
+ <button class="action-btn edit" @click="editMedication(medication.id)">编辑</button>
|
|
|
+ <button class="action-btn delete" @click="deleteMedication(medication.id)">删除</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="empty-state" v-else>
|
|
|
+ <image class="empty-icon" src="/static/icons/remixicon/capsule-fill.svg" />
|
|
|
+ <text class="empty-text">暂无用药记录</text>
|
|
|
+ <text class="empty-hint">点击右下角 + 添加</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="fab-container">
|
|
|
+ <view class="scan-fab" @click="scanMedication">
|
|
|
+ <view class="fab-inner">
|
|
|
+ <uni-icons type="scan" size="24" color="#fff"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="fab" @click="openAdd">
|
|
|
+ <view class="fab-inner">+</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="modal" v-if="showAdd">
|
|
|
+ <view class="modal-backdrop" @click="closeAdd"></view>
|
|
|
+ <view class="modal-panel add-medication-modal">
|
|
|
+ <view class="drag-handle"></view>
|
|
|
+ <view class="modal-header">
|
|
|
+ <text class="modal-title">添加用药</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="modal-inner">
|
|
|
+ <view class="form-group">
|
|
|
+ <view class="form-row">
|
|
|
+ <text class="form-label">药品名称</text>
|
|
|
+ <view class="medicine-selector" @click="showMedicinePicker = true">
|
|
|
+ <text class="selector-text" :class="{ placeholder: !selectedMedicine }">{{ selectedMedicine ? selectedMedicine.name : '请选择药品' }}</text>
|
|
|
+ <uni-icons type="arrowdown" size="16" color="#999" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-row">
|
|
|
+ <text class="form-label">剂量规格</text>
|
|
|
+ <input type="text" v-model="addDosage" class="form-input" placeholder="例如:0.85g" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-row">
|
|
|
+ <text class="form-label">用法用量</text>
|
|
|
+ <view class="dosage-selector">
|
|
|
+ <view class="dosage-row">
|
|
|
+ <text class="dosage-label">每日</text>
|
|
|
+ <input type="number" v-model.number="dailyTimes" class="small-input" />
|
|
|
+ <text class="dosage-label">次,每次</text>
|
|
|
+ <input type="number" v-model.number="eachTime" class="small-input" />
|
|
|
+ <text class="dosage-label">粒</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-row">
|
|
|
+ <text class="form-label">服用时间</text>
|
|
|
+ <view class="time-list">
|
|
|
+ <view
|
|
|
+ class="time-item"
|
|
|
+ v-for="(time, index) in addTimes"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <text class="time-text">{{ time }}</text>
|
|
|
+ <view class="delete-time" @click="removeTime(index)">
|
|
|
+ <uni-icons type="closeempty" size="16" color="#ff4757"></uni-icons>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="add-time-btn" @click="showTimePicker = true">
|
|
|
+ <uni-icons type="plusempty" size="20" color="#3742fa"></uni-icons>
|
|
|
+ <text class="add-time-text">添加时间点</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="form-row">
|
|
|
+ <text class="form-label">备注</text>
|
|
|
+ <input type="text" v-model="addNote" class="form-input" placeholder="可选备注信息" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="modal-footer">
|
|
|
+ <button class="btn-secondary" @click="closeAdd">取消</button>
|
|
|
+ <button class="btn-primary" @click="confirmAdd">保存</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 药品选择器模态框 -->
|
|
|
+ <view class="modal" v-if="showMedicinePicker">
|
|
|
+ <view class="modal-backdrop" @click="closeMedicinePicker"></view>
|
|
|
+ <view class="modal-panel medicine-picker-panel">
|
|
|
+ <view class="drag-handle"></view>
|
|
|
+ <view class="modal-header">
|
|
|
+ <text class="modal-title">选择药品</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="search-container">
|
|
|
+ <input type="text" v-model="medicineSearch" class="search-input" placeholder="搜索药品(支持拼音首字母)" @input="onMedicineSearch" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <scroll-view class="medicine-list" scroll-y="true">
|
|
|
+ <view
|
|
|
+ class="medicine-item"
|
|
|
+ v-for="medicine in filteredMedicines"
|
|
|
+ :key="medicine.id"
|
|
|
+ @click="selectMedicine(medicine)"
|
|
|
+ >
|
|
|
+ <text class="medicine-name">{{ medicine.name }}</text>
|
|
|
+ <text class="medicine-pinyin">{{ medicine.pinyin }}</text>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 时间选择器模态框 -->
|
|
|
+ <view class="modal" v-if="showTimePicker">
|
|
|
+ <view class="modal-backdrop" @click="closeTimePicker"></view>
|
|
|
+ <view class="modal-panel time-picker-panel">
|
|
|
+ <view class="drag-handle"></view>
|
|
|
+ <view class="modal-header">
|
|
|
+ <text class="modal-title">选择时间</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="time-picker-container">
|
|
|
+ <view class="time-picker-row">
|
|
|
+ <view class="time-unit">
|
|
|
+ <text class="time-unit-label">时</text>
|
|
|
+ <picker mode="selector" :range="hoursRange" @change="onHourChange">
|
|
|
+ <view class="time-value">{{ selectedHour }}</view>
|
|
|
+ </picker>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="time-unit">
|
|
|
+ <text class="time-unit-label">分</text>
|
|
|
+ <picker mode="selector" :range="minutesRange" @change="onMinuteChange">
|
|
|
+ <view class="time-value">{{ selectedMinute }}</view>
|
|
|
+ </picker>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="modal-footer">
|
|
|
+ <button class="btn-secondary" @click="closeTimePicker">取消</button>
|
|
|
+ <button class="btn-primary" @click="confirmTime">确定</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, computed, onMounted, watch } from 'vue'
|
|
|
+import CustomNav from '@/components/custom-nav.vue'
|
|
|
+
|
|
|
+interface Medication {
|
|
|
+ id: string
|
|
|
+ name: string
|
|
|
+ dosage: string
|
|
|
+ frequency: string
|
|
|
+ time: string
|
|
|
+ note?: string
|
|
|
+}
|
|
|
+
|
|
|
+interface Medicine {
|
|
|
+ id: string
|
|
|
+ name: string
|
|
|
+ pinyin: string
|
|
|
+}
|
|
|
+
|
|
|
+const medications = ref<Medication[]>([
|
|
|
+ {
|
|
|
+ id: '1',
|
|
|
+ name: '阿司匹林',
|
|
|
+ dosage: '100mg',
|
|
|
+ frequency: '每日1次,每次1粒',
|
|
|
+ time: '08:00',
|
|
|
+ note: '饭后服用'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: '2',
|
|
|
+ name: '硝苯地平',
|
|
|
+ dosage: '10mg',
|
|
|
+ frequency: '每日3次,每次1粒',
|
|
|
+ time: '07:00, 15:00, 21:00'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+// 模态框控制
|
|
|
+const showAdd = ref(false)
|
|
|
+const showMedicinePicker = ref(false)
|
|
|
+
|
|
|
+// 添加用药表单数据
|
|
|
+const selectedMedicine = ref<Medicine | null>(null)
|
|
|
+const addDosage = ref('')
|
|
|
+const dailyTimes = ref(1)
|
|
|
+const eachTime = ref(1)
|
|
|
+const addTimes = ref<string[]>([])
|
|
|
+const addNote = ref('')
|
|
|
+
|
|
|
+// 预设时间配置
|
|
|
+const presetTimes: Record<number, string[]> = {
|
|
|
+ 1: ['08:00'],
|
|
|
+ 2: ['08:00', '20:00'],
|
|
|
+ 3: ['07:00', '15:00', '21:00']
|
|
|
+}
|
|
|
+
|
|
|
+// 时间选择器相关
|
|
|
+const showTimePicker = ref(false)
|
|
|
+const selectedHour = ref('08')
|
|
|
+const selectedMinute = ref('00')
|
|
|
+const hoursRange = Array.from({length: 24}, (_, i) => i.toString().padStart(2, '0'))
|
|
|
+const minutesRange = ['00', '30']
|
|
|
+
|
|
|
+// 药品搜索
|
|
|
+const medicineSearch = ref('')
|
|
|
+const allMedicines = ref<Medicine[]>([
|
|
|
+ { id: '1', name: '阿司匹林', pinyin: 'aspirin' },
|
|
|
+ { id: '2', name: '硝苯地平', pinyin: 'xiaobendiping' },
|
|
|
+ { id: '3', name: '盐酸二甲双胍片', pinyin: 'yansuanerjiashuanggua' },
|
|
|
+ { id: '4', name: '格列美脲', pinyin: 'gelimeiwo' },
|
|
|
+ { id: '5', name: '瑞格列奈', pinyin: 'ruigelienai' },
|
|
|
+ { id: '6', name: '胰岛素', pinyin: 'yidaosu' },
|
|
|
+ { id: '7', name: '美托洛尔', pinyin: 'meituoluoer' },
|
|
|
+ { id: '8', name: '氨氯地平', pinyin: 'anlviping' },
|
|
|
+ { id: '9', name: '辛伐他汀', pinyin: 'xinfatating' },
|
|
|
+ { id: '10', name: '阿托伐他汀', pinyin: 'atuofatating' }
|
|
|
+])
|
|
|
+
|
|
|
+// 过滤后的药品列表
|
|
|
+const filteredMedicines = computed(() => {
|
|
|
+ if (!medicineSearch.value.trim()) {
|
|
|
+ return allMedicines.value
|
|
|
+ }
|
|
|
+ const search = medicineSearch.value.toLowerCase()
|
|
|
+ return allMedicines.value.filter(medicine =>
|
|
|
+ medicine.name.includes(search) ||
|
|
|
+ medicine.pinyin.toLowerCase().includes(search) ||
|
|
|
+ medicine.pinyin.toLowerCase().startsWith(search)
|
|
|
+ )
|
|
|
+})
|
|
|
+
|
|
|
+// 获取状态文本
|
|
|
+// const getStatusText = (status: string) => {
|
|
|
+// switch (status) {
|
|
|
+// case 'active': return '进行中'
|
|
|
+// case 'inactive': return '已停用'
|
|
|
+// default: return status
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// 打开添加模态框
|
|
|
+const openAdd = () => {
|
|
|
+ showAdd.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 打开时间选择器
|
|
|
+const openTimePicker = () => {
|
|
|
+ showTimePicker.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭时间选择器
|
|
|
+const closeTimePicker = () => {
|
|
|
+ showTimePicker.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 小时改变
|
|
|
+const onHourChange = (e: any) => {
|
|
|
+ selectedHour.value = hoursRange[e.detail.value]
|
|
|
+}
|
|
|
+
|
|
|
+// 分钟改变
|
|
|
+const onMinuteChange = (e: any) => {
|
|
|
+ selectedMinute.value = minutesRange[e.detail.value]
|
|
|
+}
|
|
|
+
|
|
|
+// 确认选择时间
|
|
|
+const confirmTime = () => {
|
|
|
+ const time = `${selectedHour.value}:${selectedMinute.value}`
|
|
|
+ // 检查是否已存在该时间点
|
|
|
+ if (!addTimes.value.includes(time)) {
|
|
|
+ addTimes.value.push(time)
|
|
|
+ // 按时间排序
|
|
|
+ addTimes.value.sort()
|
|
|
+ }
|
|
|
+ closeTimePicker()
|
|
|
+}
|
|
|
+
|
|
|
+// 删除时间点
|
|
|
+const removeTime = (index: number) => {
|
|
|
+ addTimes.value.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭添加模态框
|
|
|
+const closeAdd = () => {
|
|
|
+ showAdd.value = false
|
|
|
+ // 重置表单
|
|
|
+ selectedMedicine.value = null
|
|
|
+ addDosage.value = ''
|
|
|
+ dailyTimes.value = 1
|
|
|
+ eachTime.value = 1
|
|
|
+ addTimes.value = []
|
|
|
+ addNote.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+// 打开药品选择器
|
|
|
+const openMedicinePicker = () => {
|
|
|
+ showMedicinePicker.value = true
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭药品选择器
|
|
|
+const closeMedicinePicker = () => {
|
|
|
+ showMedicinePicker.value = false
|
|
|
+ medicineSearch.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+// 药品搜索
|
|
|
+const onMedicineSearch = () => {
|
|
|
+ // 搜索逻辑在 computed 中处理
|
|
|
+}
|
|
|
+
|
|
|
+// 选择药品
|
|
|
+const selectMedicine = (medicine: Medicine) => {
|
|
|
+ selectedMedicine.value = medicine
|
|
|
+ closeMedicinePicker()
|
|
|
+}
|
|
|
+
|
|
|
+// 确认添加用药
|
|
|
+const confirmAdd = () => {
|
|
|
+ if (!selectedMedicine.value) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请选择药品',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!addDosage.value.trim()) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请输入剂量规格',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (addTimes.value.length === 0) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '请添加至少一个服用时间',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const frequency = `每日${dailyTimes.value}次,每次${eachTime.value}粒`
|
|
|
+ const timeStr = addTimes.value.join(', ')
|
|
|
+
|
|
|
+ const newMedication: Medication = {
|
|
|
+ id: Date.now().toString(),
|
|
|
+ name: selectedMedicine.value.name,
|
|
|
+ dosage: addDosage.value.trim(),
|
|
|
+ frequency: frequency,
|
|
|
+ time: timeStr,
|
|
|
+ note: addNote.value.trim() || undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ medications.value.unshift(newMedication)
|
|
|
+ uni.showToast({
|
|
|
+ title: '添加成功',
|
|
|
+ icon: 'success'
|
|
|
+ })
|
|
|
+
|
|
|
+ closeAdd()
|
|
|
+}
|
|
|
+
|
|
|
+// 扫码录入药品
|
|
|
+const scanMedication = () => {
|
|
|
+ uni.showToast({
|
|
|
+ title: '功能开发中',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 编辑用药
|
|
|
+const editMedication = (id: string) => {
|
|
|
+ const medication = medications.value.find(m => m.id === id)
|
|
|
+ if (medication) {
|
|
|
+ // 加载选中药物的信息到编辑表单
|
|
|
+ selectedMedicine.value = {
|
|
|
+ id: '',
|
|
|
+ name: medication.name,
|
|
|
+ pinyin: ''
|
|
|
+ }
|
|
|
+
|
|
|
+ addDosage.value = medication.dosage
|
|
|
+
|
|
|
+ // 解析频率信息
|
|
|
+ const frequencyMatch = medication.frequency.match(/每日(\d+)次,每次(\d+)粒/)
|
|
|
+ if (frequencyMatch) {
|
|
|
+ dailyTimes.value = parseInt(frequencyMatch[1])
|
|
|
+ eachTime.value = parseInt(frequencyMatch[2])
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析时间信息
|
|
|
+ addTimes.value = medication.time.split(', ').filter(t => t.trim() !== '')
|
|
|
+
|
|
|
+ // 加载备注
|
|
|
+ addNote.value = medication.note || ''
|
|
|
+
|
|
|
+ // 显示编辑模态框
|
|
|
+ showAdd.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 删除用药
|
|
|
+const deleteMedication = (id: string) => {
|
|
|
+ uni.showModal({
|
|
|
+ title: '确认删除',
|
|
|
+ content: '确定要删除这个用药记录吗?',
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ const index = medications.value.findIndex(m => m.id === id)
|
|
|
+ if (index !== -1) {
|
|
|
+ medications.value.splice(index, 1)
|
|
|
+ uni.showToast({ title: '删除成功', icon: 'success' })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 监听每日次数变化,自动设置预设时间
|
|
|
+watch(dailyTimes, (newVal) => {
|
|
|
+ if (presetTimes[newVal]) {
|
|
|
+ addTimes.value = [...presetTimes[newVal]]
|
|
|
+ } else {
|
|
|
+ // 如果没有预设,则清空时间列表
|
|
|
+ addTimes.value = []
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // 这里可以调用API获取用药数据
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.page-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ padding-top: calc(var(--status-bar-height) + 44px);
|
|
|
+ padding-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.add-button-container {
|
|
|
+ padding: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.add-btn {
|
|
|
+ width: 100%;
|
|
|
+ height: 80rpx;
|
|
|
+ background-color: #3742fa;
|
|
|
+ color: #fff;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 32rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.add-btn uni-icons {
|
|
|
+ margin-right: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medication-list {
|
|
|
+ padding: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medication-card {
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 30rpx;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.medication-info {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.medication-name {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medication-time {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge {
|
|
|
+ padding: 8rpx 16rpx;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge.active {
|
|
|
+ background-color: #2ed573;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge.inactive {
|
|
|
+ background-color: #ccc;
|
|
|
+}
|
|
|
+
|
|
|
+.card-body {
|
|
|
+ padding: 30rpx;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.label {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+ width: 120rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.value {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.card-footer {
|
|
|
+ display: flex;
|
|
|
+ padding: 30rpx;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn {
|
|
|
+ padding: 0 30rpx;
|
|
|
+ height: 60rpx;
|
|
|
+ line-height: 60rpx;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ margin-left: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.edit {
|
|
|
+ background-color: #3742fa;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.delete {
|
|
|
+ background-color: #fff;
|
|
|
+ color: #ff4757;
|
|
|
+ border: 1rpx solid #ff4757;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-state {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 100rpx 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-icon {
|
|
|
+ width: 120rpx;
|
|
|
+ height: 120rpx;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-text {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-hint {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.scan-fab {
|
|
|
+ position: fixed;
|
|
|
+ right: 28rpx;
|
|
|
+ bottom: 240rpx;
|
|
|
+ width: 110rpx;
|
|
|
+ height: 110rpx;
|
|
|
+ border-radius: 999px;
|
|
|
+ background: linear-gradient(180deg, #00b4d8, #0077b6);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ box-shadow: 0 6rpx 18rpx rgba(0, 0, 0, 0.2);
|
|
|
+ z-index: 1200
|
|
|
+}
|
|
|
+
|
|
|
+.fab {
|
|
|
+ position: fixed;
|
|
|
+ right: 28rpx;
|
|
|
+ bottom: 100rpx;
|
|
|
+ width: 110rpx;
|
|
|
+ height: 110rpx;
|
|
|
+ border-radius: 999px;
|
|
|
+ background: linear-gradient(180deg, #ff7a00, #ff4a00);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ box-shadow: 0 6rpx 18rpx rgba(0, 0, 0, 0.2);
|
|
|
+ z-index: 1200
|
|
|
+}
|
|
|
+
|
|
|
+.fab-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.fab-inner {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 56rpx;
|
|
|
+ line-height: 56rpx
|
|
|
+}
|
|
|
+
|
|
|
+.modal {
|
|
|
+ position: fixed;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ top: 0;
|
|
|
+ bottom: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-end;
|
|
|
+ justify-content: center;
|
|
|
+ z-index: 1300
|
|
|
+}
|
|
|
+
|
|
|
+.modal-backdrop {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ top: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(0, 0, 0, 0.4)
|
|
|
+}
|
|
|
+
|
|
|
+.modal-panel {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ background: #fff;
|
|
|
+ border-top-left-radius: 18rpx;
|
|
|
+ border-top-right-radius: 18rpx;
|
|
|
+ padding: 28rpx 24rpx 40rpx 24rpx;
|
|
|
+ box-shadow: 0 -8rpx 30rpx rgba(0,0,0,0.12)
|
|
|
+}
|
|
|
+
|
|
|
+.add-medication-modal {
|
|
|
+ padding-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.drag-handle {
|
|
|
+ width: 64rpx;
|
|
|
+ height: 6rpx;
|
|
|
+ background: rgba(0,0,0,0.08);
|
|
|
+ border-radius: 999px;
|
|
|
+ margin: 10rpx auto 14rpx auto
|
|
|
+}
|
|
|
+
|
|
|
+.modal-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 12rpx;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.modal-title {
|
|
|
+ font-size: 36rpx;
|
|
|
+ color: #222;
|
|
|
+ font-weight: 700;
|
|
|
+ letter-spacing: 1rpx
|
|
|
+}
|
|
|
+
|
|
|
+.modal-inner {
|
|
|
+ max-width: 100%;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 0 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.form-group {
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.form-row {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.form-row:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.form-label {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 12rpx;
|
|
|
+ align-self: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.form-input {
|
|
|
+ width: 100%;
|
|
|
+ text-align: left;
|
|
|
+ padding: 16rpx;
|
|
|
+ border-radius: 14rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ background: #fff;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.picker-display {
|
|
|
+ color: #333;
|
|
|
+ padding: 16rpx;
|
|
|
+ border-radius: 14rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ background: #fff7f0;
|
|
|
+ min-width: 200rpx;
|
|
|
+ text-align: left
|
|
|
+}
|
|
|
+
|
|
|
+.btn-primary {
|
|
|
+ background: #ff6a00;
|
|
|
+ color: #fff;
|
|
|
+ padding: 18rpx 22rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ text-align: center;
|
|
|
+ flex: 1;
|
|
|
+ margin: 0 10rpx;
|
|
|
+ box-shadow: 0 10rpx 28rpx rgba(255,106,0,0.18);
|
|
|
+ font-size: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-secondary {
|
|
|
+ background: #f0f0f0;
|
|
|
+ color: #666;
|
|
|
+ padding: 18rpx 22rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ text-align: center;
|
|
|
+ flex: 1;
|
|
|
+ margin: 0 10rpx;
|
|
|
+ font-size: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.modal-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ margin-top: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.label {
|
|
|
+ color: #666
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-selector {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 16rpx;
|
|
|
+ border-radius: 14rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ background: #fff;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.selector-text {
|
|
|
+ color: #333;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.selector-text.placeholder {
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.dosage-selector {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.dosage-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16rpx;
|
|
|
+ padding: 16rpx;
|
|
|
+ border-radius: 14rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.dosage-label {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.small-input {
|
|
|
+ width: 80rpx;
|
|
|
+ text-align: center;
|
|
|
+ padding: 8rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ background: #fff;
|
|
|
+ margin: 0 4rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-picker-panel {
|
|
|
+ height: 600rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.search-container {
|
|
|
+ padding: 0 24rpx 20rpx 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.search-input {
|
|
|
+ width: 100%;
|
|
|
+ padding: 16rpx;
|
|
|
+ border-radius: 14rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ background: #fff7f0;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-list {
|
|
|
+ max-height: 400rpx;
|
|
|
+ padding: 0 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-item {
|
|
|
+ padding: 20rpx 16rpx;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-name {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.medicine-pinyin {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.time-list {
|
|
|
+ width: 100%;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 14rpx;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+ padding: 16rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12rpx 16rpx;
|
|
|
+ background-color: #f0f5ff;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ margin-bottom: 12rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-item:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.time-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.delete-time {
|
|
|
+ padding: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.add-time-btn {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 20rpx;
|
|
|
+ border: 1rpx dashed #3742fa;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ margin-top: 12rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.add-time-text {
|
|
|
+ color: #3742fa;
|
|
|
+ font-size: 28rpx;
|
|
|
+ margin-left: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-picker-panel {
|
|
|
+ height: 400rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-picker-container {
|
|
|
+ padding: 40rpx 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-picker-row {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ gap: 60rpx;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-unit {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.time-unit-label {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.time-value {
|
|
|
+ font-size: 48rpx;
|
|
|
+ color: #333;
|
|
|
+ padding: 10rpx 20rpx;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ border-radius: 10rpx;
|
|
|
+}
|
|
|
+</style>
|