Browse Source

feat(reminder): 重构提醒功能支持多时间点设置

- 将单个时间点改为支持多个时间点配置
- 新增时间点的添加、删除功能
- 为每日用药提醒动态加载时间点
- 添加时间选择器模态框界面
- 实现提醒设置本地存储与读取逻辑
- 更新UI布局以适应新的时间点列表展示
- 每日用药提醒不支持手动修改时间点
- 增加用户信息加载及当前用户ID计算逻辑
mcbaiyun 1 tháng trước cách đây
mục cha
commit
db822abc97
1 tập tin đã thay đổi với 283 bổ sung19 xóa
  1. 283 19
      src/pages/patient/health/reminder.vue

+ 283 - 19
src/pages/patient/health/reminder.vue

@@ -12,11 +12,24 @@
 
     <view class="reminder-list">
       <view class="reminder-item" v-for="(reminder, index) in reminders" :key="index">
-        <view class="reminder-info">
+        <view class="reminder-header">
           <text class="reminder-title">{{ reminder.title }}</text>
-          <text class="reminder-time">{{ reminder.time }}</text>
+          <switch :checked="reminder.enabled" @change="toggleReminder(index)" />
+        </view>
+        <view class="time-list">
+          <view class="time-item" v-for="(time, timeIndex) in reminder.times" :key="timeIndex">
+            <text class="time-text">{{ time }}</text>
+            <!-- 每日用药提醒不允许删除时间点 -->
+            <view class="delete-time" v-if="reminder.title !== '每日用药提醒'" @click="deleteTime(index, timeIndex)">
+              <uni-icons type="closeempty" size="16" color="#ff4757"></uni-icons>
+            </view>
+          </view>
+          <!-- 每日用药提醒不显示添加按钮 -->
+          <view class="add-time-btn" v-if="reminder.title !== '每日用药提醒'" @click="openTimePicker(index)">
+            <uni-icons type="plusempty" size="20" color="#3742fa"></uni-icons>
+            <text class="add-time-text">添加时间点</text>
+          </view>
         </view>
-        <switch :checked="reminder.enabled" @change="toggleReminder(index)" />
       </view>
     </view>
   </view>
@@ -38,6 +51,28 @@
       </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">
+        <picker mode="selector" :range="timeOptions" @change="onTimeChange">
+          <view class="time-value">{{ timeOptions[selectedTimeIndex] }}</view>
+        </picker>
+      </view>
+
+      <view class="modal-footer">
+        <button class="btn-secondary" @click="closeTimePicker">取消</button>
+        <button class="btn-primary" @click="confirmTime">确定</button>
+      </view>
+    </view>
+  </view>
   
 </template>
 
@@ -46,19 +81,20 @@ import { ref, onMounted, onUnmounted, computed } from 'vue'
 import { onShow, onHide } from '@dcloudio/uni-app'
 
 import CustomNav from '@/components/custom-nav.vue'
+import { getMedicationListByPatientId } from '@/api/patientMedication'
 
 interface Reminder {
   title: string
-  time: string
   enabled: boolean
+  times: string[]
 }
 
 const reminders = ref<Reminder[]>([
-  { title: '喝水提醒', time: '08:00', enabled: true },
-  { title: '运动提醒', time: '18:00', enabled: false },
-  { title: '测量血压', time: '07:00', enabled: true },
-  { title: '测量血糖', time: '12:00', enabled: false },
-  { title: '服药提醒', time: '09:00', enabled: true }
+  { title: '测量血压数据', enabled: true, times: ['07:00'] },
+  { title: '测量血糖数据', enabled: false, times: ['12:00'] },
+  { title: '测量心率数据', enabled: true, times: ['18:00'] },
+  // 每日用药提醒的时间点将通过API获取,初始值为空数组
+  { title: '每日用药提醒', enabled: true, times: [] }
 ])
 
 // 模板 ID & 其他展示信息
@@ -71,6 +107,36 @@ const notificationsEnabled = ref<boolean>(false)
 // 是否显示权限引导
 const showPermissionGuide = ref<boolean>(false)
 
