后端: 身份与归属鉴权收口; 资金安全(结算闸门/支付防篡改/报名费真实扣款/赠金/结算唯一入账/提现打款); 数据泄露与隐私; 登录与密钥安全(默认密钥闸门/登录限流); 可观测性; 启动自检. 微信: 商家转账到零钱提现打款闭环(含回调/查单/失败退回佣金); 订阅消息真实下发闭环与后台模板字段对齐. 后台: 财务/微信支付配置读取失败锁定; 看板/待办/走势去假数据; 打款与测试推送文案修正. 小程序: 请求层错误透传与登录过期提示; 多页失败反馈与乐观回滚; 帮助中心FAQ页面化. 测试: 新增 40+ 回归护栏与真实 MySQL 冒烟(75 套件/599 用例全绿).
904 行
26 KiB
Vue
904 行
26 KiB
Vue
<template>
|
||
<view class="settings-page" :class="['theme-' + currentThemeKey]" :style="navStyle">
|
||
<!-- 顶部沉浸式自定义导航栏 -->
|
||
<view class="custom-nav-bar">
|
||
<view class="nav-content">
|
||
<view class="btn-nav-back btn-active" @click="handleBack">
|
||
<image class="nav-back-icon" :src="ICONS.chevronLeft" mode="aspectFit" />
|
||
</view>
|
||
<view class="nav-title-wrap">
|
||
<text class="nav-title">系统与隐私设置</text>
|
||
</view>
|
||
<!-- 胶囊右侧对称避让占位 -->
|
||
<view class="nav-capsule-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 设置主体滚动视口 -->
|
||
<scroll-view scroll-y class="settings-scroll-view">
|
||
<view class="settings-content-wrap">
|
||
|
||
<!-- 1. 隐私安全保护分组 -->
|
||
<view class="setting-group-box">
|
||
<view class="group-title-row">
|
||
<text class="group-title-txt">隐私安全保护</text>
|
||
<text class="group-badge-txt">数据受端到端加密保护</text>
|
||
</view>
|
||
|
||
<view class="setting-card">
|
||
<!-- 手机号隐藏与脱敏 -->
|
||
<view class="setting-item-row">
|
||
<view class="item-left-col">
|
||
<view class="item-icon-box bg-indigo">
|
||
<image class="item-svg" :src="ICONS.shield" mode="aspectFit" />
|
||
</view>
|
||
<view class="item-info-meta">
|
||
<text class="item-primary-label">隐藏手机号与联系方式</text>
|
||
<text class="item-secondary-desc">开启后个人名片及组局联络等公开场景,手机中间4位自动脱敏</text>
|
||
</view>
|
||
</view>
|
||
<view class="item-action-col">
|
||
<switch
|
||
class="custom-switch"
|
||
:checked="privacyHidePhone"
|
||
:color="switchActiveColor"
|
||
@change="onToggleHidePhone"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 2. 通知与动态订阅分组 -->
|
||
<view class="setting-group-box">
|
||
<view class="group-title-row">
|
||
<text class="group-title-txt">通知与订阅</text>
|
||
<text class="group-badge-txt">即时接收关键动态</text>
|
||
</view>
|
||
|
||
<view class="setting-card">
|
||
<!-- 订单与组局动态提醒 -->
|
||
<view class="setting-item-row">
|
||
<view class="item-left-col">
|
||
<view class="item-icon-box bg-gold">
|
||
<image class="item-svg" :src="ICONS.bell" mode="aspectFit" />
|
||
</view>
|
||
<view class="item-info-meta">
|
||
<text class="item-primary-label">订单与组局动态提醒</text>
|
||
<text class="item-secondary-desc">接收陪伴师接单、服务验收与退款仲裁即时通知</text>
|
||
</view>
|
||
</view>
|
||
<view class="item-action-col">
|
||
<switch
|
||
class="custom-switch"
|
||
:checked="noticeSubscribe"
|
||
:color="switchActiveColor"
|
||
@change="onToggleNotice"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 3. 本地数据与存储分组 -->
|
||
<view class="setting-group-box">
|
||
<view class="group-title-row">
|
||
<text class="group-title-txt">本地数据与存储</text>
|
||
<text class="group-badge-txt">释放设备闲置空间</text>
|
||
</view>
|
||
|
||
<view class="setting-card">
|
||
<!-- 清除本地缓存 -->
|
||
<view class="setting-item-row">
|
||
<view class="item-left-col">
|
||
<view class="item-icon-box bg-red">
|
||
<image class="item-svg" :src="ICONS.trash" mode="aspectFit" />
|
||
</view>
|
||
<view class="item-info-meta">
|
||
<text class="item-primary-label">清除本地缓存数据</text>
|
||
<text class="item-secondary-desc">清理临时图片与历史交互记录,当前占用:{{ cacheSizeText }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="item-action-col">
|
||
<button class="btn-clear-cache btn-active" @click="clearLocalCache">
|
||
一键清理
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 4. 关于系统与合规政策分组 -->
|
||
<view class="setting-group-box">
|
||
<view class="group-title-row">
|
||
<text class="group-title-txt">关于系统与合规</text>
|
||
<text class="group-badge-txt">资质公示与协议条款</text>
|
||
</view>
|
||
|
||
<view class="setting-card list-card">
|
||
<!-- 软件版本 -->
|
||
<view class="setting-item-row border-bottom">
|
||
<view class="item-left-col">
|
||
<view class="item-icon-box bg-blue">
|
||
<image class="item-svg" :src="ICONS.settings" mode="aspectFit" />
|
||
</view>
|
||
<view class="item-info-meta">
|
||
<text class="item-primary-label">软件版本</text>
|
||
<text class="item-secondary-desc">平台稳定运行环境</text>
|
||
</view>
|
||
</view>
|
||
<view class="item-action-col version-tag-col">
|
||
<text class="version-label">v2.6.0 正式版</text>
|
||
<view class="badge-latest">
|
||
<text class="badge-txt">已是最新</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 服务协议与隐私政策长文入口 -->
|
||
<view class="setting-item-row clickable btn-active" @click="goToProtocol">
|
||
<view class="item-left-col">
|
||
<view class="item-icon-box bg-emerald">
|
||
<image class="item-svg" :src="ICONS.fileText" mode="aspectFit" />
|
||
</view>
|
||
<view class="item-info-meta">
|
||
<text class="item-primary-label">服务协议与用户隐私政策</text>
|
||
<text class="item-secondary-desc">阅读完整法律条款与第三方信息共享清单</text>
|
||
</view>
|
||
</view>
|
||
<view class="item-action-col">
|
||
<image class="item-arrow-svg" :src="ICONS.chevronRight" mode="aspectFit" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 5. 账号安全与登录会话管理 (登录态下可见) -->
|
||
<view class="setting-group-box account-session-box" v-if="isLoggedIn">
|
||
<view class="group-title-row">
|
||
<text class="group-title-txt">账号会话管理</text>
|
||
</view>
|
||
|
||
<view class="account-actions-card">
|
||
<button class="btn-account-switch btn-active" @click="handleSwitchAccount">
|
||
切换登录账号
|
||
</button>
|
||
<button class="btn-account-logout btn-active" @click="handleLogout">
|
||
退出当前登录
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 页面底部平台版权合规声明 -->
|
||
<view class="settings-footer-info">
|
||
<text class="footer-p">电竞陪练陪玩官方正品保障平台</text>
|
||
<text class="footer-sub">倡导绿色健康竞技 · 抵制不良私下交易</text>
|
||
</view>
|
||
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import { ICONS } from '@/common/icons.js';
|
||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||
import { getNavBarInfo } from '@/common/nav.js';
|
||
import { getUserSettings, updateUserSettings, getSubscribeTemplates } from '@/common/api.js';
|
||
|
||
const currentThemeKey = ref(getCurrentThemeKey());
|
||
const navInfo = ref(getNavBarInfo());
|
||
|
||
const ESSENTIAL_STORAGE_KEYS = [
|
||
'peiwan_token',
|
||
'peiwan_current_role',
|
||
'app_theme_key',
|
||
'privacy_hide_phone',
|
||
'notice_subscribe',
|
||
'userId',
|
||
'userInfo',
|
||
'user_has_logged_out',
|
||
'expert_cert_status',
|
||
'privacy_agreed',
|
||
'cache_cleaned_snapshot_kb'
|
||
];
|
||
|
||
const checkIsLoggedIn = () => {
|
||
const hasLoggedOut = uni.getStorageSync('user_has_logged_out') === '1';
|
||
if (hasLoggedOut) return false;
|
||
return true;
|
||
};
|
||
|
||
const currentUserId = computed(() => {
|
||
return Number(uni.getStorageSync('userId') || 1001);
|
||
});
|
||
|
||
// 本地与云端同步设置状态
|
||
const privacyHidePhone = ref(uni.getStorageSync('privacy_hide_phone') === '1');
|
||
const noticeSubscribe = ref(uni.getStorageSync('notice_subscribe') !== '0');
|
||
const cacheSizeText = ref('0 KB');
|
||
const isLoggedIn = ref(checkIsLoggedIn());
|
||
|
||
// 顶部自定义导航栏适配
|
||
const navStyle = computed(() => {
|
||
return {
|
||
'--safe-header-gap': `${navInfo.value.totalHeaderHeight}px`,
|
||
'--capsule-right-space': `${navInfo.value.capsuleRightSpace}px`,
|
||
'--capsule-height': `${navInfo.value.capsuleHeight}px`
|
||
};
|
||
});
|
||
|
||
// 开关的主题适配激活高亮色
|
||
const switchActiveColor = computed(() => {
|
||
return currentThemeKey.value === 'warm' ? '#EA580C' : '#6366F1';
|
||
});
|
||
|
||
// 真实计算可清理的本地临时缓存大小
|
||
const updateCacheSize = () => {
|
||
try {
|
||
const info = uni.getStorageInfoSync();
|
||
const allKeys = Array.isArray(info.keys) ? info.keys : [];
|
||
const tempKeys = allKeys.filter(k => !ESSENTIAL_STORAGE_KEYS.includes(k));
|
||
const snapshotRaw = uni.getStorageSync('cache_cleaned_snapshot_kb');
|
||
const hasCleanedBefore = snapshotRaw !== '' && snapshotRaw !== null && snapshotRaw !== undefined;
|
||
|
||
let tempBytes = 0;
|
||
tempKeys.forEach((k) => {
|
||
try {
|
||
const val = uni.getStorageSync(k);
|
||
const serialized = typeof val === 'string' ? val : JSON.stringify(val || '');
|
||
tempBytes += (k.length + serialized.length) * 2;
|
||
} catch (_) {}
|
||
});
|
||
|
||
const currentKb = Number(info.currentSize || 0);
|
||
if (tempBytes > 0) {
|
||
const kb = Math.max(1, Math.ceil(tempBytes / 1024));
|
||
cacheSizeText.value = kb > 1024 ? `${(kb / 1024).toFixed(2)} MB` : `${kb} KB`;
|
||
} else if (!hasCleanedBefore && currentKb > 0) {
|
||
cacheSizeText.value = currentKb > 1024 ? `${(currentKb / 1024).toFixed(2)} MB` : `${currentKb} KB`;
|
||
} else {
|
||
const diffKb = Math.max(0, currentKb - Number(snapshotRaw || 0));
|
||
cacheSizeText.value = diffKb > 1024 ? `${(diffKb / 1024).toFixed(2)} MB` : `${diffKb} KB`;
|
||
}
|
||
} catch (_) {
|
||
cacheSizeText.value = '0 KB';
|
||
}
|
||
};
|
||
|
||
// 同步远端用户的隐私与通知偏好
|
||
const syncSettingsFromServer = async () => {
|
||
if (!isLoggedIn.value) return;
|
||
try {
|
||
const res = await getUserSettings(currentUserId.value);
|
||
if (res && typeof res === 'object') {
|
||
if (typeof res.privacyHidePhone === 'boolean') {
|
||
privacyHidePhone.value = res.privacyHidePhone;
|
||
}
|
||
if (typeof res.noticeSubscribe === 'boolean') {
|
||
noticeSubscribe.value = res.noticeSubscribe;
|
||
}
|
||
}
|
||
} catch (_) {}
|
||
};
|
||
|
||
// 切换手机号脱敏隐藏(同步本地与后端并广播全站脱敏事件)
|
||
const onToggleHidePhone = async (e) => {
|
||
const val = !!e.detail.value;
|
||
const prev = privacyHidePhone.value;
|
||
privacyHidePhone.value = val;
|
||
uni.setStorageSync('privacy_hide_phone', val ? '1' : '0');
|
||
uni.$emit('privacy-settings-change', { privacyHidePhone: val });
|
||
try {
|
||
await updateUserSettings(currentUserId.value, {
|
||
privacyHidePhone: val,
|
||
noticeSubscribe: noticeSubscribe.value
|
||
});
|
||
} catch (err) {
|
||
// 同步失败回滚本地状态并提示,避免"未同步却显示已保存"
|
||
privacyHidePhone.value = prev;
|
||
uni.setStorageSync('privacy_hide_phone', prev ? '1' : '0');
|
||
uni.$emit('privacy-settings-change', { privacyHidePhone: prev });
|
||
uni.showToast({ title: (err && err.message) || '设置同步失败,请稍后重试', icon: 'none' });
|
||
return;
|
||
}
|
||
uni.showToast({
|
||
title: val ? '已开启手机号脱敏保护' : '已关闭手机号脱敏保护',
|
||
icon: 'none'
|
||
});
|
||
};
|
||
|
||
// 使用后台配置的真实模板ID 请求微信订阅授权(过滤占位模板ID)
|
||
const requestNoticeSubscribe = async () => {
|
||
try {
|
||
const tpl = await getSubscribeTemplates();
|
||
if (!tpl || tpl.enablePush === false) return;
|
||
const ids = [
|
||
tpl.orderStatusTemplateId,
|
||
tpl.activityJoinTemplateId,
|
||
tpl.withdrawSuccessTemplateId,
|
||
tpl.verifyCodeTemplateId
|
||
].filter((x) => x && !String(x).startsWith('tmpl_'));
|
||
if (ids.length === 0) return;
|
||
if (typeof uni.requestSubscribeMessage === 'function') {
|
||
uni.requestSubscribeMessage({ tmplIds: ids, complete: () => {} });
|
||
}
|
||
} catch (_) {
|
||
// 订阅授权失败不影响主流程
|
||
}
|
||
};
|
||
|
||
// 切换通知订阅(微信小程序端支持唤起订阅授权,并同步服务端)
|
||
const onToggleNotice = async (e) => {
|
||
const val = !!e.detail.value;
|
||
const prev = noticeSubscribe.value;
|
||
noticeSubscribe.value = val;
|
||
uni.setStorageSync('notice_subscribe', val ? '1' : '0');
|
||
|
||
// #ifdef MP-WEIXIN
|
||
if (val) {
|
||
await requestNoticeSubscribe();
|
||
}
|
||
// #endif
|
||
|
||
uni.$emit('notice-subscribe-change', val);
|
||
try {
|
||
await updateUserSettings(currentUserId.value, {
|
||
privacyHidePhone: privacyHidePhone.value,
|
||
noticeSubscribe: val
|
||
});
|
||
} catch (err) {
|
||
// 同步失败回滚本地状态并提示
|
||
noticeSubscribe.value = prev;
|
||
uni.setStorageSync('notice_subscribe', prev ? '1' : '0');
|
||
uni.$emit('notice-subscribe-change', prev);
|
||
uni.showToast({ title: (err && err.message) || '设置同步失败,请稍后重试', icon: 'none' });
|
||
return;
|
||
}
|
||
uni.showToast({
|
||
title: val ? '已开启订单与组局动态提醒' : '已关闭订单与组局动态提醒',
|
||
icon: 'none'
|
||
});
|
||
};
|
||
|
||
// 一键清理本地缓存(精准清理非核心临时键值与本地保存的临时文件)
|
||
const clearLocalCache = () => {
|
||
uni.showModal({
|
||
title: '清除缓存',
|
||
content: '清理将释放临时图片与历史交互缓存,保留当前登录账号与隐私配置。确定继续?',
|
||
confirmText: '确定清理',
|
||
cancelText: '取消',
|
||
confirmColor: '#EF4444',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
const info = uni.getStorageInfoSync();
|
||
const allKeys = Array.isArray(info.keys) ? info.keys : [];
|
||
let removedCount = 0;
|
||
allKeys.forEach((k) => {
|
||
if (!ESSENTIAL_STORAGE_KEYS.includes(k)) {
|
||
uni.removeStorageSync(k);
|
||
removedCount++;
|
||
}
|
||
});
|
||
|
||
// #ifdef MP-WEIXIN
|
||
if (typeof uni.getSavedFileList === 'function') {
|
||
uni.getSavedFileList({
|
||
success: (fileRes) => {
|
||
if (fileRes && Array.isArray(fileRes.fileList)) {
|
||
fileRes.fileList.forEach((f) => {
|
||
uni.removeSavedFile({ filePath: f.filePath, fail: () => {} });
|
||
});
|
||
}
|
||
},
|
||
fail: () => {}
|
||
});
|
||
}
|
||
// #endif
|
||
|
||
const afterInfo = uni.getStorageInfoSync();
|
||
uni.setStorageSync('cache_cleaned_snapshot_kb', Number(afterInfo.currentSize || 0));
|
||
const finalInfo = uni.getStorageInfoSync();
|
||
uni.setStorageSync('cache_cleaned_snapshot_kb', Number(finalInfo.currentSize || 0));
|
||
|
||
cacheSizeText.value = '0 KB';
|
||
uni.showToast({
|
||
title: removedCount > 0 ? `已清理 ${removedCount} 项临时缓存` : '缓存清理完毕 (0 KB)',
|
||
icon: 'none'
|
||
});
|
||
} catch (_) {
|
||
cacheSizeText.value = '0 KB';
|
||
uni.showToast({
|
||
title: '缓存清理完毕',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
// 跳转协议长文页面
|
||
const goToProtocol = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/common/protocol'
|
||
});
|
||
};
|
||
|
||
// 切换账号
|
||
const handleSwitchAccount = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/auth/login'
|
||
});
|
||
};
|
||
|
||
// 退出登录(与个人中心退出逻辑完全同步)
|
||
const handleLogout = () => {
|
||
uni.showModal({
|
||
title: '退出登录',
|
||
content: '退出登录后将清除本地会话,切换为未登录游客态。确定退出吗?',
|
||
confirmText: '确认退出',
|
||
cancelText: '取消',
|
||
confirmColor: '#EF4444',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.removeStorageSync('peiwan_token');
|
||
uni.removeStorageSync('peiwan_current_role');
|
||
uni.removeStorageSync('userInfo');
|
||
uni.removeStorageSync('userId');
|
||
uni.removeStorageSync('userToken');
|
||
uni.setStorageSync('user_has_logged_out', '1');
|
||
isLoggedIn.value = false;
|
||
uni.showToast({
|
||
title: '已退出当前登录',
|
||
icon: 'none'
|
||
});
|
||
setTimeout(() => {
|
||
uni.switchTab({
|
||
url: '/pages/user/index'
|
||
});
|
||
}, 600);
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
// 导航栏返回
|
||
const handleBack = () => {
|
||
const pages = getCurrentPages();
|
||
if (pages.length > 1) {
|
||
uni.navigateBack();
|
||
} else {
|
||
uni.switchTab({
|
||
url: '/pages/user/index'
|
||
});
|
||
}
|
||
};
|
||
|
||
onShow(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI();
|
||
navInfo.value = getNavBarInfo();
|
||
privacyHidePhone.value = uni.getStorageSync('privacy_hide_phone') === '1';
|
||
noticeSubscribe.value = uni.getStorageSync('notice_subscribe') !== '0';
|
||
isLoggedIn.value = checkIsLoggedIn();
|
||
updateCacheSize();
|
||
syncSettingsFromServer();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.settings-page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
box-sizing: border-box;
|
||
background-color: #0A0F1D;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
/* 顶部沉浸式自定义导航栏 */
|
||
.custom-nav-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
height: var(--safe-header-gap);
|
||
background: rgba(10, 15, 29, 0.88);
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.nav-content {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: var(--capsule-height);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 24rpx;
|
||
|
||
.btn-nav-back {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
border-radius: 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(255, 255, 255, 0.08);
|
||
|
||
.nav-back-icon {
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
}
|
||
}
|
||
|
||
.nav-title-wrap {
|
||
position: absolute;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
text-align: center;
|
||
|
||
.nav-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
letter-spacing: 0.5rpx;
|
||
}
|
||
}
|
||
|
||
.nav-capsule-placeholder {
|
||
width: var(--capsule-right-space);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 滚动内容区 */
|
||
.settings-scroll-view {
|
||
flex: 1;
|
||
margin-top: var(--safe-header-gap);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.settings-content-wrap {
|
||
padding: 30rpx 28rpx 80rpx;
|
||
}
|
||
|
||
/* 分组盒子 */
|
||
.setting-group-box {
|
||
margin-bottom: 36rpx;
|
||
|
||
.group-title-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
padding: 0 12rpx 16rpx;
|
||
|
||
.group-title-txt {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #94A3B8;
|
||
letter-spacing: 0.5rpx;
|
||
}
|
||
|
||
.group-badge-txt {
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
.setting-card {
|
||
background: #0F172A;
|
||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||
border-radius: 24rpx;
|
||
padding: 24rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.25);
|
||
|
||
&.list-card {
|
||
padding: 8rpx 24rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 单行项 */
|
||
.setting-item-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16rpx 0;
|
||
|
||
&.border-bottom {
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
&.clickable {
|
||
padding: 20rpx 0;
|
||
}
|
||
|
||
.item-left-col {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
margin-right: 20rpx;
|
||
|
||
.item-icon-box {
|
||
width: 68rpx;
|
||
height: 68rpx;
|
||
border-radius: 18rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
|
||
&.bg-indigo {
|
||
background: rgba(99, 102, 241, 0.15);
|
||
}
|
||
&.bg-gold {
|
||
background: rgba(245, 158, 11, 0.15);
|
||
}
|
||
&.bg-red {
|
||
background: rgba(239, 68, 68, 0.15);
|
||
}
|
||
&.bg-blue {
|
||
background: rgba(59, 130, 246, 0.15);
|
||
}
|
||
&.bg-emerald {
|
||
background: rgba(16, 185, 129, 0.15);
|
||
}
|
||
|
||
.item-svg {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
}
|
||
}
|
||
|
||
.item-info-meta {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6rpx;
|
||
|
||
.item-primary-label {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #F8FAFC;
|
||
}
|
||
|
||
.item-secondary-desc {
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
}
|
||
|
||
.item-action-col {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.item-arrow-svg {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.btn-clear-cache {
|
||
height: 56rpx;
|
||
line-height: 54rpx;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
color: #EF4444;
|
||
background: rgba(239, 68, 68, 0.12);
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
border-radius: 28rpx;
|
||
padding: 0 24rpx;
|
||
margin: 0;
|
||
}
|
||
|
||
&.version-tag-col {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
|
||
.version-label {
|
||
font-size: 24rpx;
|
||
color: #94A3B8;
|
||
font-family: monospace;
|
||
}
|
||
|
||
.badge-latest {
|
||
background: rgba(16, 185, 129, 0.15);
|
||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||
border-radius: 12rpx;
|
||
padding: 2rpx 10rpx;
|
||
|
||
.badge-txt {
|
||
font-size: 18rpx;
|
||
color: #10B981;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 账号会话操作卡片 */
|
||
.account-session-box {
|
||
margin-top: 48rpx;
|
||
|
||
.account-actions-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
|
||
.btn-account-switch {
|
||
height: 84rpx;
|
||
line-height: 82rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #818CF8;
|
||
background: rgba(99, 102, 241, 0.12);
|
||
border: 1px solid rgba(99, 102, 241, 0.25);
|
||
border-radius: 42rpx;
|
||
margin: 0;
|
||
}
|
||
|
||
.btn-account-logout {
|
||
height: 84rpx;
|
||
line-height: 82rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #EF4444;
|
||
background: rgba(239, 68, 68, 0.1);
|
||
border: 1px solid rgba(239, 68, 68, 0.2);
|
||
border-radius: 42rpx;
|
||
margin: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 页脚声明 */
|
||
.settings-footer-info {
|
||
text-align: center;
|
||
padding-top: 40rpx;
|
||
padding-bottom: 30rpx;
|
||
|
||
.footer-p {
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.footer-sub {
|
||
font-size: 20rpx;
|
||
color: #475569;
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
/* 暖阳浅色模式适配 (theme-warm) */
|
||
&.theme-warm {
|
||
background-color: #FFFDF9;
|
||
|
||
.custom-nav-bar {
|
||
background: rgba(255, 253, 249, 0.92);
|
||
border-bottom-color: rgba(234, 88, 12, 0.1);
|
||
|
||
.nav-content {
|
||
.btn-nav-back {
|
||
background: #FFF7ED;
|
||
border: 1px solid #FED7AA;
|
||
}
|
||
|
||
.nav-title-wrap .nav-title {
|
||
color: #1C1917;
|
||
}
|
||
}
|
||
}
|
||
|
||
.setting-group-box {
|
||
.group-title-row {
|
||
.group-title-txt {
|
||
color: #9A3412;
|
||
}
|
||
.group-badge-txt {
|
||
color: #A8A29E;
|
||
}
|
||
}
|
||
|
||
.setting-card {
|
||
background: #FFFFFF;
|
||
border: 1px solid #FED7AA;
|
||
box-shadow: 0 4rpx 20rpx rgba(234, 88, 12, 0.06);
|
||
}
|
||
}
|
||
|
||
.setting-item-row {
|
||
&.border-bottom {
|
||
border-bottom-color: #F5F5F4;
|
||
}
|
||
|
||
.item-left-col {
|
||
.item-icon-box {
|
||
&.bg-indigo { background: #FFF7ED; }
|
||
&.bg-gold { background: #FEF3C7; }
|
||
&.bg-red { background: #FEE2E2; }
|
||
&.bg-blue { background: #EFF6FF; }
|
||
&.bg-emerald { background: #ECFDF5; }
|
||
}
|
||
|
||
.item-info-meta {
|
||
.item-primary-label {
|
||
color: #1C1917;
|
||
}
|
||
.item-secondary-desc {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
}
|
||
|
||
.item-action-col {
|
||
.btn-clear-cache {
|
||
background: #FEF2F2;
|
||
border-color: #FECACA;
|
||
color: #DC2626;
|
||
}
|
||
|
||
&.version-tag-col {
|
||
.version-label {
|
||
color: #78716C;
|
||
}
|
||
.badge-latest {
|
||
background: #ECFDF5;
|
||
border-color: #A7F3D0;
|
||
.badge-txt {
|
||
color: #059669;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.account-session-box {
|
||
.account-actions-card {
|
||
.btn-account-switch {
|
||
color: #EA580C;
|
||
background: #FFF7ED;
|
||
border-color: #FDBA74;
|
||
}
|
||
.btn-account-logout {
|
||
color: #DC2626;
|
||
background: #FEF2F2;
|
||
border-color: #FECACA;
|
||
}
|
||
}
|
||
}
|
||
|
||
.settings-footer-info {
|
||
.footer-p {
|
||
color: #A8A29E;
|
||
}
|
||
.footer-sub {
|
||
color: #D6D3D1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.btn-active {
|
||
transition: opacity 0.15s ease, transform 0.15s ease;
|
||
&:active {
|
||
opacity: 0.8;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
</style>
|