This commit is contained in:
2025-12-09 21:32:03 +08:00
parent 37de05de48
commit 98af3513b4
9 changed files with 1335 additions and 1834 deletions

14
components.d.ts vendored
View File

@@ -33,19 +33,5 @@ declare module 'vue' {
UserMomentsModal: typeof import('./src/components/moment/UserMomentsModal.vue')['default']
VideoPlayer: typeof import('./src/components/common/VideoPlayer.vue')['default']
VisibilitySelector: typeof import('./src/components/moment/VisibilitySelector.vue')['default']
WdActionSheet: typeof import('wot-design-uni/components/wd-action-sheet/wd-action-sheet.vue')['default']
WdBadge: typeof import('wot-design-uni/components/wd-badge/wd-badge.vue')['default']
WdButton: typeof import('wot-design-uni/components/wd-button/wd-button.vue')['default']
WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
WdEmpty: typeof import('wot-design-uni/components/wd-empty/wd-empty.vue')['default']
WdIcon: typeof import('wot-design-uni/components/wd-icon/wd-icon.vue')['default']
WdImg: typeof import('wot-design-uni/components/wd-img/wd-img.vue')['default']
WdLoading: typeof import('wot-design-uni/components/wd-loading/wd-loading.vue')['default']
WdMessageBox: typeof import('wot-design-uni/components/wd-message-box/wd-message-box.vue')['default']
WdPopup: typeof import('wot-design-uni/components/wd-popup/wd-popup.vue')['default']
WdSearch: typeof import('wot-design-uni/components/wd-search/wd-search.vue')['default']
WdSwipeAction: typeof import('wot-design-uni/components/wd-swipe-action/wd-swipe-action.vue')['default']
WdSwitch: typeof import('wot-design-uni/components/wd-switch/wd-switch.vue')['default']
WdToast: typeof import('wot-design-uni/components/wd-toast/wd-toast.vue')['default']
}
}

View File

