pet/pages/profile/notifications.vue

816 lines
18 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="notifications-container page-container-with-bg">
<!-- 通知设置卡片 -->
<view class="settings-card">
<view class="card-header">
<text class="card-title">通知设置</text>
</view>
<view class="settings-list">
<view class="setting-item">
<view class="setting-info">
<text class="setting-title">系统通知</text>
<text class="setting-desc">接收系统重要消息和更新通知</text>
</view>
<view class="setting-switch">
<u-switch
v-model="notificationSettings.system"
@change="onSettingChange('system', $event)"
active-color="#FF8A80"
></u-switch>
</view>
</view>
<view class="setting-item">
<view class="setting-info">
<text class="setting-title">宠物提醒</text>
<text class="setting-desc">宠物喂食、医疗等重要提醒</text>
</view>
<view class="setting-switch">
<u-switch
v-model="notificationSettings.petReminder"
@change="onSettingChange('petReminder', $event)"
active-color="#FF8A80"
></u-switch>
</view>
</view>
<view class="setting-item">
<view class="setting-info">
<text class="setting-title">领养消息</text>
<text class="setting-desc">领养申请、回复等相关消息</text>
</view>
<view class="setting-switch">
<u-switch
v-model="notificationSettings.adoption"
@change="onSettingChange('adoption', $event)"
active-color="#FF8A80"
></u-switch>
</view>
</view>
<view class="setting-item">
<view class="setting-info">
<text class="setting-title">家庭动态</text>
<text class="setting-desc">家庭成员活动和共享内容通知</text>
</view>
<view class="setting-switch">
<u-switch
v-model="notificationSettings.family"
@change="onSettingChange('family', $event)"
active-color="#FF8A80"
></u-switch>
</view>
</view>
<view class="setting-item">
<view class="setting-info">
<text class="setting-title">营销推广</text>
<text class="setting-desc">产品推荐、活动优惠等信息</text>
</view>
<view class="setting-switch">
<u-switch
v-model="notificationSettings.marketing"
@change="onSettingChange('marketing', $event)"
active-color="#FF8A80"
></u-switch>
</view>
</view>
</view>
</view>
<!-- 免打扰设置卡片 -->
<view class="dnd-card">
<view class="card-header">
<text class="card-title">免打扰设置</text>
</view>
<view class="dnd-content">
<view class="dnd-toggle">
<view class="toggle-info">
<text class="toggle-title">开启免打扰</text>
<text class="toggle-desc">在指定时间段内不接收通知</text>
</view>
<view class="toggle-switch">
<u-switch
v-model="dndSettings.enabled"
@change="onDndToggle"
active-color="#FF8A80"
></u-switch>
</view>
</view>
<view class="dnd-time" v-if="dndSettings.enabled">
<view class="time-item" @click="showStartTimePicker">
<text class="time-label">开始时间</text>
<text class="time-value">{{ dndSettings.startTime }}</text>
</view>
<view class="time-item" @click="showEndTimePicker">
<text class="time-label">结束时间</text>
<text class="time-value">{{ dndSettings.endTime }}</text>
</view>
</view>
</view>
</view>
<!-- 消息列表卡片 -->
<view class="messages-card">
<view class="card-header">
<text class="card-title">最近消息</text>
<view class="header-action" @click="markAllRead" v-if="unreadCount > 0">
<text class="action-text">全部已读</text>
</view>
</view>
<view class="messages-list" v-if="messagesList.length > 0">
<view
class="message-item"
v-for="message in messagesList"
:key="message.id"
:class="{ unread: !message.read }"
@click="readMessage(message)"
>
<view class="message-icon" :class="message.type">
<text class="icon-text">{{ getMessageIcon(message.type) }}</text>
</view>
<view class="message-content">
<view class="message-title">{{ message.title }}</view>
<view class="message-desc">{{ message.content }}</view>
<view class="message-time">{{ formatTime(message.time) }}</view>
</view>
<view class="message-status" v-if="!message.read">
<view class="unread-dot"></view>
</view>
</view>
</view>
<view class="empty-messages" v-else>
<view class="empty-icon">📬</view>
<view class="empty-text">暂无消息</view>
</view>
</view>
<!-- 清理设置卡片 -->
<view class="cleanup-card">
<view class="card-header">
<text class="card-title">消息管理</text>
</view>
<view class="cleanup-list">
<view class="cleanup-item" @click="clearReadMessages">
<view class="cleanup-info">
<text class="cleanup-title">清理已读消息</text>
<text class="cleanup-desc">删除所有已读的消息记录</text>
</view>
<view class="cleanup-action">
<text class="action-arrow">→</text>
</view>
</view>
<view class="cleanup-item" @click="clearAllMessages">
<view class="cleanup-info">
<text class="cleanup-title">清空所有消息</text>
<text class="cleanup-desc">删除所有消息记录(不可恢复)</text>
</view>
<view class="cleanup-action">
<text class="action-arrow"></text>
</view>
</view>
</view>
</view>
<!-- 时间选择器 -->
<u-datetime-picker
:show="showStartTime"
v-model="selectedStartTime"
mode="time"
@confirm="onStartTimeConfirm"
@cancel="showStartTime = false"
></u-datetime-picker>
<u-datetime-picker
:show="showEndTime"
v-model="selectedEndTime"
mode="time"
@confirm="onEndTimeConfirm"
@cancel="showEndTime = false"
></u-datetime-picker>
</view>
</template>
<script>
import { reactive, ref, onMounted, computed } from 'vue'
export default {
name: 'NotificationsPage',
setup() {
// 响应式数据
const notificationSettings = reactive({
system: true,
petReminder: true,
adoption: true,
family: true,
marketing: false
})
const dndSettings = reactive({
enabled: false,
startTime: '22:00',
endTime: '08:00'
})
const messagesList = ref([])
const showStartTime = ref(false)
const showEndTime = ref(false)
const selectedStartTime = ref(Date.now())
const selectedEndTime = ref(Date.now())
// 计算属性
const unreadCount = computed(() => {
return messagesList.value.filter(msg => !msg.read).length
})
// 生命周期
onMounted(() => {
loadSettings()
loadMessages()
})
// 方法定义
const loadSettings = () => {
try {
const savedSettings = uni.getStorageSync('notificationSettings')
if (savedSettings) {
Object.assign(notificationSettings, savedSettings)
}
const savedDndSettings = uni.getStorageSync('dndSettings')
if (savedDndSettings) {
Object.assign(dndSettings, savedDndSettings)
}
} catch (error) {
console.error('加载设置失败:', error)
}
}
const loadMessages = () => {
try {
const savedMessages = uni.getStorageSync('notifications') || []
// 如果没有消息,创建一些模拟消息
if (savedMessages.length === 0) {
const mockMessages = [
{
id: 'msg_1',
type: 'system',
title: '系统更新',
content: '宠物AI助手已更新到最新版本新增了家庭共享功能。',
time: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),
read: false
},
{
id: 'msg_2',
type: 'pet',
title: '喂食提醒',
content: '小橘的晚餐时间到了,记得给它准备食物哦!',
time: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(),
read: true
},
{
id: 'msg_3',
type: 'adoption',
title: '领养申请',
content: '有用户申请领养您发布的小白,请及时查看处理。',
time: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(),
read: false
},
{
id: 'msg_4',
type: 'family',
title: '家庭动态',
content: '家庭成员小明为小花添加了一条健康记录。',
time: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
read: true
}
]
uni.setStorageSync('notifications', mockMessages)
messagesList.value = mockMessages
} else {
messagesList.value = savedMessages
}
} catch (error) {
console.error('加载消息失败:', error)
}
}
const saveSettings = () => {
uni.setStorageSync('notificationSettings', notificationSettings)
uni.setStorageSync('dndSettings', dndSettings)
}
const saveMessages = () => {
uni.setStorageSync('notifications', messagesList.value)
}
const onSettingChange = (key, value) => {
notificationSettings[key] = value
saveSettings()
uni.showToast({
title: value ? '已开启' : '已关闭',
icon: 'success'
})
}
const onDndToggle = (value) => {
dndSettings.enabled = value
saveSettings()
if (value) {
uni.showToast({
title: '免打扰已开启',
icon: 'success'
})
} else {
uni.showToast({
title: '免打扰已关闭',
icon: 'success'
})
}
}
const showStartTimePicker = () => {
const [hours, minutes] = dndSettings.startTime.split(':')
const date = new Date()
date.setHours(parseInt(hours), parseInt(minutes), 0, 0)
selectedStartTime.value = date.getTime()
showStartTime.value = true
}
const showEndTimePicker = () => {
const [hours, minutes] = dndSettings.endTime.split(':')
const date = new Date()
date.setHours(parseInt(hours), parseInt(minutes), 0, 0)
selectedEndTime.value = date.getTime()
showEndTime.value = true
}
const onStartTimeConfirm = (e) => {
const date = new Date(e.value)
dndSettings.startTime = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
saveSettings()
showStartTime.value = false
}
const onEndTimeConfirm = (e) => {
const date = new Date(e.value)
dndSettings.endTime = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
saveSettings()
showEndTime.value = false
}
const getMessageIcon = (type) => {
const iconMap = {
'system': '🔔',
'pet': '🐱',
'adoption': '🏠',
'family': '👨‍👩‍👧‍👦',
'marketing': '📢'
}
return iconMap[type] || '📬'
}
const formatTime = (timeString) => {
if (!timeString) return ''
const date = new Date(timeString)
const now = new Date()
const diffTime = now - date
const diffHours = Math.floor(diffTime / (1000 * 60 * 60))
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24))
if (diffHours < 1) {
return '刚刚'
} else if (diffHours < 24) {
return `${diffHours}小时前`
} else if (diffDays < 7) {
return `${diffDays}天前`
} else {
return date.toLocaleDateString('zh-CN')
}
}
const readMessage = (message) => {
if (!message.read) {
message.read = true
saveMessages()
}
// 显示消息详情
uni.showModal({
title: message.title,
content: message.content,
showCancel: false
})
}
const markAllRead = () => {
messagesList.value.forEach(msg => {
msg.read = true
})
saveMessages()
uni.showToast({
title: '已全部标记为已读',
icon: 'success'
})
}
const clearReadMessages = () => {
uni.showModal({
title: '清理已读消息',
content: '确定要删除所有已读消息吗?此操作不可恢复。',
success: (res) => {
if (res.confirm) {
messagesList.value = messagesList.value.filter(msg => !msg.read)
saveMessages()
uni.showToast({
title: '已清理已读消息',
icon: 'success'
})
}
}
})
}
const clearAllMessages = () => {
uni.showModal({
title: '清空所有消息',
content: '确定要删除所有消息吗?此操作不可恢复。',
success: (res) => {
if (res.confirm) {
messagesList.value = []
saveMessages()
uni.showToast({
title: '已清空所有消息',
icon: 'success'
})
}
}
})
}
return {
notificationSettings,
dndSettings,
messagesList,
showStartTime,
showEndTime,
selectedStartTime,
selectedEndTime,
unreadCount,
onSettingChange,
onDndToggle,
showStartTimePicker,
showEndTimePicker,
onStartTimeConfirm,
onEndTimeConfirm,
getMessageIcon,
formatTime,
readMessage,
markAllRead,
clearReadMessages,
clearAllMessages
}
}
}
</script>
<style lang="scss" scoped>
.notifications-container {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
min-height: 100vh;
padding-bottom: 40rpx;
}
/* 通用卡片样式 */
.settings-card,
.dnd-card,
.messages-card,
.cleanup-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
.header-action {
.action-text {
font-size: 24rpx;
color: #FF8A80;
}
}
}
}
/* 设置列表 */
.settings-list {
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
&:last-child {
border-bottom: none;
}
.setting-info {
flex: 1;
.setting-title {
display: block;
font-size: 30rpx;
font-weight: 500;
color: #333333;
margin-bottom: 8rpx;
}
.setting-desc {
font-size: 24rpx;
color: #666666;
line-height: 1.4;
}
}
.setting-switch {
margin-left: 24rpx;
}
}
}
/* 免打扰设置 */
.dnd-content {
.dnd-toggle {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.toggle-info {
flex: 1;
.toggle-title {
display: block;
font-size: 30rpx;
font-weight: 500;
color: #333333;
margin-bottom: 8rpx;
}
.toggle-desc {
font-size: 24rpx;
color: #666666;
}
}
.toggle-switch {
margin-left: 24rpx;
}
}
.dnd-time {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
.time-item {
padding: 20rpx;
background: rgba(255, 138, 128, 0.05);
border-radius: 16rpx;
text-align: center;
transition: all 0.3s ease;
&:active {
background: rgba(255, 138, 128, 0.1);
transform: scale(0.98);
}
.time-label {
display: block;
font-size: 22rpx;
color: #666666;
margin-bottom: 8rpx;
}
.time-value {
font-size: 32rpx;
font-weight: 600;
color: #FF8A80;
}
}
}
}
/* 消息列表 */
.messages-list {
.message-item {
display: flex;
align-items: flex-start;
padding: 20rpx 0;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
&.unread {
background: rgba(255, 138, 128, 0.05);
border-radius: 16rpx;
padding: 20rpx;
margin-bottom: 8rpx;
}
&:active {
background: rgba(255, 138, 128, 0.1);
border-radius: 16rpx;
}
.message-icon {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
&.system {
background: rgba(33, 150, 243, 0.1);
}
&.pet {
background: rgba(255, 138, 128, 0.1);
}
&.adoption {
background: rgba(76, 175, 80, 0.1);
}
&.family {
background: rgba(255, 152, 0, 0.1);
}
&.marketing {
background: rgba(156, 39, 176, 0.1);
}
.icon-text {
font-size: 28rpx;
}
}
.message-content {
flex: 1;
.message-title {
font-size: 28rpx;
font-weight: 600;
color: #333333;
margin-bottom: 8rpx;
}
.message-desc {
font-size: 24rpx;
color: #666666;
line-height: 1.5;
margin-bottom: 8rpx;
}
.message-time {
font-size: 20rpx;
color: #999999;
}
}
.message-status {
margin-left: 16rpx;
.unread-dot {
width: 12rpx;
height: 12rpx;
background: #FF8A80;
border-radius: 50%;
}
}
}
}
/* 空消息状态 */
.empty-messages {
text-align: center;
padding: 60rpx 0;
.empty-icon {
font-size: 60rpx;
margin-bottom: 16rpx;
opacity: 0.5;
}
.empty-text {
font-size: 24rpx;
color: #999999;
}
}
/* 清理设置 */
.cleanup-list {
.cleanup-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
&:active {
background: rgba(255, 138, 128, 0.05);
border-radius: 16rpx;
}
.cleanup-info {
flex: 1;
.cleanup-title {
display: block;
font-size: 30rpx;
font-weight: 500;
color: #333333;
margin-bottom: 8rpx;
}
.cleanup-desc {
font-size: 24rpx;
color: #666666;
}
}
.cleanup-action {
.action-arrow {
font-size: 24rpx;
color: #FF8A80;
}
}
}
}
/* 响应式设计 */
@media (max-width: 375px) {
.notifications-container {
.settings-card,
.dnd-card,
.messages-card,
.cleanup-card {
margin: 0 20rpx 20rpx 20rpx;
padding: 24rpx;
}
.dnd-time {
gap: 16rpx;
}
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.notifications-container > view {
animation: fadeIn 0.5s ease-out;
}
.notifications-container > view:nth-child(1) { animation-delay: 0.1s; }
.notifications-container > view:nth-child(2) { animation-delay: 0.2s; }
.notifications-container > view:nth-child(3) { animation-delay: 0.3s; }
.notifications-container > view:nth-child(4) { animation-delay: 0.4s; }
</style>