This commit is contained in:
2025-12-09 22:14:51 +08:00
parent 98af3513b4
commit a102312166
2 changed files with 236 additions and 204 deletions

View File

@@ -91,7 +91,7 @@ function switchTab(name: string) {
bottom: 0;
left: 0;
right: 0;
z-index: 999;
z-index: 1;
background: var(--bg-content, #fff);
border-top: 1rpx solid var(--divider-color, #eee);
padding-bottom: env(safe-area-inset-bottom);

View File

@@ -1,3 +1,150 @@
<script setup lang="ts">
import type { Conversation } from '@/types/conversation'
import { onShow } from '@dcloudio/uni-app'
import { computed, onMounted, ref } from 'vue'
import { useMessage, useToast } from 'wot-design-uni'
import * as conversationApi from '@/api/modules/conversation'
import AppAvatar from '@/components/common/AppAvatar.vue'
import AppTabBar from '@/components/common/AppTabBar.vue'
import { useTheme } from '@/composables/useTheme'
import { useAuthStore, useConversationStore } from '@/stores'
import { formatMessageTime } from '@/utils/format'
const authStore = useAuthStore()
const conversationStore = useConversationStore()
const { isDark } = useTheme()
// --- 保持原有逻辑代码不变 ---
const toast = useToast()
const messageBox = useMessage()
const searchKeyword = ref('')
const showPlusMenu = ref(false)
const showDrawer = ref(false)
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' })
items.push({ name: '删除会话', value: 'delete', color: '#fa5151' })
return items
})
onMounted(() => loadData())
onShow(() => loadData())
async function loadData() {
if (!authStore.isAuthenticated) {
uni.reLaunch({ url: '/pages/login/index' })
return
}
await conversationStore.loadConversations()
}
async function onRefresh() {
refreshing.value = true
await conversationStore.loadConversations()
refreshing.value = false
}
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 || '')}` })
}
function handleLongPress(item: Conversation) { selectedConv.value = item; showConvActions.value = true }
async function onConvActionSelect(action: { value: string }) {
if (!selectedConv.value)
return
const conv = selectedConv.value
showConvActions.value = false
try {
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()
}
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 deleteConversation(item: Conversation) {
try {
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)
toast.success('已删除')
}
catch (e: any) {
if (e !== 'cancel')
toast.error('删除失败')
}
}
function onPlusMenuSelect(item: { value: string }) {
showPlusMenu.value = false
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' }) }
async function logout() {
try { await messageBox.confirm({ title: '提示', msg: '确定退出?' }); showDrawer.value = false; authStore.logout() }
catch {}
}
// --- 保持逻辑代码结束 ---
</script>
<template>
<!--
WotUI 最佳实践
@@ -6,10 +153,9 @@
-->
<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-bg" /> <!-- 独立背景层用于做高斯模糊 -->
<view class="navbar-content">
<view class="navbar-left" @click="openDrawer">
<app-avatar
@@ -21,8 +167,10 @@
/>
</view>
<view class="navbar-title">
<text class="title-text">{{ user?.name || '消息' }}</text>
<view class="online-status" v-if="!loading"></view>
<text class="title-text">
{{ user?.name || '消息' }}
</text>
<view v-if="!loading" class="online-status" />
</view>
<view class="navbar-right">
<view class="icon-btn" @click="showPlusMenu = true">
@@ -36,7 +184,9 @@
<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>
<text class="search-placeholder">
搜索
</text>
</view>
</view>
@@ -98,11 +248,15 @@
{{ item.name || '未知' }}
</text>
</view>
<text class="time">{{ formatMessageTime(item.last_time) }}</text>
<text class="time">
{{ formatMessageTime(item.last_time) }}
</text>
</view>
<view class="row-bottom">
<text class="msg-preview text-ellipsis">{{ item.last_message || '暂无消息' }}</text>
<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>
@@ -123,7 +277,7 @@
</template>
<!-- 底部垫片 -->
<view class="safe-area-spacer"></view>
<view class="safe-area-spacer" />
</scroll-view>
<!-- 弹窗组件保持原有逻辑 -->
@@ -136,8 +290,8 @@
<wd-action-sheet
v-model="showConvActions"
:actions="convActionItems"
@select="onConvActionSelect"
cancel-text="取消"
@select="onConvActionSelect"
/>
<!-- 侧边抽屉 -->
@@ -147,11 +301,15 @@
custom-style="width: 75%; height: 100%; background: var(--bg-surface);"
>
<view class="drawer-container">
<view class="drawer-header-bg"></view>
<view class="drawer-header-bg" />
<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>
<text class="drawer-username">
{{ user?.name || '请登录' }}
</text>
<text class="drawer-bio">
{{ user?.desc || '编辑个签,展示你的独特个性' }}
</text>
</view>
<view class="drawer-menu-list">
@@ -163,7 +321,9 @@
</view>
<view class="drawer-footer-btn">
<wd-button type="info" plain block @click="logout">退出登录</wd-button>
<wd-button type="info" plain block @click="logout">
退出登录
</wd-button>
</view>
</view>
</wd-popup>
@@ -175,135 +335,6 @@
</wd-config-provider>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useAuthStore, useConversationStore } from '@/stores'
import { useTheme } from '@/composables/useTheme'
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'
import AppTabBar from '@/components/common/AppTabBar.vue'
import type { Conversation } from '@/types/conversation'
const authStore = useAuthStore()
const conversationStore = useConversationStore()
const { isDark } = useTheme()
// --- 保持原有逻辑代码不变 ---
const toast = useToast()
const messageBox = useMessage()
const searchKeyword = ref('')
const showPlusMenu = ref(false)
const showDrawer = ref(false)
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' })
items.push({ name: '删除会话', value: 'delete', color: '#fa5151' })
return items
})
onMounted(() => loadData())
onShow(() => loadData())
async function loadData() {
if (!authStore.isAuthenticated) {
uni.reLaunch({ url: '/pages/login/index' })
return
}
await conversationStore.loadConversations()
}
async function onRefresh() {
refreshing.value = true
await conversationStore.loadConversations()
refreshing.value = false
}
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 || '')}` })
}
function handleLongPress(item: Conversation) { selectedConv.value = item; showConvActions.value = true }
async function onConvActionSelect(action: { value: string }) {
if (!selectedConv.value) return
const conv = selectedConv.value
showConvActions.value = false
try {
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()
} 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 deleteConversation(item: Conversation) {
try {
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)
toast.success('已删除')
} catch (e: any) { if(e !== 'cancel') toast.error('删除失败') }
}
function onPlusMenuSelect(item: { value: string }) {
showPlusMenu.value = false
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' }) }
async function logout() {
try { await messageBox.confirm({ title: '提示', msg: '确定退出?' }); showDrawer.value = false; authStore.logout() } catch {}
}
// --- 保持逻辑代码结束 ---
</script>
<style lang="scss" scoped>
/* ----- 语义化 CSS 变量定义 ----- */
.page-container {
@@ -345,7 +376,7 @@ async function logout() {
.custom-navbar {
position: sticky;
top: 0;
z-index: 100;
z-index: 0;
padding-top: var(--status-bar-height);
.navbar-bg {
@@ -402,7 +433,7 @@ async function logout() {
padding: 16rpx 32rpx;
position: sticky;
top: calc(44px + var(--status-bar-height));
z-index: 99;
z-index: 0;
}
.search-inner {
@@ -543,6 +574,7 @@ async function logout() {
display: flex;
flex-direction: column;
position: relative;
z-index: 50000;
overflow: hidden;
}
@@ -559,7 +591,7 @@ async function logout() {
.drawer-info {
position: relative;
z-index: 1;
z-index: 1000;
padding: 100rpx 40rpx 60rpx;
display: flex;
flex-direction: column;