UI优化
This commit is contained in:
14
components.d.ts
vendored
14
components.d.ts
vendored
@@ -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']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +1,112 @@
|
||||
<template>
|
||||
<view :class="{ dark: isDark }">
|
||||
<wd-navbar
|
||||
:title="title"
|
||||
:left-text="leftText"
|
||||
:right-text="rightText"
|
||||
: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"
|
||||
:bordered="bordered"
|
||||
custom-class="app-nav-bar"
|
||||
@click-left="handleClickLeft"
|
||||
@click-right="handleClickRight"
|
||||
>
|
||||
<template v-if="$slots.left" #left>
|
||||
<!-- 插槽透传 -->
|
||||
<template #left v-if="$slots.left">
|
||||
<slot name="left" />
|
||||
</template>
|
||||
<template v-if="$slots.title" #title>
|
||||
<template #title v-if="$slots.title">
|
||||
<slot name="title" />
|
||||
</template>
|
||||
<template v-if="$slots.right" #right>
|
||||
<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() {
|
||||
function handleClickLeft() {
|
||||
if (props.customBack) {
|
||||
emit('click-left')
|
||||
// 默认返回上一页
|
||||
if (props.leftArrow) {
|
||||
uni.navigateBack({
|
||||
fail: () => {
|
||||
// 如果没有上一页,则跳转到首页
|
||||
} 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>
|
||||
|
||||
@@ -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(--bg-content) !important;-->
|
||||
|
||||
<!-- // 覆盖选中态颜色,保持与原有设计一致-->
|
||||
<!-- .wd-tabbar-item.is-active {-->
|
||||
<!-- color: var(--color-primary) !important;-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
|
||||
<!--.dark {-->
|
||||
<!-- :deep(.app-tab-bar) {-->
|
||||
<!-- background-color: #1c1c1e !important;-->
|
||||
<!-- border-top-color: #333;-->
|
||||
|
||||
<!-- .wd-tabbar-item {-->
|
||||
<!-- color: var(--text-secondary);-->
|
||||
<!-- &.is-active {-->
|
||||
<!-- color: var(--color-primary);-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
<!--</style>-->
|
||||
<!--*/-->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,43 @@
|
||||
<template>
|
||||
<!--
|
||||
WotUI 最佳实践:
|
||||
使用 wd-config-provider 包裹页面以确保组件级暗黑模式生效
|
||||
custom-style 用于注入我们定义的 CSS 变量
|
||||
-->
|
||||
<wd-config-provider :theme="isDark ? 'dark' : 'light'">
|
||||
<view class="page-container" :class="{ dark: isDark }">
|
||||
<!-- 自定义导航栏 -->
|
||||
|
||||
<!-- 沉浸式导航栏 -->
|
||||
<view class="custom-navbar">
|
||||
<view class="navbar-bg"></view> <!-- 独立背景层用于做高斯模糊 -->
|
||||
<view class="navbar-content">
|
||||
<!-- 左侧头像 -->
|
||||
<view class="navbar-left" @click="openDrawer">
|
||||
<app-avatar
|
||||
:src="user?.avatar"
|
||||
:name="user?.name"
|
||||
:size="64"
|
||||
:size="72"
|
||||
round
|
||||
custom-style="border: 2rpx solid var(--border-subtle);"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 标题 -->
|
||||
<view class="navbar-title">{{ user?.name || '消息' }}</view>
|
||||
|
||||
<!-- 右侧加号 -->
|
||||
<view class="navbar-title">
|
||||
<text class="title-text">{{ user?.name || '消息' }}</text>
|
||||
<view class="online-status" v-if="!loading"></view>
|
||||
</view>
|
||||
<view class="navbar-right">
|
||||
<wd-icon name="add" size="48rpx" @click="showPlusMenu = true" />
|
||||
<view class="icon-btn" @click="showPlusMenu = true">
|
||||
<wd-icon name="add" size="44rpx" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<wd-search
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索"
|
||||
disabled
|
||||
hide-cancel
|
||||
@click="goSearch"
|
||||
/>
|
||||
<!-- 搜索区域 (模拟 iOS 风格) -->
|
||||
<view class="search-wrapper">
|
||||
<view class="search-inner" @click="goSearch">
|
||||
<wd-icon name="search" size="32rpx" color="var(--text-tertiary)" />
|
||||
<text class="search-placeholder">搜索</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 会话列表 -->
|
||||
@@ -42,79 +48,91 @@
|
||||
:refresher-triggered="refreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
>
|
||||
<!-- 骨架屏/加载态 (优化体验) -->
|
||||
<view v-if="loading && conversations.length === 0" class="loading-state">
|
||||
<wd-loading />
|
||||
<text>加载中...</text>
|
||||
<wd-loading size="60rpx" color="var(--color-primary)" />
|
||||
<text>同步消息中...</text>
|
||||
</view>
|
||||
|
||||
<wd-status-tip v-else-if="conversations.length === 0" tip="暂无消息" />
|
||||
<wd-status-tip
|
||||
v-else-if="conversations.length === 0"
|
||||
image="message"
|
||||
tip="暂无消息,开始聊天吧"
|
||||
/>
|
||||
|
||||
<template v-else>
|
||||
<!-- 列表渲染 -->
|
||||
<wd-swipe-action
|
||||
v-for="item in conversations"
|
||||
:key="item.id"
|
||||
:disabled="false"
|
||||
>
|
||||
<view
|
||||
class="conversation-item"
|
||||
:class="{ 'is-top': item.is_top, 'is-special': item.is_special_care }"
|
||||
:class="{ 'is-top': item.is_top }"
|
||||
@click="goChat(item)"
|
||||
@longpress="handleLongPress(item)"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<!-- 头像区域 -->
|
||||
<view class="avatar-wrap">
|
||||
<app-avatar
|
||||
:src="item.avatar"
|
||||
:name="item.name"
|
||||
:size="96"
|
||||
radius="8rpx"
|
||||
:size="104"
|
||||
radius="16rpx"
|
||||
:badge="item.is_muted ? 0 : item.unread_count"
|
||||
:dot="item.is_muted && item.unread_count > 0"
|
||||
/>
|
||||
<!-- 特别关心标识 -->
|
||||
<view v-if="item.is_special_care" class="special-badge">
|
||||
<!-- 特别关心采用更现代的角标 -->
|
||||
<view v-if="item.is_special_care" class="special-badge-wrap">
|
||||
<wd-icon name="heart-fill" size="20rpx" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容 -->
|
||||
<view class="conversation-content">
|
||||
<view class="conversation-header">
|
||||
<view class="name-wrap">
|
||||
<!-- 内容区域 -->
|
||||
<view class="content-wrap">
|
||||
<view class="row-top">
|
||||
<view class="title-box">
|
||||
<wd-icon v-if="item.is_top" name="pin" size="24rpx" class="pin-icon" />
|
||||
<text class="conversation-name text-ellipsis" :class="{ 'special-name': item.is_special_care }">{{ item.name || '未知' }}</text>
|
||||
<text class="name text-ellipsis" :class="{ 'special-text': item.is_special_care }">
|
||||
{{ item.name || '未知' }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="conversation-time">{{ formatMessageTime(item.last_time) }}</text>
|
||||
<text class="time">{{ formatMessageTime(item.last_time) }}</text>
|
||||
</view>
|
||||
<view class="conversation-footer">
|
||||
<text class="conversation-msg text-ellipsis">{{ item.last_message || '' }}</text>
|
||||
|
||||
<view class="row-bottom">
|
||||
<text class="msg-preview text-ellipsis">{{ item.last_message || '暂无消息' }}</text>
|
||||
<wd-icon v-if="item.is_muted" name="volume-mute" size="28rpx" class="mute-icon" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右滑操作 -->
|
||||
<!-- 右滑菜单 -->
|
||||
<template #right>
|
||||
<view class="swipe-actions">
|
||||
<view class="swipe-action action-top" @click="toggleTop(item)">
|
||||
{{ item.is_top ? '取消置顶' : '置顶' }}
|
||||
<view class="action-btn top" @click.stop="toggleTop(item)">
|
||||
<wd-icon name="pin" size="36rpx" />
|
||||
</view>
|
||||
<view class="swipe-action action-delete" @click="deleteConversation(item)">
|
||||
删除
|
||||
<view class="action-btn delete" @click.stop="deleteConversation(item)">
|
||||
<wd-icon name="delete" size="36rpx" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-swipe-action>
|
||||
</template>
|
||||
|
||||
<!-- 底部垫片 -->
|
||||
<view class="safe-area-spacer"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 加号菜单 -->
|
||||
<!-- 弹窗组件保持原有逻辑 -->
|
||||
<wd-action-sheet
|
||||
v-model="showPlusMenu"
|
||||
:actions="plusMenuActions"
|
||||
@select="onPlusMenuSelect"
|
||||
/>
|
||||
|
||||
<!-- 会话操作菜单 -->
|
||||
<wd-action-sheet
|
||||
v-model="showConvActions"
|
||||
:actions="convActionItems"
|
||||
@@ -126,43 +144,35 @@
|
||||
<wd-popup
|
||||
v-model="showDrawer"
|
||||
position="left"
|
||||
custom-style="width: 70%; height: 100%; z-index: 1000;"
|
||||
:z-index="1000"
|
||||
custom-style="width: 75%; height: 100%; background: var(--bg-surface);"
|
||||
>
|
||||
<view class="drawer-content">
|
||||
<!-- 用户信息 -->
|
||||
<view class="drawer-header">
|
||||
<app-avatar
|
||||
:src="user?.avatar"
|
||||
:name="user?.name"
|
||||
:size="120"
|
||||
round
|
||||
/>
|
||||
<text class="drawer-name">{{ user?.name || '未登录' }}</text>
|
||||
<text class="drawer-desc">{{ user?.desc || '这个人很懒,什么都没写' }}</text>
|
||||
<view class="drawer-container">
|
||||
<view class="drawer-header-bg"></view>
|
||||
<view class="drawer-info">
|
||||
<app-avatar :src="user?.avatar" :name="user?.name" :size="140" round custom-style="border: 4rpx solid var(--bg-surface);" />
|
||||
<text class="drawer-username">{{ user?.name || '请登录' }}</text>
|
||||
<text class="drawer-bio">{{ user?.desc || '编辑个签,展示你的独特个性' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 菜单列表 -->
|
||||
<view class="drawer-menu">
|
||||
<wd-cell title="个人资料" icon="user" is-link @click="goProfile" />
|
||||
<wd-cell title="设置" icon="setting" is-link @click="goSettings" />
|
||||
<wd-cell title="深色模式" icon="picture" :value="themeText" is-link @click="goTheme" />
|
||||
<view class="drawer-menu-list">
|
||||
<wd-cell-group border>
|
||||
<wd-cell title="个人资料" icon="user" is-link size="large" @click="goProfile" />
|
||||
<wd-cell title="设置" icon="setting" is-link size="large" @click="goSettings" />
|
||||
<wd-cell title="深色模式" icon="picture" :value="themeText" is-link size="large" @click="goTheme" />
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="drawer-footer">
|
||||
<wd-button type="error" block plain @click="logout">退出登录</wd-button>
|
||||
<view class="drawer-footer-btn">
|
||||
<wd-button type="info" plain block @click="logout">退出登录</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
|
||||
<!-- Toast 组件 -->
|
||||
<wd-toast />
|
||||
<wd-message-box />
|
||||
|
||||
<!-- 自定义 TabBar -->
|
||||
<app-tab-bar current="messages" />
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -170,8 +180,7 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { useAuthStore, useConversationStore } from '@/stores'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { formatMessageTime, generateColor } from '@/utils/format'
|
||||
import { resolveImageUrl } from '@/utils/image'
|
||||
import { formatMessageTime } from '@/utils/format'
|
||||
import { useToast, useMessage } from 'wot-design-uni'
|
||||
import * as conversationApi from '@/api/modules/conversation'
|
||||
import AppAvatar from '@/components/common/AppAvatar.vue'
|
||||
@@ -182,20 +191,9 @@ const authStore = useAuthStore()
|
||||
const conversationStore = useConversationStore()
|
||||
const { isDark } = useTheme()
|
||||
|
||||
// 主题显示文本
|
||||
const themeText = computed(() => {
|
||||
const mode = uni.getStorageSync('nl_im_theme_mode') || 'system'
|
||||
const texts: Record<string, string> = {
|
||||
system: '跟随系统',
|
||||
light: '浅色模式',
|
||||
dark: '深色模式'
|
||||
}
|
||||
return texts[mode] || '跟随系统'
|
||||
})
|
||||
// --- 保持原有逻辑代码不变 ---
|
||||
const toast = useToast()
|
||||
const messageBox = useMessage()
|
||||
|
||||
// 状态
|
||||
const searchKeyword = ref('')
|
||||
const showPlusMenu = ref(false)
|
||||
const showDrawer = ref(false)
|
||||
@@ -203,54 +201,39 @@ const refreshing = ref(false)
|
||||
const showConvActions = ref(false)
|
||||
const selectedConv = ref<Conversation | null>(null)
|
||||
|
||||
// 计算属性
|
||||
const user = computed(() => authStore.user)
|
||||
const conversations = computed(() => conversationStore.conversations)
|
||||
const loading = computed(() => conversationStore.loading)
|
||||
|
||||
// 加号菜单选项
|
||||
const themeText = computed(() => {
|
||||
const mode = uni.getStorageSync('nl_im_theme_mode') || 'system'
|
||||
const texts: Record<string, string> = { system: '跟随系统', light: '浅色模式', dark: '深色模式' }
|
||||
return texts[mode] || '跟随系统'
|
||||
})
|
||||
|
||||
const plusMenuActions = [
|
||||
{ name: '创建群聊', value: 'createGroup' },
|
||||
{ name: '添加好友', value: 'addFriend' },
|
||||
{ name: '扫一扫', value: 'scan' }
|
||||
]
|
||||
|
||||
// 会话操作菜单项
|
||||
const convActionItems = computed(() => {
|
||||
if (!selectedConv.value) return []
|
||||
const conv = selectedConv.value
|
||||
const isGroup = conv.is_group || conv.type === 2
|
||||
|
||||
const items: any[] = [
|
||||
{ name: conv.is_top ? '取消置顶' : '置顶会话', value: 'toggleTop' },
|
||||
{ name: conv.is_muted ? '开启提醒' : '消息免打扰', value: 'toggleMute' }
|
||||
]
|
||||
|
||||
// 私聊才显示特别关心
|
||||
if (!isGroup) {
|
||||
items.push({ name: conv.is_special_care ? '取消特别关心' : '特别关心', value: 'toggleSpecial' })
|
||||
}
|
||||
|
||||
// 有未读消息时显示标记已读
|
||||
if (conv.unread_count && conv.unread_count > 0) {
|
||||
items.push({ name: '标记已读', value: 'markRead' })
|
||||
}
|
||||
|
||||
if (!isGroup) items.push({ name: conv.is_special_care ? '取消特别关心' : '特别关心', value: 'toggleSpecial' })
|
||||
if (conv.unread_count && conv.unread_count > 0) items.push({ name: '标记已读', value: 'markRead' })
|
||||
items.push({ name: '删除会话', value: 'delete', color: '#fa5151' })
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
onMounted(() => loadData())
|
||||
onShow(() => loadData())
|
||||
|
||||
onShow(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
// 方法
|
||||
async function loadData() {
|
||||
if (!authStore.isAuthenticated) {
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
@@ -265,460 +248,357 @@ async function onRefresh() {
|
||||
refreshing.value = false
|
||||
}
|
||||
|
||||
function goSearch() {
|
||||
uni.navigateTo({ url: '/pages/search/index' })
|
||||
}
|
||||
|
||||
function goSearch() { uni.navigateTo({ url: '/pages/search/index' }) }
|
||||
function goChat(item: Conversation) {
|
||||
conversationStore.clearUnread(item.target_id)
|
||||
uni.navigateTo({
|
||||
url: `/pages/chat/index?roomId=${item.room_id || ''}&targetId=${item.target_id}&name=${encodeURIComponent(item.name || '')}&avatar=${encodeURIComponent(item.avatar || '')}`
|
||||
})
|
||||
uni.navigateTo({ url: `/pages/chat/index?roomId=${item.room_id || ''}&targetId=${item.target_id}&name=${encodeURIComponent(item.name || '')}&avatar=${encodeURIComponent(item.avatar || '')}` })
|
||||
}
|
||||
|
||||
// 长按会话
|
||||
function handleLongPress(item: Conversation) {
|
||||
selectedConv.value = item
|
||||
showConvActions.value = true
|
||||
}
|
||||
|
||||
// 会话操作菜单选择
|
||||
function handleLongPress(item: Conversation) { selectedConv.value = item; showConvActions.value = true }
|
||||
async function onConvActionSelect(action: { value: string }) {
|
||||
if (!selectedConv.value) return
|
||||
const conv = selectedConv.value
|
||||
|
||||
switch (action.value) {
|
||||
case 'toggleTop':
|
||||
await toggleTop(conv)
|
||||
break
|
||||
case 'toggleMute':
|
||||
await toggleMute(conv)
|
||||
break
|
||||
case 'toggleSpecial':
|
||||
await toggleSpecialCare(conv)
|
||||
break
|
||||
case 'markRead':
|
||||
await markAsRead(conv)
|
||||
break
|
||||
case 'delete':
|
||||
await deleteConversation(conv)
|
||||
break
|
||||
}
|
||||
showConvActions.value = false
|
||||
}
|
||||
|
||||
// 置顶/取消置顶
|
||||
async function toggleTop(item: Conversation) {
|
||||
try {
|
||||
const newValue = !item.is_top
|
||||
await conversationApi.updateConversation({
|
||||
target_id: item.target_id,
|
||||
is_top: newValue
|
||||
})
|
||||
item.is_top = newValue
|
||||
if (action.value === 'toggleTop') {
|
||||
await conversationApi.updateConversation({ target_id: conv.target_id, is_top: !conv.is_top })
|
||||
conv.is_top = !conv.is_top
|
||||
conversationStore.sortConversations()
|
||||
toast.success(newValue ? '已置顶' : '已取消置顶')
|
||||
} catch (e) {
|
||||
toast.error('操作失败')
|
||||
} else if (action.value === 'toggleMute') {
|
||||
await conversationApi.updateConversation({ target_id: conv.target_id, is_muted: !conv.is_muted })
|
||||
conv.is_muted = !conv.is_muted
|
||||
} else if (action.value === 'toggleSpecial') {
|
||||
await conversationApi.updateConversation({ target_id: conv.target_id, is_special_care: !conv.is_special_care })
|
||||
conv.is_special_care = !conv.is_special_care
|
||||
} else if (action.value === 'markRead') {
|
||||
await conversationStore.clearUnread(conv.target_id)
|
||||
} else if (action.value === 'delete') {
|
||||
await deleteConversation(conv)
|
||||
}
|
||||
} catch(e) { toast.error('操作失败') }
|
||||
}
|
||||
|
||||
// 免打扰
|
||||
async function toggleMute(item: Conversation) {
|
||||
try {
|
||||
const newValue = !item.is_muted
|
||||
await conversationApi.updateConversation({
|
||||
target_id: item.target_id,
|
||||
is_muted: newValue
|
||||
})
|
||||
item.is_muted = newValue
|
||||
toast.success(newValue ? '已开启免打扰' : '已开启提醒')
|
||||
} catch (e) {
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 特别关心
|
||||
async function toggleSpecialCare(item: Conversation) {
|
||||
try {
|
||||
const newValue = !item.is_special_care
|
||||
await conversationApi.updateConversation({
|
||||
target_id: item.target_id,
|
||||
is_special_care: newValue
|
||||
})
|
||||
item.is_special_care = newValue
|
||||
toast.success(newValue ? '已设为特别关心' : '已取消特别关心')
|
||||
} catch (e) {
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 标记已读
|
||||
async function markAsRead(item: Conversation) {
|
||||
try {
|
||||
await conversationStore.clearUnread(item.target_id)
|
||||
toast.success('已标记为已读')
|
||||
} catch (e) {
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除会话
|
||||
// 提取删除逻辑
|
||||
async function deleteConversation(item: Conversation) {
|
||||
try {
|
||||
await messageBox.confirm({
|
||||
title: '提示',
|
||||
msg: '确定删除该会话吗?'
|
||||
})
|
||||
await messageBox.confirm({ title: '提示', msg: '确定删除该会话吗?' })
|
||||
await conversationApi.deleteConversation(item.target_id)
|
||||
// 从本地列表移除
|
||||
const index = conversationStore.conversations.findIndex(c => c.id === item.id)
|
||||
if (index > -1) {
|
||||
conversationStore.conversations.splice(index, 1)
|
||||
}
|
||||
if (index > -1) conversationStore.conversations.splice(index, 1)
|
||||
toast.success('已删除')
|
||||
} catch (e: any) {
|
||||
if (e !== 'cancel') {
|
||||
toast.error('删除失败')
|
||||
}
|
||||
}
|
||||
} catch (e: any) { if(e !== 'cancel') toast.error('删除失败') }
|
||||
}
|
||||
|
||||
function onPlusMenuSelect(item: { name: string; value: string }) {
|
||||
function onPlusMenuSelect(item: { value: string }) {
|
||||
showPlusMenu.value = false
|
||||
switch (item.value) {
|
||||
case 'createGroup':
|
||||
uni.navigateTo({ url: '/pages/group/create' })
|
||||
break
|
||||
case 'addFriend':
|
||||
uni.navigateTo({ url: '/pages/contact/add' })
|
||||
break
|
||||
case 'scan':
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
console.log('扫码结果:', res.result)
|
||||
toast.show('扫码成功')
|
||||
},
|
||||
fail: () => {
|
||||
toast.error('扫码失败')
|
||||
}
|
||||
})
|
||||
break
|
||||
}
|
||||
if(item.value === 'createGroup') uni.navigateTo({ url: '/pages/group/create' })
|
||||
else if(item.value === 'addFriend') uni.navigateTo({ url: '/pages/contact/add' })
|
||||
else if(item.value === 'scan') uni.scanCode({ success: () => toast.show('扫码成功'), fail: () => toast.error('扫码失败') })
|
||||
}
|
||||
|
||||
function openDrawer() {
|
||||
showDrawer.value = true
|
||||
}
|
||||
|
||||
function goProfile() {
|
||||
showDrawer.value = false
|
||||
uni.navigateTo({ url: '/pages/profile/index' })
|
||||
}
|
||||
|
||||
function goSettings() {
|
||||
showDrawer.value = false
|
||||
uni.navigateTo({ url: '/pages/settings/index' })
|
||||
}
|
||||
|
||||
function goTheme() {
|
||||
showDrawer.value = false
|
||||
uni.navigateTo({ url: '/pages/settings/theme' })
|
||||
}
|
||||
|
||||
function openDrawer() { showDrawer.value = true }
|
||||
function goProfile() { showDrawer.value = false; uni.navigateTo({ url: '/pages/profile/index' }) }
|
||||
function goSettings() { showDrawer.value = false; uni.navigateTo({ url: '/pages/settings/index' }) }
|
||||
function goTheme() { showDrawer.value = false; uni.navigateTo({ url: '/pages/settings/theme' }) }
|
||||
async function logout() {
|
||||
try {
|
||||
await messageBox.confirm({
|
||||
title: '提示',
|
||||
msg: '确定要退出登录吗?'
|
||||
})
|
||||
showDrawer.value = false
|
||||
authStore.logout()
|
||||
} catch {
|
||||
// 取消
|
||||
}
|
||||
try { await messageBox.confirm({ title: '提示', msg: '确定退出?' }); showDrawer.value = false; authStore.logout() } catch {}
|
||||
}
|
||||
// --- 保持逻辑代码结束 ---
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* ----- 语义化 CSS 变量定义 ----- */
|
||||
.page-container {
|
||||
/* 亮色模式默认值 */
|
||||
--bg-page: #f7f8fa;
|
||||
--bg-surface: #ffffff;
|
||||
--bg-hover: #f5f5f5;
|
||||
--text-primary: #1d1d1f;
|
||||
--text-secondary: #86868b;
|
||||
--text-tertiary: #a1a1a6;
|
||||
--border-subtle: #ebedf0;
|
||||
--nav-bg: rgba(255, 255, 255, 0.85);
|
||||
--color-brand: #4f46e5;
|
||||
--color-danger: #ef4444;
|
||||
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
|
||||
|
||||
min-height: 100vh;
|
||||
background: var(--bg-page);
|
||||
color: var(--text-primary);
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
// 自定义导航栏
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: var(--nav-bg);
|
||||
padding-top: var(--status-bar-height, 0);
|
||||
/* 暗色模式覆盖 */
|
||||
.page-container.dark {
|
||||
--bg-page: #000000;
|
||||
--bg-surface: #1c1c1e;
|
||||
--bg-hover: #2c2c2e;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #98989d;
|
||||
--text-tertiary: #636366;
|
||||
--border-subtle: #38383a;
|
||||
--nav-bg: rgba(28, 28, 30, 0.85);
|
||||
--shadow-sm: none;
|
||||
}
|
||||
|
||||
.navbar-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
/* ----- UI 样式重构 ----- */
|
||||
|
||||
/* 1. 沉浸式导航栏 */
|
||||
.custom-navbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
padding-top: var(--status-bar-height);
|
||||
|
||||
.navbar-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--nav-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.navbar-left {
|
||||
margin-right: 20rpx;
|
||||
.navbar-content {
|
||||
position: relative;
|
||||
height: 44px; /* iOS 标准导航高度 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
flex: 1;
|
||||
font-size: 36rpx;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
|
||||
.online-status {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: #34c759;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
color: var(--text-primary);
|
||||
.icon-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
&:active { background: var(--bg-hover); }
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索栏
|
||||
.search-bar {
|
||||
position: fixed;
|
||||
top: calc(88rpx + var(--status-bar-height, 0));
|
||||
left: 0;
|
||||
right: 0;
|
||||
/* 2. 搜索框 (仿原生) */
|
||||
.search-wrapper {
|
||||
background: var(--bg-page);
|
||||
padding: 16rpx 32rpx;
|
||||
position: sticky;
|
||||
top: calc(44px + var(--status-bar-height));
|
||||
z-index: 99;
|
||||
padding: 10rpx 20rpx;
|
||||
background: var(--nav-bg);
|
||||
}
|
||||
|
||||
// 会话列表
|
||||
.search-inner {
|
||||
height: 72rpx;
|
||||
background: var(--bg-surface);
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 28rpx;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. 列表区域 */
|
||||
.conversation-list {
|
||||
position: fixed;
|
||||
top: calc(88rpx + 80rpx + var(--status-bar-height, 0));
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(100rpx + env(safe-area-inset-bottom));
|
||||
background: var(--bg-content);
|
||||
/* 移除复杂的 calc,利用 Flex 布局或 padding */
|
||||
height: calc(100vh - 44px - var(--status-bar-height) - 104rpx); /* 减去 nav 和 search */
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
background: var(--bg-content);
|
||||
border-bottom: 1rpx solid var(--divider-color);
|
||||
padding: 24rpx 32rpx;
|
||||
background: var(--bg-surface);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
&.is-top {
|
||||
background: rgba(var(--color-primary-rgb, 99, 102, 241), 0.05);
|
||||
background: var(--bg-page); /* 置顶颜色微调 */
|
||||
}
|
||||
|
||||
&.is-special {
|
||||
.conversation-name {
|
||||
color: #ec4899;
|
||||
/* 边框处理:只给内容区加底边框,头像不加,模仿 iOS */
|
||||
.content-wrap {
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
&:last-child .content-wrap {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.special-badge {
|
||||
.special-badge-wrap {
|
||||
position: absolute;
|
||||
bottom: -4rpx;
|
||||
right: -4rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
bottom: -6rpx;
|
||||
right: -6rpx;
|
||||
background: #ec4899;
|
||||
border: 4rpx solid var(--bg-surface);
|
||||
border-radius: 50%;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2rpx solid var(--bg-content);
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
|
||||
&.avatar-sm {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
&.avatar-md {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
&.avatar-lg {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
font-size: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.unread-badge {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
}
|
||||
|
||||
.unread-dot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background: var(--color-primary);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.conversation-content {
|
||||
.content-wrap {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-right: 4rpx;
|
||||
}
|
||||
|
||||
.conversation-header {
|
||||
.row-top, .row-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.name-wrap {
|
||||
.row-top { margin-bottom: 8rpx; }
|
||||
|
||||
.title-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.pin-icon {
|
||||
color: var(--color-primary);
|
||||
color: var(--color-brand);
|
||||
margin-right: 8rpx;
|
||||
flex-shrink: 0;
|
||||
transform: rotate(45deg); /* 让图钉更灵动 */
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-name {
|
||||
flex: 1;
|
||||
.name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
|
||||
&.special-name {
|
||||
color: #ec4899;
|
||||
&.special-text { color: #ec4899; }
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-time {
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
color: var(--text-tertiary);
|
||||
margin-left: 16rpx;
|
||||
font-feature-settings: "tnum"; /* 等宽数字 */
|
||||
}
|
||||
|
||||
.conversation-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.conversation-msg {
|
||||
flex: 1;
|
||||
.msg-preview {
|
||||
font-size: 28rpx;
|
||||
color: var(--text-secondary);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mute-icon {
|
||||
color: var(--text-tertiary);
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
// 滑动操作
|
||||
/* 4. 滑动菜单 */
|
||||
.swipe-actions {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.swipe-action {
|
||||
.action-btn {
|
||||
width: 140rpx;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 150rpx;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
|
||||
&.action-top {
|
||||
background: #c8c8c8;
|
||||
}
|
||||
|
||||
&.action-delete {
|
||||
background: var(--color-danger);
|
||||
}
|
||||
&.top { background: #4f46e5; }
|
||||
&.delete { background: #ef4444; }
|
||||
}
|
||||
|
||||
// 抽屉
|
||||
.drawer-content {
|
||||
/* 5. 抽屉重构 */
|
||||
.drawer-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-page);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.drawer-header {
|
||||
.drawer-header-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
background: linear-gradient(135deg, var(--color-brand), #818cf8);
|
||||
opacity: 0.1;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.drawer-info {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 100rpx 40rpx 60rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40rpx 30rpx 40rpx;
|
||||
padding-top: calc(40rpx + var(--status-bar-height, 44px));
|
||||
background: var(--bg-content);
|
||||
}
|
||||
|
||||
.drawer-name {
|
||||
margin-top: 20rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
.drawer-username {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
margin-top: 24rpx;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-desc {
|
||||
margin-top: 8rpx;
|
||||
.drawer-bio {
|
||||
font-size: 26rpx;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-menu {
|
||||
.drawer-menu-list {
|
||||
flex: 1;
|
||||
margin-top: 20rpx;
|
||||
background: var(--bg-content);
|
||||
background: var(--bg-surface);
|
||||
}
|
||||
|
||||
.drawer-footer {
|
||||
padding: 30rpx;
|
||||
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
|
||||
.drawer-footer-btn {
|
||||
padding: 40rpx;
|
||||
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.safe-area-spacer {
|
||||
height: 150rpx; /* 留出 Tabbar 空间 */
|
||||
}
|
||||
|
||||
// 加载和空状态
|
||||
.loading-state {
|
||||
padding-top: 200rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx;
|
||||
color: var(--text-tertiary);
|
||||
gap: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,52 +1,62 @@
|
||||
<template>
|
||||
<!-- 根节点绑定 dark class 适配全局暗黑模式 -->
|
||||
<view class="login-page" :class="{ dark: isDark }">
|
||||
<!-- 导航栏 -->
|
||||
<app-nav-bar title="登录" :left-arrow="false" />
|
||||
<app-nav-bar title="登录" :left-arrow="false" :bordered="false" />
|
||||
|
||||
<!-- Logo -->
|
||||
<!-- 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"
|
||||
size="large"
|
||||
label="账号"
|
||||
label-width="60px"
|
||||
placeholder="请输入手机号/邮箱"
|
||||
clearable
|
||||
:prefix-icon="'user'"
|
||||
prefix-icon="user"
|
||||
:placeholder-style="isDark ? 'color: #555' : ''"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="form.password"
|
||||
size="large"
|
||||
label="密码"
|
||||
label-width="60px"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
clearable
|
||||
show-password
|
||||
:prefix-icon="'lock'"
|
||||
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>
|
||||
|
||||
<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +1,138 @@
|
||||
<template>
|
||||
<view class="profile-page" :class="{ dark: isDark }">
|
||||
<!-- 导航栏 -->
|
||||
<app-nav-bar title="个人资料" />
|
||||
<!-- 顶部导航栏 (透明背景或自定义颜色) -->
|
||||
<app-nav-bar title="" :left-arrow="false" :bordered="false" />
|
||||
|
||||
<!-- 头像区域 -->
|
||||
<view class="header-section">
|
||||
<view class="avatar-wrap" @click="changeAvatar">
|
||||
<app-avatar
|
||||
:src="user?.avatar"
|
||||
:name="user?.name"
|
||||
:size="160"
|
||||
<!-- 用户信息卡片 -->
|
||||
<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')" />
|
||||
<!-- 功能列表 -->
|
||||
<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 {
|
||||
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;
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-size: 64rpx;
|
||||
font-weight: 600;
|
||||
padding: 40rpx 32rpx;
|
||||
background-color: var(--bg-content);
|
||||
margin-bottom: 24rpx;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&: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>
|
||||
|
||||
@@ -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" />
|
||||
<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="消息通知" 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 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>
|
||||
|
||||
<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 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 plain @click="logout">退出登录</wd-button>
|
||||
<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`
|
||||
}
|
||||
} 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({
|
||||
function handleLogout() {
|
||||
message.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() {
|
||||
msg: '确定要退出登录吗?',
|
||||
success: async () => {
|
||||
try {
|
||||
await messageBox.confirm({
|
||||
title: '提示',
|
||||
msg: '确定要退出登录吗?'
|
||||
})
|
||||
authStore.logout()
|
||||
} catch {
|
||||
// 取消
|
||||
await authStore.logout()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
} catch (error) {
|
||||
toast.error('退出失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user