Files
medical-mall/pages/mall/consumer/settings.uvue

859 lines
20 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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="settings-page">
<!-- 顶部栏 -->
<!--<view class="settings-header" :style="{ paddingTop: statusBarHeight + 'px' }">
<text class="back-btn" @click="goBack"></text>
<text class="header-title">设置</text>
</view>-->
<scroll-view class="settings-content" scroll-y>
<!-- 账户设置 -->
<view class="settings-section">
<text class="section-title">账户设置</text>
<view class="section-list">
<view class="list-item" @click="goToProfile">
<text class="item-icon">👤</text>
<text class="item-text">个人资料</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="goToAddressList">
<text class="item-icon">📍</text>
<text class="item-text">收货地址</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="changePassword">
<text class="item-icon">🔒</text>
<text class="item-text">修改密码</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="bindPhone">
<text class="item-icon">📱</text>
<text class="item-text">手机绑定</text>
<view class="item-right">
<text class="item-status" :class="{ bound: userInfo.phone }">
{{ userInfo.phone ? '已绑定' : '未绑定' }}
</text>
<text class="item-arrow"></text>
</view>
</view>
<view class="list-item" @click="bindEmail">
<text class="item-icon">📧</text>
<text class="item-text">邮箱绑定</text>
<view class="item-right">
<text class="item-status" :class="{ bound: userInfo.email }">
{{ userInfo.email ? '已绑定' : '未绑定' }}
</text>
<text class="item-arrow"></text>
</view>
</view>
</view>
</view>
<!-- 消息通知 -->
<view class="settings-section">
<text class="section-title">消息通知</text>
<view class="section-list">
<view class="list-item">
<text class="item-icon">🔔</text>
<text class="item-text">订单消息</text>
<switch :checked="notifications.order" @change="toggleNotification('order')" />
</view>
<view class="list-item">
<text class="item-icon">🎁</text>
<text class="item-text">促销活动</text>
<switch :checked="notifications.promotion" @change="toggleNotification('promotion')" />
</view>
<view class="list-item">
<text class="item-icon">⭐</text>
<text class="item-text">评价提醒</text>
<switch :checked="notifications.review" @change="toggleNotification('review')" />
</view>
</view>
</view>
<!-- 隐私设置 -->
<view class="settings-section">
<text class="section-title">隐私设置</text>
<view class="section-list">
<view class="list-item">
<text class="item-icon">👁️</text>
<text class="item-text">隐藏购物记录</text>
<switch :checked="privacy.hidePurchase" @change="togglePrivacy('hidePurchase')" />
</view>
<view class="list-item">
<text class="item-icon">🔍</text>
<text class="item-text">允许通过手机号找到我</text>
<switch :checked="privacy.allowSearchByPhone" @change="togglePrivacy('allowSearchByPhone')" />
</view>
<view class="list-item">
<text class="item-icon">💬</text>
<text class="item-text">接收商家消息</text>
<switch :checked="privacy.receiveMerchantMsg" @change="togglePrivacy('receiveMerchantMsg')" />
</view>
</view>
</view>
<!-- 通用设置 -->
<view class="settings-section">
<text class="section-title">通用设置</text>
<view class="section-list">
<view class="list-item" @click="clearCache">
<text class="item-icon">🗑️</text>
<text class="item-text">清除缓存</text>
<view class="item-right">
<text class="item-cache">{{ cacheSize }}</text>
<text class="item-arrow"></text>
</view>
</view>
<view class="list-item" @click="changeLanguage">
<text class="item-icon">🌐</text>
<text class="item-text">语言设置</text>
<view class="item-right">
<text class="item-status">{{ currentLanguage }}</text>
<text class="item-arrow"></text>
</view>
</view>
<view class="list-item" @click="changeTheme">
<text class="item-icon">🎨</text>
<text class="item-text">主题设置</text>
<view class="item-right">
<text class="item-status">{{ currentTheme }}</text>
<text class="item-arrow"></text>
</view>
</view>
</view>
</view>
<!-- 我的服务 -->
<view class="settings-section">
<text class="section-title">我的服务</text>
<view class="section-list">
<view class="list-item" @click="goToMyReviews">
<text class="item-icon">📝</text>
<text class="item-text">我的评价</text>
<text class="item-arrow"></text>
</view>
</view>
</view>
<!-- 关于我们 -->
<view class="settings-section">
<text class="section-title">关于我们</text>
<view class="section-list">
<view class="list-item" @click="aboutUs">
<text class="item-icon"></text>
<text class="item-text">关于商城</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="userAgreement">
<text class="item-icon">📜</text>
<text class="item-text">用户协议</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="privacyPolicy">
<text class="item-icon">🛡️</text>
<text class="item-text">隐私政策</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="checkUpdate">
<text class="item-icon">🔄</text>
<text class="item-text">检查更新</text>
<view class="item-right">
<text class="item-status">{{ appVersion }}</text>
<text class="item-arrow"></text>
</view>
</view>
</view>
</view>
<!-- 客服与反馈 -->
<view class="settings-section">
<text class="section-title">客服与反馈</text>
<view class="section-list">
<view class="list-item" @click="contactService">
<text class="item-icon">💬</text>
<text class="item-text">联系客服</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="feedback">
<text class="item-icon">📝</text>
<text class="item-text">意见反馈</text>
<text class="item-arrow"></text>
</view>
<view class="list-item" @click="rateApp">
<text class="item-icon">⭐</text>
<text class="item-text">给个好评</text>
<text class="item-arrow"></text>
</view>
</view>
</view>
<!-- 退出登录 -->
<view class="logout-section">
<button class="logout-btn" @click="logout">退出登录</button>
</view>
<!-- 账号注销 -->
<view class="delete-account-section">
<text class="delete-account" @click="deleteAccount">注销账号</text>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted } from 'vue'
import { onBackPress } from '@dcloudio/uni-app'
import supa from '@/components/supadb/aksupainstance.uts'
// 拦截返回事件,强制跳转到个人中心页
onBackPress((options) => {
// 无论是什么触发的返回系统返回键或导航栏返回按钮都跳转到profile
// 注意onBackPress 只能在 page 中使用component 中无效
uni.switchTab({
url: '/pages/mall/consumer/profile'
})
// 返回 true 表示阻止默认返回行为
return true
})
type UserType = {
id: string
phone: string | null
email: string | null
nickname: string | null
avatar_url: string | null
}
type NotificationType = {
order: boolean
promotion: boolean
review: boolean
}
type PrivacyType = {
hidePurchase: boolean
allowSearchByPhone: boolean
receiveMerchantMsg: boolean
}
const userInfo = ref<UserType>({
id: '',
phone: null,
email: null,
nickname: null,
avatar_url: null
})
const notifications = ref<NotificationType>({
order: true,
promotion: true,
review: true
})
const privacy = ref<PrivacyType>({
hidePurchase: false,
allowSearchByPhone: true,
receiveMerchantMsg: true
})
const cacheSize = ref<string>('0.0 MB')
const currentLanguage = ref<string>('简体中文')
const currentTheme = ref<string>('自动')
const appVersion = ref<string>('1.0.0')
const statusBarHeight = ref(0)
// 生命周期
onMounted(() => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
loadUserInfo()
loadSettings()
})
// 加载用户信息
const loadUserInfo = () => {
const userStore = uni.getStorageSync('userInfo')
if (userStore) {
userInfo.value = userStore
}
}
// 加载设置
const loadSettings = () => {
// 从本地存储加载设置
const savedNotifications = uni.getStorageSync('userNotifications')
if (savedNotifications) {
notifications.value = savedNotifications
}
const savedPrivacy = uni.getStorageSync('userPrivacy')
if (savedPrivacy) {
privacy.value = savedPrivacy
}
// 计算缓存大小
calculateCacheSize()
// 获取应用版本
// @ts-ignore
const appInfo = uni.getAppBaseInfo()
if (appInfo?.appVersion) {
appVersion.value = appInfo.appVersion
}
}
// 计算缓存大小
const calculateCacheSize = () => {
// 这里应该计算实际缓存大小,这里使用模拟数据
cacheSize.value = '12.5 MB'
}
// 跳转到个人资料
const goToProfile = () => {
uni.navigateTo({
url: '/pages/user/profile'
})
}
// 跳转到地址管理
const goToAddressList = () => {
uni.navigateTo({
url: '/pages/mall/consumer/address-list'
})
}
// 修改密码
const changePassword = () => {
uni.navigateTo({
url: '/pages/user/change-password'
})
}
// 绑定手机
const bindPhone = () => {
uni.navigateTo({
url: '/pages/user/bind-phone'
})
}
// 绑定邮箱
const bindEmail = () => {
uni.navigateTo({
url: '/pages/user/bind-email'
})
}
// 切换通知设置
const toggleNotification = (type: keyof NotificationType) => {
notifications.value[type] = !notifications.value[type]
uni.setStorageSync('userNotifications', notifications.value)
}
// 切换隐私设置
const togglePrivacy = (type: keyof PrivacyType) => {
privacy.value[type] = !privacy.value[type]
uni.setStorageSync('userPrivacy', privacy.value)
}
// 清除缓存
const clearCache = () => {
uni.showModal({
title: '清除缓存',
content: `确定要清除 ${cacheSize.value} 缓存吗?`,
success: (res) => {
if (res.confirm) {
// 这里应该清除实际缓存
uni.showLoading({
title: '清除中...'
})
setTimeout(() => {
cacheSize.value = '0.0 MB'
uni.hideLoading()
uni.showToast({
title: '缓存已清除',
icon: 'success'
})
}, 1000)
}
}
})
}
// 切换语言
const changeLanguage = () => {
uni.showActionSheet({
itemList: ['简体中文', 'English', '日本語'],
success: (res) => {
const languages = ['简体中文', 'English', '日本語']
currentLanguage.value = languages[res.tapIndex]
uni.setStorageSync('appLanguage', currentLanguage.value)
uni.showToast({
title: '语言已切换',
icon: 'success'
})
}
})
}
// 切换主题
const changeTheme = () => {
uni.showActionSheet({
itemList: ['自动', '浅色模式', '深色模式'],
success: (res) => {
const themes = ['自动', '浅色模式', '深色模式']
currentTheme.value = themes[res.tapIndex]
uni.setStorageSync('appTheme', currentTheme.value)
uni.showToast({
title: '主题已切换',
icon: 'success'
})
}
})
}
// 我的评价
const goToMyReviews = () => {
// 跳转到订单列表的已完成或者是评价相关的页面
uni.navigateTo({
url: '/pages/mall/consumer/orders?status=completed'
})
}
// 关于我们
const aboutUs = () => {
uni.navigateTo({
url: '/pages/user/terms?type=about'
})
}
// 用户协议
const userAgreement = () => {
uni.navigateTo({
url: '/pages/user/terms?type=agreement'
})
}
// 隐私政策
const privacyPolicy = () => {
uni.navigateTo({
url: '/pages/user/terms?type=privacy'
})
}
// 检查更新
const checkUpdate = () => {
uni.showLoading({
title: '检查更新中...'
})
setTimeout(() => {
uni.hideLoading()
uni.showModal({
title: '检查更新',
content: '当前已是最新版本',
showCancel: false
})
}, 1000)
}
// 联系客服
const contactService = () => {
uni.navigateTo({
url: '/pages/mall/consumer/chat'
})
}
// 意见反馈
const feedback = () => {
uni.navigateTo({
url: '/pages/info/feedback'
})
}
// 给个好评
const rateApp = () => {
// 这里应该跳转到应用商店评分
uni.showModal({
title: '给个好评',
content: '如果喜欢我们的应用,请给个好评吧!',
confirmText: '去评分',
success: (res) => {
if (res.confirm) {
// 跳转到应用商店
// @ts-ignore
uni.navigateToMiniProgram({
appId: 'wx1234567890', // 示例AppID
fail: () => {
uni.showToast({
title: '跳转失败',
icon: 'none'
})
}
})
}
}
})
}
// 退出登录
const logout = () => {
uni.showModal({
title: '退出登录',
content: '确定要退出登录吗?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '正在退出...'
})
// 调用登出接口
const { error } = await supa.auth.signOut()
if (error !== null) {
console.error('登出失败:', error)
// 即使失败也继续清除本地状态
}
// 清除本地存储的用户信息
uni.removeStorageSync('userInfo')
uni.removeStorageSync('user_id')
uni.removeStorageSync('access_token')
uni.hideLoading()
uni.showToast({
title: '已退出登录',
icon: 'success'
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/user/login'
})
}, 1000)
} catch (e) {
uni.hideLoading()
console.error('Logout Exception:', e)
uni.showToast({
title: '退出异常',
icon: 'none'
})
// 强制退出
uni.removeStorageSync('userInfo')
uni.reLaunch({
url: '/pages/user/login'
})
}
}
}
})
}
// 注销账号
const deleteAccount = () => {
uni.showModal({
title: '注销账号',
content: '确定要注销账号吗?此操作不可恢复,所有数据将被删除!',
confirmText: '注销',
confirmColor: '#ff4757',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '注销中...'
})
const userId = userInfo.value.id || uni.getStorageSync('user_id')
if (userId) {
try {
// 标记用户状态为注销 (status=3)
await supa
.from('ml_user_profiles')
.update({ status: 3 })
.eq('user_id', userId)
} catch(e) {
console.error('Update status failed', e)
}
// 登出
await supa.auth.signOut()
}
// 清除本地存储
uni.removeStorageSync('userInfo')
uni.removeStorageSync('user_id')
uni.removeStorageSync('access_token')
// 提示并跳转
uni.hideLoading()
uni.showToast({
title: '账号已注销',
icon: 'success',
duration: 2000
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/user/login'
})
}, 1500)
} catch (err) {
uni.hideLoading()
console.error('注销账号失败:', err)
uni.showToast({
title: '注销失败',
icon: 'none'
})
}
}
}
})
}
</script>
<style scoped>
/* 响应式布局优化 */
.section-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
}
.list-item {
display: flex;
align-items: center;
padding: 15px;
border-bottom: 1px solid #f5f5f5;
background-color: #ffffff;
/* 手机端每行显示4个自适应排到下一行 */
width: 25%;
flex-direction: column; /* 内容改为垂直排列,图标在上文字在下 */
justify-content: center;
text-align: center;
box-sizing: border-box;
border-right: 1px solid #f5f5f5; /* 添加右边框分隔 */
}
.item-icon {
font-size: 24px;
margin-right: 0; /* 移除右侧间距 */
margin-bottom: 5px; /* 添加底部间距 */
}
.item-text {
font-size: 12px;
color: #333333;
/* 文字太长可能需要处理,这里暂时不做截断 */
}
.item-arrow {
display: none; /* 网格模式下通常不需要箭头 */
}
.item-right {
display: none; /* 简化显示,隐藏右侧状态/箭头等复杂内容 */
}
/* 针对 switch 组件的特殊处理,如果需要显示开关,可能需要调整布局 */
.list-item switch {
transform: scale(0.7);
margin-top: 5px;
}
/* 屏幕宽度大于 480px (大屏手机/平板/PC) 时,启用更宽的网格布局或列表布局 */
@media screen and (min-width: 480px) {
.list-item {
width: calc(50% - 10px); /* 每行两个,留出间隙 */
margin: 5px;
border: 1px solid #f0f0f0;
border-radius: 8px;
border-bottom: 1px solid #f0f0f0;
flex-direction: row; /* 恢复水平排列 */
text-align: left;
justify-content: flex-start;
}
.item-icon {
margin-right: 15px;
margin-bottom: 0;
}
.item-text {
font-size: 14px;
}
.item-arrow, .item-right {
display: flex; /* 恢复显示 */
margin-left: auto; /* 推到右侧 */
}
}
/* 增加针对手机横屏的媒体查询 */
@media screen and (orientation: landscape) and (max-height: 500px) {
.list-item {
width: calc(25% - 10px); /* 横屏也保持4个一行或者根据需要调整 */
margin: 5px;
border: 1px solid #f0f0f0;
border-radius: 8px;
flex-direction: column;
}
}
/* 屏幕宽度大于 1024px (大屏PC) 时 */
@media screen and (min-width: 1024px) {
.settings-page {
flex-direction: row; /* 整体左右布局 */
}
.settings-header {
display: none;
}
.settings-content {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.list-item {
width: calc(33.33% - 10px); /* 每行三个 */
flex-direction: row; /* PC端保持水平排列 */
justify-content: flex-start;
text-align: left;
}
.item-icon {
margin-right: 15px;
margin-bottom: 0;
}
.item-arrow, .item-right {
display: flex;
margin-left: auto;
}
}
.settings-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.settings-header {
background-color: #ffffff;
padding: 15px;
display: flex;
align-items: center;
border-bottom: 1px solid #e5e5e5;
}
.back-btn {
font-size: 24px;
color: #333333;
padding: 5px;
margin-right: 15px;
}
.header-title {
font-size: 18px;
font-weight: bold;
color: #333333;
}
.settings-content {
flex: 1;
}
.settings-section {
background-color: #ffffff;
margin-bottom: 10px;
padding: 15px;
}
.section-title {
display: block;
font-size: 16px;
font-weight: bold;
color: #333333;
margin-bottom: 15px;
}
/* 删除多余的 .section-list 定义 */
/* 删除多余的 .list-item 定义 */
/* 删除多余的 .list-item:last-child 定义 */
.item-icon {
font-size: 20px;
margin-right: 15px;
}
.item-text {
flex: 1;
font-size: 14px;
color: #333333;
}
.item-arrow {
color: #999999;
font-size: 16px;
margin-left: 10px;
}
.item-right {
display: flex;
align-items: center;
}
.item-status {
font-size: 12px;
color: #999999;
margin-right: 10px;
}
.item-status.bound {
color: #4caf50;
}
.item-cache {
font-size: 12px;
color: #999999;
margin-right: 10px;
}
.logout-section {
background-color: #ffffff;
margin-top: 10px;
padding: 15px;
}
.logout-btn {
background-color: #ffffff;
color: #ff4757;
height: 45px;
border: 1px solid #ff4757;
border-radius: 22.5px;
font-size: 16px;
font-weight: bold;
}
.delete-account-section {
background-color: #ffffff;
padding: 20px 15px;
text-align: center;
}
.delete-account {
color: #999999;
font-size: 14px;
text-decoration: underline;
}
</style>