Files
everything-is-suitable/everything-is-suitable-uniapp/src/pages/push-subscription/index.vue
T
zhangxiang f4c1bd957c feat(miniapp): 实现订阅管理页面与运势页订阅入口
- 新增订阅管理页面:订阅开关、推送时间设置、模板管理
- 运势主页添加订阅入口区域
- 更新页面路由配置
- 补充运势页相关测试
2026-08-13 08:06:19 +08:00

263 lines
6.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="subscription-manage">
<!-- 订阅状态 -->
<view class="status-card">
<text class="status-title">订阅状态</text>
<view class="status-row">
<text class="status-label">每日运势推送</text>
<text class="status-value active">已开启</text>
</view>
</view>
<!-- 推送时间设置 -->
<view class="setting-card">
<text class="setting-title">推送时间设置</text>
<text class="setting-hint">每日在此时间推送当日运势30分钟步长</text>
<picker
mode="time"
:value="selectedTime"
:start="'06:00'"
:end="'22:00'"
@change="onTimeChange"
>
<view class="time-picker">
<text class="time-label">推送时间</text>
<text class="time-value">{{ selectedTime }}</text>
</view>
</picker>
<button
class="save-btn"
:disabled="!timeChanged"
@tap="savePushTime"
>
保存设置
</button>
</view>
<!-- 取消订阅 -->
<view class="action-card">
<button class="unsubscribe-btn" @tap="handleUnsubscribe">
取消订阅
</button>
<text class="unsubscribe-hint">取消后将不再收到每日运势推送</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const PUSH_SUB_KEY = 'eis_push_subscription'
interface PushSubscription {
pushTime: string
pushEnabled: boolean
subscribedAt: string
}
const selectedTime = ref('07:00')
const savedTime = ref('07:00')
const timeChanged = ref(false)
onShow(() => {
uni.setNavigationBarTitle({ title: '订阅管理' })
loadSubscription()
})
function loadSubscription() {
try {
const raw = uni.getStorageSync(PUSH_SUB_KEY)
if (raw) {
const sub = JSON.parse(raw) as PushSubscription
selectedTime.value = sub.pushTime || '07:00'
savedTime.value = selectedTime.value
timeChanged.value = false
}
} catch {
// 未订阅状态
}
}
function onTimeChange(e: any) {
// 将时间四舍五入到最近的 30 分钟步长(00 或 30)
const [hour, minute] = e.detail.value.split(':').map(Number)
const roundedMinute = minute < 15 ? 0 : minute < 45 ? 30 : 0
const adjustedHour = minute >= 45 ? hour + 1 : hour
const finalHour = Math.min(Math.max(adjustedHour, 6), 22)
selectedTime.value = `${String(finalHour).padStart(2, '0')}:${String(roundedMinute).padStart(2, '0')}`
timeChanged.value = selectedTime.value !== savedTime.value
}
async function savePushTime() {
try {
const result = await wx.cloud.callFunction({
name: 'updatePushTime',
data: { pushTime: selectedTime.value },
})
if (result.result.success) {
// 更新本地缓存
const raw = uni.getStorageSync(PUSH_SUB_KEY)
if (raw) {
const sub = JSON.parse(raw) as PushSubscription
sub.pushTime = selectedTime.value
uni.setStorageSync(PUSH_SUB_KEY, JSON.stringify(sub))
}
savedTime.value = selectedTime.value
timeChanged.value = false
uni.showToast({ title: '保存成功', icon: 'success' })
} else {
uni.showToast({ title: '保存失败,请重试', icon: 'none' })
}
} catch (err) {
console.error('Save push time error:', err)
uni.showToast({ title: '保存失败,请重试', icon: 'none' })
}
}
async function handleUnsubscribe() {
uni.showModal({
title: '确认取消',
content: '取消后将不再收到每日运势推送,确定要取消吗?',
success: async (res) => {
if (res.confirm) {
try {
const result = await wx.cloud.callFunction({
name: 'unsubscribe',
})
if (result.result.success) {
// 清除本地缓存
uni.removeStorageSync(PUSH_SUB_KEY)
uni.showToast({ title: '已取消订阅', icon: 'success' })
// 返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
} else {
uni.showToast({ title: '取消失败,请重试', icon: 'none' })
}
} catch (err) {
console.error('Unsubscribe error:', err)
uni.showToast({ title: '取消失败,请重试', icon: 'none' })
}
}
},
})
}
</script>
<style scoped lang="scss">
.subscription-manage {
min-height: 100vh;
background-color: rgba(244, 244, 245, 0.3);
padding: 16px;
}
.status-card,
.setting-card,
.action-card {
background-color: #fff;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
}
.status-title,
.setting-title {
font-size: 16px;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 12px;
}
.status-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.status-label {
font-size: 15px;
color: #333;
}
.status-value {
font-size: 15px;
font-weight: 600;
}
.status-value.active {
color: #52c41a;
}
.setting-hint {
font-size: 13px;
color: #999;
display: block;
margin-bottom: 16px;
}
.time-picker {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-top: 1px solid #f0f0f0;
}
.time-label {
font-size: 15px;
color: #333;
}
.time-value {
font-size: 15px;
color: #1677ff;
font-weight: 600;
}
.save-btn {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
background-color: rgba(44, 24, 16, 255);
color: #fff;
border-radius: 20px;
font-size: 15px;
margin-top: 12px;
border: none;
}
.save-btn[disabled] {
opacity: 0.5;
}
.unsubscribe-btn {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
background-color: #fff;
color: #ff4d4f;
border-radius: 20px;
font-size: 15px;
border: 1px solid #ff4d4f;
}
.unsubscribe-hint {
font-size: 12px;
color: #999;
display: block;
text-align: center;
margin-top: 8px;
}
</style>