Files
property-resident-side/property-uniapp-project/pageSubPack/my/settingIndex.vue
2026-08-07 09:24:15 +08:00

458 lines
12 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="contentStyle">
<scroll-view class="page" scroll-y>
<view class="setting-item" v-for="(item, index) in settingList" :key="index" @click="onItemClick(item)">
<text class="item-title">{{ item.title }}</text>
<view class="item-right" v-if="item.type === 'text'">
<text :class="item.valueClass || 'item-value'">{{ item.value }}</text>
<view class="item-arrow"></view>
</view>
<switch class="no-cross-switch" v-else-if="item.type === 'switch'" :checked="item.checked"
@change="onSwitchChange(item, $event)" />
<view class="item-arrow" v-else></view>
</view>
</scroll-view>
<view class="bottom-btn">
<button class="logout-btn" @click="logout">退出登录</button>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted
} from 'vue'
import {
authLogout, getUserProfile, getUserNotify, changeUserNotify, getAppVersion, checkAppUpdate, postUpdateLog
} from '/pageSubPack/api/apiSub.js'
//
const cacheSize = ref(0)
const currentVersion = ref('1.0.0')
const currentVersionCode = ref(1)
const settingList = ref([{
title: '更改手机号码',
type: 'text',
value: ''
},
{
title: '修改密码',
type: 'arrow'
},
{
title: '消息通知',
type: 'switch',
checked: false
},
{
title: '清除缓存',
type: 'text',
value: '9.2M'
},
{
title: '注销账号',
type: 'arrow'
},
// {
// title: '升级版本',
// type: 'text',
// value: '当前版本 1.0.0',
// valueClass: 'item-version'
// }
])
// 格式化字节大小
const formatSize = (bytes) => {
if (!bytes || bytes === 0) return '0B'
const k = 1024
const sizes = ['B', 'KB', 'M', 'G']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + sizes[i]
}
// 获取真实缓存大小
const getCacheSizeData = () => {
return new Promise((resolve) => {
// #ifdef APP-PLUS
// App 端获取完整缓存(含图片、文件等)
plus.cache.calculate((size) => {
resolve(formatSize(size))
})
// #endif
// #ifndef APP-PLUS
// 小程序/H5 端只能获取 storage 缓存
const info = uni.getStorageInfoSync()
const size = (info.currentSize || 0) * 1024 // currentSize 单位 KB
resolve(formatSize(size))
// #endif
})
}
// 获取当前运行版本号
const initCurrentVersion = () => {
// #ifdef APP-PLUS
currentVersion.value = plus.runtime.version || '1.0.0'
currentVersionCode.value = plus.runtime.versionCode || 1
// #endif
// #ifndef APP-PLUS
const manifest = uni.getAppAuthorizeSetting ? uni.getAppAuthorizeSetting() : null
if (manifest && manifest.version) {
currentVersion.value = manifest.version
}
// #endif
if (settingList.value[5]) {
settingList.value[5].value = '当前版本 ' + currentVersion.value
}
}
// 版本号比较v1 > v2 返回 1v1 = v2 返回 0v1 < v2 返回 -1
const compareVersion = (v1, v2) => {
const arr1 = v1.split('.').map(Number)
const arr2 = v2.split('.').map(Number)
const len = Math.max(arr1.length, arr2.length)
for (let i = 0; i < len; i++) {
const n1 = arr1[i] || 0
const n2 = arr2[i] || 0
if (n1 > n2) return 1
if (n1 < n2) return -1
}
return 0
}
// 跳转到应用商店更新
const openAppStore = (downloadUrl, platform) => {
// #ifdef APP-PLUS
if (downloadUrl) {
// 优先使用服务端返回的应用商店地址
const pkgName = plus.runtime.appid || ''
console.log('包名---------',pkgName)
// 无论返回哪种 downloadUrl都在后面拼接包名
const finalUrl = downloadUrl + pkgName
plus.runtime.openURL(finalUrl)
} else if (platform === 'ios') {
// iOS: 打开 App Store需在 Apple Developer 后台获取 Apple ID
plus.runtime.openURL('itms-apps://itunes.apple.com/app/id' + plus.runtime.appid)
} else {
// Android: 通过 market 协议唤起手机自带应用商店
const packageName = plus.runtime.appid || ''
plus.runtime.openURL('market://details?id=' + packageName, () => {
// market 协议失败时的回调,提示用户手动前往应用商店
uni.showToast({ title: '请前往应用商店更新', icon: 'none' })
})
}
// #endif
// #ifndef APP-PLUS
uni.showToast({ title: '请在应用商店更新', icon: 'none' })
// #endif
}
// 检查版本更新
const checkVersionUpdate = async () => {
uni.showLoading({ title: '检查中...', mask: true })
// #ifdef APP-PLUS
const channel = plus.runtime.channel; // 云打包自动写入,自定义包为空
// const channel = 'xiaomi'
// console.log('channel',channel)
// #endif
try {
const systemInfo = uni.getSystemInfoSync()
const platform = systemInfo.platform === 'android' ? 'android':'ios'
const appChannel = 'resident'
// const channel = 'xiaomi'
// 1. 查询最新版本
const versionRes = await getAppVersion(platform, channel, appChannel)
console.log('查询最新版本',versionRes.data)
if (versionRes.code === 200 && versionRes.data) {
const latestVersion = versionRes.data.versionName || ''
const latestVersionCode = versionRes.data.versionCode || 0
// 2. 调 checkAppUpdate 检查是否需要更新
const checkRes = await checkAppUpdate({
platform: platform,
channel: channel,
appChannel: appChannel,
currentVersionCode: currentVersionCode.value
})
uni.hideLoading()
if (checkRes.code === 200 && checkRes.data) {
console.log('是否需要更新',checkRes.data)
const needUpdate = checkRes.data.needUpdate !== false
if (needUpdate) {
// 如果版本号一致,直接提示已是最新版本
if (compareVersion(currentVersion.value, latestVersion) >= 0) {
uni.showToast({ title: '已是最新版本', icon: 'success' })
return
}
const updateType = checkRes.data.updateType || versionRes.data.updateType || 'optional'
const isForce = updateType === 'force'
const downloadUrl = versionRes.data.downloadUrl || ''
uni.showModal({
title: isForce ? '强制更新' : '发现新版本',
content: `当前版本:${currentVersion.value}\n最新版本${latestVersion}${versionRes.data.releaseNotes ? '\n\n更新内容' + (versionRes.data.releaseNotes) : ''}`,
showCancel: !isForce,
confirmText: '立即更新',
cancelText: '稍后',
success: (modalRes) => {
if (modalRes.confirm) {
// 记录更新日志
const userId = uni.getStorageSync('userId')
if (userId) {
postUpdateLog({
userId: userId,
platform: platform,
fromVersion: currentVersionCode.value,
toVersion: latestVersionCode
})
}
// 跳转到应用商店更新
openAppStore(downloadUrl, platform)
} else if (isForce && !modalRes.confirm) {
// 强制更新时用户点取消,退出应用
// #ifdef APP-PLUS
plus.runtime.quit()
// #endif
}
}
})
} else {
uni.showToast({ title: '已是最新版本', icon: 'success' })
}
} else {
uni.showToast({ title: '检查更新失败1', icon: 'none' })
}
} else {
uni.hideLoading()
uni.showToast({ title: '检查更新失败2', icon: 'none' })
}
} catch (err) {
uni.hideLoading()
uni.showToast({ title: err.msg || '检查更新失败', icon: 'none' })
}
}
//
const onItemClick = (item) => {
if (item.title == '更改手机号码') {
uni.navigateTo({
url: '/pageSubPack/my/changePhone'
});
} else if (item.title == '修改密码') {
uni.navigateTo({
url: '/pageSubPack/my/changePwd'
});
} else if (item.title == '清除缓存') {
// 保存登录相关数据,清完恢复
const preserveKeys = ['token', 'userId', 'currentVillageId', 'hasShowGuide', 'push_cid']
const saved = {}
preserveKeys.forEach(key => { saved[key] = uni.getStorageSync(key) })
uni.clearStorageSync()
preserveKeys.forEach(key => { if (saved[key]) uni.setStorageSync(key, saved[key]) })
// #ifdef APP-PLUS
plus.cache.clear(() => {})
// #endif
settingList.value[3].value = '0B'
uni.showLoading({
title: '清除中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '清除成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
uni.hideLoading();
}, 2500);
} else if (item.title == '注销账号') {
uni.navigateTo({
url: '/pageSubPack/my/deleteAccount'
});
} else if (item.title == '升级版本') {
checkVersionUpdate()
}
}
const logout = () => {
uni.showModal({
title: '提示',
content: '确定要退出当前账号吗?',
success: (res) => {
if (!res.confirm) return
uni.showLoading({ title: '退出中...', mask: true })
const clearAndGo = () => {
uni.removeStorageSync('token')
uni.removeStorageSync('userId')
uni.removeStorageSync('currentVillageId')
uni.removeStorageSync('home_cache')
uni.hideLoading()
uni.hideToast()
uni.reLaunch({ url: '/pageSubPack/loginSub/login?mode=pwd' })
}
authLogout().then(res => {
if (res.code === 200) {
uni.hideLoading()
uni.showToast({ title: '退出成功', icon: 'success' })
setTimeout(clearAndGo, 1500)
} else {
clearAndGo()
}
}).catch(err => {
console.log(err)
clearAndGo()
})
}
})
}
const onSwitchChange = (item, e) => {
const checked = e.detail.value
const notifyEnabled = checked ? '1' : '0'
changeUserNotify({ notifyEnabled }).then(res => {
if (res.code === 200) {
item.checked = checked
} else {
uni.showToast({ title: res.msg || '更新失败', icon: 'none' })
item.checked = !checked
}
}).catch(() => {
uni.showToast({ title: '更新失败', icon: 'none' })
item.checked = !checked
})
}
onMounted(async () => {
initCurrentVersion()
settingList.value[3].value = await getCacheSizeData()
getUserProfile().then(res => {
if (res.code === 200 && res.data) {
const phone = res.data.phone || ''
settingList.value[0].value = phone
}
})
getUserNotify().then(res => {
if (res.code === 200 && res.data) {
settingList.value[2].checked = res.data.notifyEnabled === '1'
}
})
})
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style lang="scss" scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0 40rpx;
box-sizing: border-box;
background-color: #fff;
}
.bottom-btn {
padding: 20rpx 30rpx 40rpx;
background-color: #fff;
}
</style>
<style scoped>
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
padding: 30rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.item-title {
font-size: 32rpx;
color: #333;
line-height: 1.4;
}
.item-right {
display: flex;
align-items: center;
gap: 10rpx;
}
.item-value {
font-size: 28rpx;
color: #333;
}
.item-version {
font-size: 28rpx;
color: #999;
}
.item-arrow {
width: 10rpx;
height: 10rpx;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
}
.logout-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
text-align: center;
font-size: 32rpx;
border-radius: 12rpx;
color: #666;
background-color: #F3F3F3;
/* box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06); */
}
.logout-btn:active {
background-color: #f5f5f5;
opacity: 0.8;
}
</style>
<style>
::v-deep .uni-switch-input:before {
background-color: #cccccd !important;
}
.no-cross-switch::before {
display: none !important;
}
.no-cross-switch {
transform: scale(0.85);
border-radius: 50rpx !important;
}
.no-cross-switch>>>.uni-switch-input-checked {
background-color: #007aff !important;
}
</style>