@@ -1,76 +1,112 @@
<template>
<wd-navbar
:title="title"
:left-arrow="leftArrow"
:fixed="fixed"
:placeholder="placeholder"
:bordered="bordered"
:safe-area-inset-top="safeAreaInsetTop"
:z-index="zIndex"
custom-class="app-navbar"
@click-left="onClickLeft"
@click-right="onClickRight"
>
<template v-if="$slots.left" #left>
<slot name="left" />
</template>
<template v-if="$slots.title" #title>
<slot name="title" />
</template>
<template v-if="$slots.right" #right>
<slot name="right" />
</template>
</wd-navbar>
<view :class="{ dark: isDark }">
<wd-navbar
:title="title"
:left-text="leftText"
:right-text="rightText"
:left-arrow="leftArrow"
:fixed="fixed"
:placeholder="placeholder"
:safe-area-inset-top="safeAreaInsetTop"
:bordered="bordered"
custom-class="app-nav-bar"
@click-left="handleClickLeft"
@click-right="handleClickRight"
>
<!-- 插槽透传 -->
<template #left v-if="$slots.left">
<slot name="left" />
</template>
<template #title v-if="$slots.title">
<slot name="title" />
</template>
<template #right v-if="$slots.right">
<slot name="right" />
</template>
</wd-navbar>
</view>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useTheme } from '@/composables/useTheme'
const { isDark } = useTheme()
interface Props {
title?: string
leftText?: string
rightText?: string
leftArrow?: boolean
fixed?: boolean
placeholder?: boolean
bordered?: boolean
safeAreaInsetTop?: boolean
zIndex?: number
bordered?: boolean
// 自定义点击左侧逻辑,如果不传则默认返回上一页
customBack?: boolean
}
const props = withDefaults(defineProps<Props>(), {
title: '',
leftText: '',
rightText: '',
leftArrow: true,
fixed: true,
placeholder: true,
bordered: true,
safeAreaInsetTop: true,
zIndex: 100
bordered: false,
customBack: false
})
const emit = defineEmits<{
(e: 'click-left'): void
(e: 'click-right'): void
}>()
const emit = defineEmits(['click-left', 'click-right'])
function onClickLeft() {
emit('click-left')
// 默认返回上一页
if (props.leftArrow) {
uni.navigateBack({
fail: () => {
// 如果没有上一页,则跳转到首页
uni.reLaunch({ url: '/pages/index/index' })
}
})
function handleClickLeft() {
if (props.customBack) {
emit('click-left')
} else {
// 默认行为:返回上一页,如果无法返回则去首页
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack()
} else {
uni.reLaunch({ url: '/pages/index/index' })
}
}
}
function onClickRight() {
function handleClickRight() {
emit('click-right')
}
</script>
<style lang="scss">
.app-navbar {
--wot-navbar-bg-color: var(--nav-bg, #ededed);
--wot-navbar-title-color: var(--text-primary, #333);
--wot-navbar-icon-color: var(--text-primary, #333);
<style lang="scss" scoped>
:deep(.app-nav-bar) {
// 适配暗黑模式的背景色和文字颜色
// 假设 src/uni.scss 或 variables.scss 中定义了这些 CSS 变量
background-color: var(--bg-content) !important;
.wd-navbar__title {
color: var(--text-primary) !important;
font-weight: 600;
}
.wd-navbar__text {
color: var(--color-primary) !important;
}
.wd-icon {
color: var(--text-primary) !important;
}
}
// 暗黑模式下的特殊覆盖(如果 CSS 变量不够用)
.dark {
:deep(.app-nav-bar) {
background-color: #1c1c1e !important; // 兜底暗色
.wd-navbar__title {
color: #ffffff !important;
}
}
}
</style>

View File

@@ -1,8 +1,8 @@
<template>
<view class="app-tabbar">
<view class="tabbar-wrapper">
<view
v-for="tab in tabs"
<view
v-for="tab in tabs"
:key="tab.name"
class="tabbar-item"
:class="{ active: current === tab.name }"
@@ -22,7 +22,7 @@
{{ unreadCount > 99 ? '99+' : unreadCount }}
</view>
</template>
<!-- 联系人图标 -->
<template v-else-if="tab.name === 'contacts'">
<svg v-if="current === tab.name" class="icon" viewBox="0 0 24 24" fill="currentColor">
@@ -33,7 +33,7 @@
<circle cx="12" cy="7" r="4"/>
</svg>
</template>
<!-- 朋友圈图标 -->
<template v-else-if="tab.name === 'moments'">
<svg v-if="current === tab.name" class="icon" viewBox="0 0 24 24" fill="currentColor">
@@ -77,7 +77,7 @@ const tabs = [
function switchTab(name: string) {
// 点击当前 tab 不跳转
if (props.current === name) return
const tab = tabs.find(t => t.name === name)
if (tab) {
uni.reLaunch({ url: tab.path })
@@ -111,11 +111,11 @@ function switchTab(name: string) {
gap: 4rpx;
color: #999;
transition: color 0.2s;
&.active {
color: #07c160;
}
&:active {
opacity: 0.7;
}
@@ -128,7 +128,7 @@ function switchTab(name: string) {
display: flex;
align-items: center;
justify-content: center;
.icon {
width: 48rpx;
height: 48rpx;
@@ -156,3 +156,107 @@ function switchTab(name: string) {
line-height: 1;
}
</style>
<!--/*-->
<!--<template>-->
<!-- <view :class="{ dark: isDark }">-->
<!-- <wd-tabbar-->
<!-- :modelValue="current"-->
<!-- fixed-->
<!-- placeholder-->
<!-- safe-area-inset-bottom-->
<!-- bordered-->
<!-- custom-class="app-tab-bar"-->
<!-- @change="handleChange"-->
<!-- >-->
<!-- <wd-tabbar-item-->
<!-- v-for="(item, index) in list"-->
<!-- :key="index"-->
<!-- :name="item.pagePath"-->
<!-- :title="item.text"-->
<!-- :value="item.badge > 0 ? item.badge : null"-->
<!-- :icon="item.icon"-->
<!-- >-->
<!-- </wd-tabbar-item>-->
<!-- </wd-tabbar>-->
<!-- </view>-->
<!--</template>-->
<!--<script setup lang="ts">-->
<!--import { computed } from 'vue'-->
<!--import { useTheme } from '@/composables/useTheme'-->
<!--// 保持原有的 store 引用-->
<!--import { useAppStore } from '@/stores'-->
<!--const props = defineProps<{-->
<!-- active?: string-->
<!--}>()-->
<!--const { isDark } = useTheme()-->
<!--const appStore = useAppStore()-->
<!--// 核心修改:移除 image 路径,改用 Wot-UI 内置 Icon name-->
<!--// 如果需要自定义 SVG可以使用 custom-icon 插槽-->
<!--const list = computed(() => [-->
<!-- {-->
<!-- pagePath: '/pages/index/index',-->
<!-- text: '消息',-->
<!-- icon: 'chat', // 对应 wd-icon name="chat"-->
<!-- badge: appStore.unreadCount || 0-->
<!-- },-->
<!-- {-->
<!-- pagePath: '/pages/contact/index',-->
<!-- text: '通讯录',-->
<!-- icon: 'user-circle', // 对应 wd-icon name="user-circle"-->
<!-- badge: appStore.contactUnread || 0-->
<!-- },-->
<!-- {-->
<!-- pagePath: '/pages/moment/index',-->
<!-- text: '发现',-->
<!-- icon: 'camera', // 对应 wd-icon name="camera"-->
<!-- badge: appStore.momentUnread ? 1 : 0-->
<!-- },-->
<!-- {-->
<!-- pagePath: '/pages/profile/index',-->
<!-- text: '我的',-->
<!-- icon: 'user', // 对应 wd-icon name="user"-->
<!-- badge: 0-->
<!-- }-->
<!--])-->
<!--const current = computed(() => {-->
<!-- if (props.active) return props.active-->
<!-- const pages = getCurrentPages()-->
<!-- const page = pages[pages.length - 1]-->
<!-- return '/' + page.route-->
<!--})-->
<!--function handleChange({ value }: { value: string }) {-->
<!-- uni.switchTab({ url: value })-->
<!--}-->
<!--</script>-->
<!--<style lang="scss" scoped>-->
<!--:deep(.app-tab-bar) {-->
<!-- background-color: var(&#45;&#45;bg-content) !important;-->
<!-- // 覆盖选中态颜色,保持与原有设计一致-->
<!-- .wd-tabbar-item.is-active {-->
<!-- color: var(&#45;&#45;color-primary) !important;-->
<!-- }-->
<!--}-->
<!--.dark {-->
<!-- :deep(.app-tab-bar) {-->
<!-- background-color: #1c1c1e !important;-->
<!-- border-top-color: #333;-->
<!-- .wd-tabbar-item {-->
<!-- color: var(&#45;&#45;text-secondary);-->
<!-- &.is-active {-->
<!-- color: var(&#45;&#45;color-primary);-->
<!-- }-->
<!-- }-->
<!-- }-->
<!--}-->
<!--</style>-->
<!--*/-->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +1,62 @@
<template>
<!-- 根节点绑定 dark class 适配全局暗黑模式 -->
<view class="login-page" :class="{ dark: isDark }">
<!-- 导航栏 -->
<app-nav-bar title="登录" :left-arrow="false" />
<!-- Logo -->
<app-nav-bar title="登录" :left-arrow="false" :bordered="false" />
<!-- Logo 区域 -->
<view class="logo-section">
<view class="logo">
<wd-icon name="chat" size="120rpx" color="#07c160" />
<view class="logo-box">
<wd-icon name="chat" size="60px" color="#07c160" />
</view>
<text class="app-name">NL-IM</text>
<text class="app-desc">即时通讯</text>
<text class="app-desc">即时通讯连接你我</text>
</view>
<!-- 登录表单 -->
<!-- 表单区域 -->
<view class="form-section">
<wd-cell-group border>
<wd-input
v-model="form.account"
label="账号"
placeholder="请输入手机号/邮箱"
clearable
:prefix-icon="'user'"
v-model="form.account"
size="large"
label="账号"
label-width="60px"
placeholder="请输入手机号/邮箱"
clearable
prefix-icon="user"
:placeholder-style="isDark ? 'color: #555' : ''"
/>
<wd-input
v-model="form.password"
label="密码"
type="password"
placeholder="请输入密码"
clearable
show-password
:prefix-icon="'lock'"
v-model="form.password"
size="large"
label="密码"
label-width="60px"
type="password"
placeholder="请输入密码"
clearable
show-password
prefix-icon="lock"
:placeholder-style="isDark ? 'color: #555' : ''"
/>
</wd-cell-group>
<view class="form-options">
<wd-checkbox v-model="form.remember">记住登录</wd-checkbox>
<wd-checkbox v-model="form.remember" shape="square">记住登录</wd-checkbox>
<text class="forgot-link" @click="forgotPassword">忘记密码</text>
</view>
<wd-button
type="primary"
block
:loading="loading"
:disabled="!canLogin"
@click="handleLogin"
>
登录
</wd-button>
<view class="action-buttons">
<wd-button
type="primary"
block
size="large"
:loading="loading"
:disabled="!canLogin"
@click="handleLogin"
>
登录
</wd-button>
</view>
<view class="register-link">
<text>还没有账号</text>
@@ -56,19 +66,16 @@
<!-- 其他登录方式 -->
<view class="other-login">
<view class="divider">
<view class="line" />
<text class="text">其他登录方式</text>
<view class="line" />
</view>
<wd-divider color="var(--text-tertiary)">其他登录方式</wd-divider>
<view class="login-icons">
<view class="login-icon" @click="loginWithWeixin">
<wd-icon name="weixin" size="56rpx" color="#07c160" />
<view class="login-icon-item" hover-class="hover-opacity" @click="loginWithWeixin">
<wd-icon name="weixin" size="32px" color="#07c160" />
</view>
</view>
</view>
<!-- Toast MessageBox -->
<!-- 全局反馈组件 -->
<wd-toast />
<wd-message-box />
</view>
@@ -79,6 +86,8 @@ import { ref, computed } from 'vue'
import { useAuthStore } from '@/stores'
import { useToast, useMessage } from 'wot-design-uni'
import { useTheme } from '@/composables/useTheme'
// 引入 AppNavBar 组件 (通常已自动引入,显式引入以防万一)
import AppNavBar from '@/components/common/AppNavBar.vue'
const authStore = useAuthStore()
const toast = useToast()
@@ -99,7 +108,7 @@ const canLogin = computed(() => {
return form.value.account.trim() && form.value.password.trim()
})
// 登录
// 登录逻辑 (保持原有逻辑不变)
async function handleLogin() {
if (!canLogin.value) return
@@ -121,17 +130,14 @@ async function handleLogin() {
}
}
// 忘记密码
function forgotPassword() {
toast.show('忘记密码功能开发中')
}
// 去注册
function goRegister() {
toast.show('注册功能开发中')
}
// 微信登录
function loginWithWeixin() {
// #ifdef MP-WEIXIN
uni.login({
@@ -157,55 +163,55 @@ function loginWithWeixin() {
min-height: 100vh;
display: flex;
flex-direction: column;
background: var(--bg-page);
background-color: var(--bg-page);
padding: 0 40rpx;
box-sizing: border-box;
transition: background-color 0.3s;
}
// Logo 区域
.logo-section {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 150rpx;
padding-top: 120rpx;
margin-bottom: 80rpx;
.logo {
.logo-box {
width: 160rpx;
height: 160rpx;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-content);
background-color: var(--bg-content);
border-radius: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.05);
margin-bottom: 30rpx;
}
.app-name {
margin-top: 24rpx;
font-size: 48rpx;
font-weight: 700;
color: var(--text-primary);
}
.app-desc {
margin-top: 8rpx;
margin-top: 12rpx;
font-size: 28rpx;
color: var(--text-secondary);
}
}
// 表单区域
.form-section {
background: var(--bg-content);
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 40rpx;
background-color: var(--bg-content);
border-radius: 24rpx;
padding: 40rpx 30rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.02);
.form-options {
display: flex;
align-items: center;
justify-content: space-between;
margin: 30rpx 0;
margin: 30rpx 0 50rpx;
.forgot-link {
font-size: 28rpx;
@@ -213,56 +219,46 @@ function loginWithWeixin() {
}
}
.action-buttons {
margin-bottom: 30rpx;
}
.register-link {
text-align: center;
margin-top: 30rpx;
font-size: 28rpx;
color: var(--text-secondary);
.link {
color: var(--color-primary);
margin-left: 8rpx;
margin-left: 10rpx;
font-weight: 500;
}
}
}
// 其他登录方式
.other-login {
margin-top: auto;
padding-bottom: calc(60rpx + env(safe-area-inset-bottom));
.divider {
display: flex;
align-items: center;
margin-bottom: 40rpx;
.line {
flex: 1;
height: 1rpx;
background: var(--border-color);
}
.text {
padding: 0 24rpx;
font-size: 26rpx;
color: var(--text-tertiary);
}
}
width: 100%;
.login-icons {
display: flex;
justify-content: center;
gap: 60rpx;
margin-top: 40rpx;
.login-icon {
.login-icon-item {
width: 96rpx;
height: 96rpx;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-content);
background-color: var(--bg-content);
border-radius: 50%;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
&.hover-opacity {
opacity: 0.8;
}
}
}
}

View File

@@ -1,115 +1,138 @@
<template>
<view class="profile-page" :class="{ dark: isDark }">
<!-- 导航栏 -->
<app-nav-bar title="个人资料" />
<!-- 头像区域 -->
<view class="header-section">
<view class="avatar-wrap" @click="changeAvatar">
<app-avatar
:src="user?.avatar"
:name="user?.name"
:size="160"
round
<!-- 顶部导航栏 (透明背景或自定义颜色) -->
<app-nav-bar title="" :left-arrow="false" :bordered="false" />
<!-- 用户信息卡片 -->
<view class="user-card" @click="goProfileEdit">
<view class="avatar-wrapper">
<wd-img
:src="userInfo.avatar"
width="120rpx"
height="120rpx"
round
mode="aspectFill"
/>
<view class="change-tip">点击更换</view>
</view>
<view class="info-wrapper">
<view class="name-row">
<text class="nickname">{{ userInfo.nickname || '未设置昵称' }}</text>
<wd-icon name="edit" size="16px" color="var(--text-secondary)" custom-class="edit-icon" />
</view>
<text class="account">账号: {{ userInfo.account || '--' }}</text>
</view>
<view class="arrow">
<wd-icon name="arrow-right" color="var(--text-tertiary)" />
</view>
</view>
<!-- 基本信息 -->
<wd-cell-group title="基本信息">
<wd-cell title="昵称" :value="user?.name || '未设置'" is-link @click="editField('name')" />
<wd-cell title="ID" :value="user?.id || '-'" />
<wd-cell title="个性签名" :value="user?.desc || '未设置'" is-link @click="editField('desc')" />
<wd-cell title="地区" :value="user?.region || '未设置'" is-link @click="editField('region')" />
</wd-cell-group>
<!-- 功能列表 -->
<view class="menu-list">
<wd-cell-group border>
<wd-cell title="我的动态" is-link to="/pages/moment/my" icon="moments" size="large" />
<wd-cell title="收藏夹" is-link to="/pages/collection/index" icon="star" size="large" />
<wd-cell title="相册" is-link to="/pages/album/index" icon="picture" size="large" />
</wd-cell-group>
<!-- 联系方式 -->
<wd-cell-group title="联系方式">
<wd-cell title="手机号" :value="formatPhone(user?.phone)" />
<wd-cell title="邮箱" :value="user?.email || '未绑定'" />
</wd-cell-group>
<view class="gap" />
<wd-toast />
<wd-cell-group border>
<wd-cell title="设置" is-link to="/pages/settings/index" icon="setting" size="large" />
</wd-cell-group>
</view>
<!-- 底部 TabBar -->
<app-tab-bar active="/pages/profile/index" />
</view>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useAuthStore } from '@/stores'
import { useToast } from 'wot-design-uni'
import { useTheme } from '@/composables/useTheme'
import { resolveImageUrl } from '@/utils/image'
import AppAvatar from '@/components/common/AppAvatar.vue'
import { useAuthStore } from '@/stores'
import AppNavBar from '@/components/common/AppNavBar.vue'
import AppTabBar from '@/components/common/AppTabBar.vue'
const authStore = useAuthStore()
const toast = useToast()
const { isDark } = useTheme()
const authStore = useAuthStore()
const user = computed(() => authStore.user)
const userInfo = computed(() => authStore.userInfo || {})
function formatPhone(phone?: string) {
if (!phone) return '未绑定'
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
}
function changeAvatar() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
// TODO: 上传头像
console.log('选择的图片:', res.tempFilePaths[0])
toast.show('头像上传功能开发中')
}
})
}
function editField(field: string) {
const value = field === 'name' ? user.value?.name : field === 'desc' ? user.value?.desc : user.value?.region
uni.navigateTo({
url: `/pages/profile/edit?field=${field}&value=${encodeURIComponent(value || '')}`
})
function goProfileEdit() {
uni.navigateTo({ url: '/pages/profile/edit' })
}
</script>
<style lang="scss" scoped>
.profile-page {
min-height: 100vh;
background: var(--bg-page);
background-color: var(--bg-page);
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
}
.header-section {
.user-card {
display: flex;
justify-content: center;
padding: 60rpx 0;
background: var(--bg-content);
margin-bottom: 20rpx;
}
.avatar-wrap {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 32rpx;
background-color: var(--bg-content);
margin-bottom: 24rpx;
transition: background-color 0.2s;
.avatar-placeholder {
width: 160rpx;
height: 160rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--color-primary);
color: #fff;
font-size: 64rpx;
font-weight: 600;
&:active {
background-color: var(--bg-active);
}
.change-tip {
margin-top: 16rpx;
font-size: 26rpx;
color: var(--text-tertiary);
.avatar-wrapper {
margin-right: 32rpx;
border: 2rpx solid var(--border-color);
border-radius: 50%;
padding: 2rpx; // 模拟头像边框
}
.info-wrapper {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.name-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.nickname {
font-size: 40rpx;
font-weight: 600;
color: var(--text-primary);
margin-right: 12rpx;
}
.edit-icon {
opacity: 0.6;
}
}
.account {
font-size: 28rpx;
color: var(--text-secondary);
}
}
}
.menu-list {
.gap {
height: 24rpx;
}
}
.dark {
.user-card {
background-color: var(--bg-content);
}
:deep(.wd-cell) {
background-color: var(--bg-content);
color: var(--text-primary);
}
}
</style>

View File

@@ -1,29 +1,32 @@
<template>
<view class="settings-page" :class="{ dark: isDark }">
<!-- 导航栏 -->
<app-nav-bar title="设置" />
<wd-cell-group title="账号设置">
<wd-cell title="个人资料" icon="user" is-link @click="goProfile" />
<wd-cell title="账号与安全" icon="shield" is-link @click="goSecurity" />
</wd-cell-group>
<wd-cell-group title="通用设置">
<wd-cell title="消息通知" icon="bell" is-link @click="goNotification" />
<wd-cell title="隐私设置" icon="eye-close" is-link @click="goPrivacy" />
<wd-cell title="深色模式" icon="picture" :value="themeText" is-link @click="goTheme" />
<wd-cell title="清除缓存" icon="delete" :value="cacheSize" is-link @click="clearCache" />
</wd-cell-group>
<view class="content">
<!-- 账号安全 -->
<wd-cell-group title="账号安全" border>
<wd-cell title="账号管理" is-link to="/pages/settings/account" />
<wd-cell title="隐私设置" is-link to="/pages/settings/privacy" />
</wd-cell-group>
<wd-cell-group title="关于">
<wd-cell title="当前版本" value="v1.0.0" />
<wd-cell title="用户协议" is-link @click="goAgreement" />
<wd-cell title="隐私政策" is-link @click="goPrivacyPolicy" />
<wd-cell title="检查更新" is-link @click="checkUpdate" />
</wd-cell-group>
<!-- 通用设置 -->
<wd-cell-group title="通用" border>
<wd-cell title="新消息通知" is-link to="/pages/settings/notification" />
<wd-cell title="主题设置" is-link to="/pages/settings/theme" value="自动" />
<wd-cell title="通用设置" is-link to="/pages/settings/general" />
<wd-cell title="清理缓存" is-link value="12.5MB" clickable @click="handleClearCache" />
</wd-cell-group>
<view class="logout-section">
<wd-button type="error" block plain @click="logout">退出登录</wd-button>
<!-- 关于 -->
<wd-cell-group title="关于" border>
<wd-cell title="帮助与反馈" is-link to="/pages/settings/feedback" />
<wd-cell title="关于我们" is-link to="/pages/settings/about" value="v1.0.0" />
</wd-cell-group>
<!-- 退出登录 -->
<view class="logout-section">
<wd-button type="error" block size="large" @click="handleLogout">退出登录</wd-button>
</view>
</view>
<wd-toast />
@@ -32,128 +35,68 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useAuthStore } from '@/stores'
import { useTheme } from '@/composables/useTheme'
import { useAuthStore } from '@/stores'
import { useToast, useMessage } from 'wot-design-uni'
import AppNavBar from '@/components/common/AppNavBar.vue'
const authStore = useAuthStore()
const { isDark } = useTheme()
const authStore = useAuthStore()
const toast = useToast()
const messageBox = useMessage()
const message = useMessage()
const cacheSize = ref('0 KB')
// 主题显示文本
const themeText = computed(() => {
const mode = uni.getStorageSync('nl_im_theme_mode') || 'system'
const texts: Record<string, string> = {
system: '跟随系统',
light: '浅色模式',
dark: '深色模式'
}
return texts[mode] || '跟随系统'
})
onMounted(() => {
calculateCacheSize()
})
function calculateCacheSize() {
try {
const info = uni.getStorageInfoSync()
const size = info.currentSize || 0
if (size < 1024) {
cacheSize.value = `${size} KB`
} else {
cacheSize.value = `${(size / 1024).toFixed(2)} MB`
function handleLogout() {
message.confirm({
title: '提示',
msg: '确定要退出登录吗?',
success: async () => {
try {
await authStore.logout()
uni.reLaunch({ url: '/pages/login/index' })
} catch (error) {
toast.error('退出失败')
}
}
} catch {
cacheSize.value = '0 KB'
}
})
}
function goProfile() {
uni.navigateTo({ url: '/pages/profile/index' })
}
function goSecurity() {
toast.show('账号安全功能开发中')
}
function goNotification() {
toast.show('消息通知设置开发中')
}
function goPrivacy() {
toast.show('隐私设置功能开发中')
}
function goTheme() {
uni.navigateTo({ url: '/pages/settings/theme' })
}
async function clearCache() {
try {
await messageBox.confirm({
title: '提示',
msg: '确定要清除缓存吗?'
})
// 保留登录信息
const token = uni.getStorageSync('nl_im_token')
const userId = uni.getStorageSync('nl_im_user_id')
const userInfo = uni.getStorageSync('nl_im_user_info')
const theme = uni.getStorageSync('nl_im_theme')
uni.clearStorageSync()
// 恢复登录信息
if (token) uni.setStorageSync('nl_im_token', token)
if (userId) uni.setStorageSync('nl_im_user_id', userId)
if (userInfo) uni.setStorageSync('nl_im_user_info', userInfo)
if (theme) uni.setStorageSync('nl_im_theme', theme)
cacheSize.value = '0 KB'
toast.success('清除成功')
} catch {
// 取消
}
}
function goAgreement() {
toast.show('用户协议页面开发中')
}
function goPrivacyPolicy() {
toast.show('隐私政策页面开发中')
}
function checkUpdate() {
toast.show('当前已是最新版本')
}
async function logout() {
try {
await messageBox.confirm({
title: '提示',
msg: '确定要退出登录吗?'
})
authStore.logout()
} catch {
// 取消
}
function handleClearCache() {
toast.loading('清理中...')
setTimeout(() => {
toast.success('清理完成')
}, 1000)
}
</script>
<style lang="scss" scoped>
.settings-page {
min-height: 100vh;
background: var(--bg-page);
background-color: var(--bg-page);
.content {
padding-bottom: 40rpx;
}
.logout-section {
margin: 60rpx 30rpx 0;
}
}
.logout-section {
padding: 60rpx 30rpx;
padding-bottom: calc(60rpx + env(safe-area-inset-bottom));
// 暗黑模式微调 Cell Group 标题颜色
.dark {
:deep(.wd-cell-group__title) {
color: var(--text-secondary);
background-color: var(--bg-page);
}
:deep(.wd-cell) {
background-color: var(--bg-content);
color: var(--text-primary);
// 覆盖 cell 内部线条颜色 (如果需要)
&::after {
background-color: var(--border-color);
}
}
}
</style>

4
提示词.txt Normal file
View File

@@ -0,0 +1,4 @@
要求:
本项目使用wot-ui并且已经配置好暗色亮色模式的<wd-config-provider theme="dark">,你写的代码要适配wot-ui的暗色浅色模式