Przeglądaj źródła

feat(health): 添加健康提醒消息通知功能

- 新增消息通知开关,支持订阅每日健康上报提醒
- 展示模板编号与模板ID信息
- 实现通知开关变更时调用 uni.requestSubscribeMessage 请求用户订阅
- 记录并持久化用户订阅状态
- 增加页面来源识别逻辑(健康首页、订阅消息、未知)
- 添加通知开关与模板信息相关样式
- 引入 onMounted 和 computed 等 Vue 组合式 API 支持功能逻辑
mcbaiyun 1 miesiąc temu
rodzic
commit
eb1e9ce045
1 zmienionych plików z 191 dodań i 1 usunięć
  1. 191 1
      src/pages/health/reminder.vue

+ 191 - 1
src/pages/health/reminder.vue

@@ -1,6 +1,15 @@
 <template>
   <CustomNav title="健康提醒" leftType="back" />
   <view class="content">
+    <view class="notification-toggle">
+      <text class="notification-label">消息通知</text>
+      <switch :checked="notificationsEnabled" @change="onNotificationChange" />
+    </view>
+    <view class="template-info">
+      <text class="tmpl-title">每日健康上报提醒</text>
+      <text class="tmpl-meta">模板编号:7536 · 模板ID:ACS7cwcbx0F0Y_YaB4GZr7rWP7BO2-7wQOtYsnUjmFI</text>
+    </view>
+
     <view class="reminder-list">
       <view class="reminder-item" v-for="(reminder, index) in reminders" :key="index">
         <view class="reminder-info">
@@ -15,7 +24,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
+import { ref, onMounted, computed } from 'vue'
 
 import CustomNav from '@/components/custom-nav.vue'
 import TabBar from '@/components/tab-bar.vue'
@@ -34,6 +43,156 @@ const reminders = ref<Reminder[]>([
   { title: '服药提醒', time: '09:00', enabled: true }
 ])
 