+// 是否显示时间选择器
+const showTimePicker = ref<boolean>(false)
+
+// 当前编辑的提醒索引
+const editingReminderIndex = ref<number>(-1)
+
+// 时间选择器相关
+const timeOptions = ref<string[]>(['07:00', '08:00', '09:00', '12:00', '13:00', '18:00', '19:00', '20:00', '21:00'])
+const selectedTimeIndex = ref<number>(0)
+
+// 用户信息
+const user = ref<{ id?: string; nickname?: string; role?: string | number }>({})
+
+// 从本地存储加载用户信息
+const loadUser = () => {
+  try {
+    const u = (uni as any).getStorageSync('user_info')
+    console.log('Loaded user_info from storage:', u)
+    if (u) {
+      user.value = u
+      console.log('User data set:', user.value)
+    }
+  } catch (e) {
+    console.error('Error loading user info:', e)
+  }
+}
+
+// 获取当前用户ID (保持为字符串以避免JavaScript Number精度问题)
+const currentUserId = computed(() => user.value.id || undefined)
+
 // 记录页面进入来源:'healthIndex' | 'subscribe' | 'unknown'
 const entrySource = ref<'healthIndex' | 'subscribe' | 'unknown'>('unknown')
 const entrySourceText = computed(() => {
@@ -85,6 +151,7 @@ const entrySourceText = computed(() => {
 })
 
 onMounted(() => {
+  loadUser()
   try {
     const val = (uni as any).getStorageSync('notificationsEnabled')
     console.log('已获取全局消息开关状态:', val)
@@ -93,6 +160,28 @@ onMounted(() => {
     // 忽略错误
   }
   
+  // 加载提醒设置(除了每日用药提醒)
+  try {
+    const savedReminders = (uni as any).getStorageSync('reminders')
+    if (savedReminders && Array.isArray(savedReminders)) {
+      // 只更新非每日用药提醒的设置
+      reminders.value = reminders.value.map(reminder => {
+        if (reminder.title === '每日用药提醒') {
+          // 保留每日用药提醒项,使用空数组作为初始时间点
+          return { ...reminder, times: [] };
+        }
+        // 查找本地存储中对应的提醒设置
+        const savedReminder = savedReminders.find((r: Reminder) => r.title === reminder.title);
+        return savedReminder ? savedReminder : reminder;
+      });
+    }
+  } catch (e) {
+    // 忽略错误
+  }
+  
+  // 加载用药时间点
+  loadMedicationTimes()
+  
   // 检查用户订阅状态
   // checkSubscriptionStatus()
   
@@ -171,6 +260,7 @@ onMounted(() => {
 // 监听页面显示/隐藏(用于检测用户将小程序/APP切到后台或再次回到前台)
 onShow(() => {
   console.log('[reminder] 页面/应用返回前台(onShow)', { entrySource: entrySource.value })
+  loadMedicationTimes()  // 加载用药时间点
   // 在页面返回前台时,检查订阅设置;如果用户关闭了通知订阅主开关或对本模板拒绝,则主动关闭本地消息开关
   try {
     if (typeof (uni as any).getSetting === 'function') {
@@ -267,8 +357,40 @@ onUnmounted(() => {
 })
 
 /**
- * 检查用户订阅状态
+ * 保存提醒设置到本地存储(不包括每日用药提醒)
+ */
+const saveReminders = () => {
+  try {
+    // 过滤掉每日用药提醒,不保存到本地存储
+    const remindersToSave = reminders.value.filter(reminder => reminder.title !== '每日用药提醒');
+    (uni as any).setStorageSync('reminders', remindersToSave);
+  } catch (e) {
+    console.error('保存提醒设置失败:', e)
+  }
+}
+
+/**
+ * 加载用药时间点
  */
+const loadMedicationTimes = async () => {
+  if (!currentUserId.value) return
+  try {
+    const res = await getMedicationListByPatientId(currentUserId.value)
+    if (res && res.data && res.data.code === 200 && Array.isArray(res.data.data)) {
+      const allTimes = res.data.data.flatMap((med: any) => med.times || []).filter((t: any) => typeof t === 'string') as string[]
+      const uniqueTimes = [...new Set(allTimes)].sort()
+      // 找到每日用药提醒的索引(假设是第四个)
+      const medicationReminderIndex = reminders.value.findIndex(r => r.title === '每日用药提醒')
+      if (medicationReminderIndex >= 0) {
+        reminders.value[medicationReminderIndex].times = uniqueTimes
+        // 不再保存到本地存储,每次都从API获取最新数据
+      }
+    }
+  } catch (e) {
+    console.error('加载用药时间点失败:', e)
+  }
+}
+
 const checkSubscriptionStatus = () => {
   // 使用 uni.getSetting 检查用户授权状态
   if (typeof (uni as any).getSetting === 'function') {
@@ -431,6 +553,61 @@ const onNotificationChange = (e: any) => {
 
 const toggleReminder = (index: number) => {
   reminders.value[index].enabled = !reminders.value[index].enabled
+  // 每日用药提醒不保存到本地存储
+  if (reminders.value[index].title !== '每日用药提醒') {
+    saveReminders()
+  }
+}
+
+/**
+ * 删除时间点
+ */
+const deleteTime = (reminderIndex: number, timeIndex: number) => {
+  // 每日用药提醒不允许删除时间点
+  if (reminders.value[reminderIndex].title === '每日用药提醒') {
+    return
+  }
+  reminders.value[reminderIndex].times.splice(timeIndex, 1)
+  saveReminders()
+}
+
+/**
+ * 打开时间选择器
+ */
+const openTimePicker = (index: number) => {
+  editingReminderIndex.value = index
+  selectedTimeIndex.value = 0
+  showTimePicker.value = true
+}
+
+/**
+ * 关闭时间选择器
+ */
+const closeTimePicker = () => {
+  showTimePicker.value = false
+  editingReminderIndex.value = -1
+}
+
+/**
+ * 时间选择器变更
+ */
+const onTimeChange = (e: any) => {
+  selectedTimeIndex.value = e.detail.value
+}
+
+/**
+ * 确认时间选择
+ */
+const confirmTime = () => {
+  if (editingReminderIndex.value >= 0) {
+    const time = timeOptions.value[selectedTimeIndex.value]
+    const times = reminders.value[editingReminderIndex.value].times
+    if (!times.includes(time)) {
+      times.push(time)
+      saveReminders()
+    }
+  }
+  closeTimePicker()
 }
 </script>
 
@@ -450,9 +627,6 @@ const toggleReminder = (index: number) => {
 }
 
 .reminder-item {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
   padding: 30rpx 40rpx;
   border-bottom: 1rpx solid #eee;
 }
@@ -461,20 +635,64 @@ const toggleReminder = (index: number) => {
   border-bottom: none;
 }
 
-.reminder-info {
-  flex: 1;
+.reminder-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
 }
 
 .reminder-title {
   font-size: 32rpx;
   color: #000000;
-  display: block;
-  margin-bottom: 10rpx;
 }
 
-.reminder-time {
+.time-list {
+  width: 100%;
+  background: #fff;
+  border-radius: 14rpx;
+  border: 1rpx solid #eee;
+  padding: 16rpx;
+  margin-bottom: 20rpx;
+}
+
+.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;
-  color: #5a5a5a;
+  margin-left: 10rpx;
 }
 
 .notification-toggle {
@@ -587,4 +805,50 @@ const toggleReminder = (index: number) => {
 .modal-button:after {
   border: none;
 }
+
+/* 时间选择器样式 */
+.time-setting {
+  display: flex;
+  align-items: center;
+  margin-top: 8rpx;
+  cursor: pointer;
+}
+
+.time-setting:active {
+  opacity: 0.7;
+}
+
+.time-picker-panel {
+  height: 400rpx;
+}
+
+.time-picker-container {
+  padding: 40rpx 24rpx;
+}
+
+.time-value {
+  font-size: 48rpx;
+  color: #333;
+  padding: 10rpx 20rpx;
+  background-color: #f5f5f5;
+  border-radius: 10rpx;
+}
+
+.btn-secondary {
+  background-color: #f5f5f5;
+  color: #666;
+  border: none;
+  padding: 20rpx 0;
+  font-size: 28rpx;
+  border-radius: 8rpx;
+}
+
+.btn-primary {
+  background-color: #07c160;
+  color: #fff;
+  border: none;
+  padding: 20rpx 0;
+  font-size: 28rpx;
+  border-radius: 8rpx;
+}
 </style>