+// 模板 ID & 其他展示信息
+const TEMPLATE_ID = 'ACS7cwcbx0F0Y_YaB4GZr7rWP7BO2-7wQOtYsnUjmFI'
+const TEMPLATE_NO = '7536'
+
+// 全局消息开关
+const notificationsEnabled = ref<boolean>(false)
+
+// 记录页面进入来源:'healthIndex' | 'subscribe' | 'unknown'
+const entrySource = ref<'healthIndex' | 'subscribe' | 'unknown'>('unknown')
+const entrySourceText = computed(() => {
+  switch (entrySource.value) {
+    case 'healthIndex':
+      return '来自 健康首页'
+    case 'subscribe':
+      return '来自 订阅消息'
+    default:
+      return '来源 未知'
+  }
+})
+
+onMounted(() => {
+  try {
+    const val = (uni as any).getStorageSync('notificationsEnabled')
+    if (typeof val === 'boolean') notificationsEnabled.value = val
+  } catch (e) {
+    // ignore
+  }
+  
+  // 尝试判断来源:优先检查页面栈的上一个页面;若无(直接打开),则检查 launch options 的 query
+  try {
+    const pages = (getCurrentPages as any)()
+    console.log('Current pages stack:', pages)
+    
+    if (pages && pages.length >= 1) {
+      const currentPage = pages[pages.length - 1]
+      const currentRoute = currentPage?.route || currentPage?.__route || currentPage?.$page?.route
+      const currentOptions = currentPage?.options || {}
+      
+      console.log('Current page route:', currentRoute)
+      console.log('Current page options:', currentOptions)
+      
+      // 检查当前页面是否包含 from=subscribe 参数
+      if (currentOptions.from === 'subscribe') {
+        entrySource.value = 'subscribe'
+        console.log('Entry source identified as subscribe via current page options')
+      }
+      
+      // 检查当前页面是否包含 templateId 相关参数
+      if (currentOptions.templateId === TEMPLATE_ID || currentOptions.template_id === TEMPLATE_ID) {
+        entrySource.value = 'subscribe'
+        console.log('Entry source identified as subscribe via templateId in current page options')
+      }
+    }
+
+    if (pages && pages.length >= 2) {
+      const prev = pages[pages.length - 2]
+      const prevRoute = prev?.route || prev?.__route || prev?.$page?.route
+      console.log('Previous page route:', prevRoute)
+      if (prevRoute && String(prevRoute).includes('pages/health/index')) {
+        entrySource.value = 'healthIndex'
+        console.log('Entry source identified as healthIndex')
+      }
+    } else {
+      // 如果没有上一页信息,检查小程序启动参数(例如用户通过订阅消息打开)
+      if ((uni as any).getLaunchOptionsSync) {
+        const launch = (uni as any).getLaunchOptionsSync()
+        console.log('Launch options:', launch)
+        if (launch && launch.query) {
+          console.log('Query params:', launch.query)
+          // 如果你在推送消息里把 from=subscribe 作为参数传入,这里会命中
+          if (launch.query.from === 'subscribe') {
+            entrySource.value = 'subscribe'
+            console.log('Entry source identified as subscribe via from param')
+          }
+          // 有些平台会把 templateId 或其它字段放在 query 中,可据此判断
+          if (launch.query.templateId === TEMPLATE_ID || launch.query.template_id === TEMPLATE_ID) {
+            entrySource.value = 'subscribe'
+            console.log('Entry source identified as subscribe via templateId param')
+          }
+        }
+      }
+    }
+  } catch (e) {
+    console.error('Error identifying entry source:', e)
+  }
+  
+  // 显示来源提示(用于测试),延迟一点以保证 UI 已就绪
+  try {
+    setTimeout(() => {
+      console.log('Entry source:', entrySource.value, entrySourceText.value)
+    }, 250)
+  } catch (e) {
+    // ignore
+  }
+  
+  if (entrySource.value === 'unknown') {
+    console.log('Entry source defaulted to unknown')
+  }
+})
+
+/**
+ * 顶部总开关变更
+ * - 打开时:调用 uni.requestSubscribeMessage 请求用户同意订阅 TEMPLATE_ID
+ * - 如果用户同意(返回 accept),将状态置为 true 并持久化
+ * - 否则回滚并提示
+ */
+const onNotificationChange = (e: any) => {
+  const newVal = e?.detail?.value
+  if (newVal) {
+    // 请求订阅(仅在微信/小程序有效)
+    ;(uni as any).requestSubscribeMessage({
+      tmplIds: [TEMPLATE_ID],
+      success(res: any) {
+        // res 可能形如 { "ACS7...": 'accept' }
+        const result = res && res[TEMPLATE_ID]
+        if (result === 'accept') {
+          notificationsEnabled.value = true
+          try {
+            ;(uni as any).setStorageSync('notificationsEnabled', true)
+          } catch (err) {
+            // ignore storage error
+          }
+          uni.showToast({ title: '订阅成功', icon: 'success' })
+        } else {
+          // 用户拒绝或关闭了弹窗
+          notificationsEnabled.value = false
+          try {
+            ;(uni as any).setStorageSync('notificationsEnabled', false)
+          } catch (err) {}
+          uni.showToast({ title: '订阅被拒绝', icon: 'none' })
+        }
+      },
+      fail(err: any) {
+        notificationsEnabled.value = false
+        try {
+          ;(uni as any).setStorageSync('notificationsEnabled', false)
+        } catch (e) {}
+        uni.showToast({ title: '订阅请求失败', icon: 'none' })
+      }
+    })
+  } else {
+    // 关闭订阅:不需要额外调用接口,只改变本地记录
+    notificationsEnabled.value = false
+    try {
+      ;(uni as any).setStorageSync('notificationsEnabled', false)
+    } catch (err) {}
+    uni.showToast({ title: '已关闭通知', icon: 'none' })
+  }
+}
+
 const toggleReminder = (index: number) => {
   reminders.value[index].enabled = !reminders.value[index].enabled
 }
@@ -81,4 +240,35 @@ const toggleReminder = (index: number) => {
   font-size: 28rpx;
   color: #5a5a5a;
 }
+
+.notification-toggle {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 30rpx 40rpx;
+  background-color: #fff;
+  border-radius: 12rpx;
+  margin-top: 20rpx;
+}
+.notification-label {
+  font-size: 32rpx;
+  color: #000;
+}
+.template-info {
+  margin-top: 12rpx;
+  padding: 0 10rpx;
+  color: #666;
+  font-size: 24rpx;
+}
+.tmpl-title {
+  display: block;
+  font-size: 26rpx;
+  color: #333;
+}
+.tmpl-meta {
+  display: block;
+  font-size: 22rpx;
+  color: #8a8a8a;
+  margin-top: 6rpx;
+}
 </style>