-
+
{{ (c.name || c.username || '?').charAt(0) }}
-
+
{{ c.name }}
{{ c.isGroup ? '(群)' : '' }}
{{ c.username }}
-
+
无匹配会话
diff --git a/frontend/composables/chat/useChatExport.js b/frontend/composables/chat/useChatExport.js
index bad2c48..5843f76 100644
--- a/frontend/composables/chat/useChatExport.js
+++ b/frontend/composables/chat/useChatExport.js
@@ -73,20 +73,35 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
return Math.round(clamp01(done / total) * 100)
})
- const exportFilteredContacts = computed(() => {
- const query = String(exportSearchQuery.value || '').trim().toLowerCase()
+ const normalizeExportSelectedUsernames = (list) => {
+ const seen = new Set()
+ return (Array.isArray(list) ? list : []).reduce((acc, item) => {
+ const username = String(item || '').trim()
+ if (!username || seen.has(username)) return acc
+ seen.add(username)
+ acc.push(username)
+ return acc
+ }, [])
+ }
+
+ const getExportFilteredContacts = ({ tab = exportListTab.value, query = exportSearchQuery.value } = {}) => {
+ const normalizedQuery = String(query || '').trim().toLowerCase()
let list = Array.isArray(contacts.value) ? contacts.value : []
- const tab = String(exportListTab.value || 'all')
- if (tab === 'groups') list = list.filter((contact) => !!contact?.isGroup)
- if (tab === 'singles') list = list.filter((contact) => !contact?.isGroup)
+ const normalizedTab = String(tab || 'all')
+ if (normalizedTab === 'groups') list = list.filter((contact) => !!contact?.isGroup)
+ if (normalizedTab === 'singles') list = list.filter((contact) => !contact?.isGroup)
- if (!query) return list
+ if (!normalizedQuery) return list
return list.filter((contact) => {
const name = String(contact?.name || '').toLowerCase()
const username = String(contact?.username || '').toLowerCase()
- return name.includes(query) || username.includes(query)
+ return name.includes(normalizedQuery) || username.includes(normalizedQuery)
})
+ }
+
+ const exportFilteredContacts = computed(() => {
+ return getExportFilteredContacts()
})
const exportContactCounts = computed(() => {
@@ -96,6 +111,60 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
return { total, groups, singles: total - groups }
})
+ const exportSelectedUsernameSet = computed(() => {
+ return new Set(normalizeExportSelectedUsernames(exportSelectedUsernames.value))
+ })
+
+ const setExportSelectedUsernames = (list) => {
+ exportSelectedUsernames.value = normalizeExportSelectedUsernames(list)
+ }
+
+ const getExportFilteredUsernames = (tab = exportListTab.value) => {
+ return getExportFilteredContacts({ tab })
+ .map((contact) => String(contact?.username || '').trim())
+ .filter(Boolean)
+ }
+
+ const selectExportFilteredContacts = (tab = exportListTab.value) => {
+ setExportSelectedUsernames(getExportFilteredUsernames(tab))
+ }
+
+ const clearExportFilteredContacts = () => {
+ setExportSelectedUsernames([])
+ }
+
+ const areExportFilteredContactsAllSelected = (tab = exportListTab.value) => {
+ const usernames = getExportFilteredUsernames(tab)
+ if (usernames.length !== exportSelectedUsernameSet.value.size) return false
+ return usernames.every((username) => exportSelectedUsernameSet.value.has(username))
+ }
+
+ const onExportListTabClick = (tab) => {
+ const nextTab = String(tab || 'all')
+ const isSameTab = String(exportListTab.value || 'all') === nextTab
+ exportListTab.value = nextTab
+
+ if (isSameTab) {
+ if (areExportFilteredContactsAllSelected(nextTab)) {
+ clearExportFilteredContacts(nextTab)
+ } else {
+ selectExportFilteredContacts(nextTab)
+ }
+ return
+ }
+
+ selectExportFilteredContacts(nextTab)
+ }
+
+ const isExportContactSelected = (username) => {
+ return exportSelectedUsernameSet.value.has(String(username || '').trim())
+ }
+
+ const onExportBatchScopeClick = (tab) => {
+ exportScope.value = 'selected'
+ onExportListTabClick(tab)
+ }
+
const isDesktopExportRuntime = () => {
return !!(process.client && window?.wechatDesktop?.chooseDirectory)
}
@@ -269,12 +338,17 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
exportModalOpen.value = true
exportError.value = ''
exportSaveMsg.value = ''
+ exportSearchQuery.value = ''
exportListTab.value = 'all'
+ exportSelectedUsernames.value = []
exportStartLocal.value = ''
exportEndLocal.value = ''
exportMessageTypes.value = exportMessageTypeOptions.map((item) => item.value)
exportAutoSavedFor.value = ''
- exportScope.value = selectedContact.value?.username ? 'current' : 'all'
+ exportScope.value = selectedContact.value?.username ? 'current' : 'selected'
+ if (!selectedContact.value?.username) {
+ selectExportFilteredContacts('all')
+ }
}
const closeExportModal = () => {
@@ -296,6 +370,12 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
}
})
+ watch(exportScope, (scope, previousScope) => {
+ if (scope !== 'selected' || previousScope === 'selected') return
+ if (exportSelectedUsernames.value.length > 0) return
+ selectExportFilteredContacts(exportListTab.value)
+ })
+
watch(
() => ({
exportId: String(exportJob.value?.exportId || ''),
@@ -447,6 +527,9 @@ export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selecte
exportCurrentPercent,
exportFilteredContacts,
exportContactCounts,
+ onExportBatchScopeClick,
+ onExportListTabClick,
+ isExportContactSelected,
hasWebExportFolder,
chooseExportFolder,
getExportDownloadUrl,
From e507024ef658788bb9effda2300cfbee3dffae5c Mon Sep 17 00:00:00 2001
From: 2977094657 <2977094657@qq.com>
Date: Sun, 22 Mar 2026 15:34:05 +0800
Subject: [PATCH 04/39] =?UTF-8?q?feat(ui):=20=E6=96=B0=E5=A2=9E=E6=B5=85?=
=?UTF-8?q?=E6=B7=B1=E8=89=B2=E4=B8=BB=E9=A2=98=E5=88=87=E6=8D=A2=E5=B9=B6?=
=?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=95=8C=E9=9D=A2=E9=85=8D=E8=89=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增主题 store 与本地持久化能力,支持侧边栏切换浅色/深色模式
- 将聊天页、会话列表、标题栏、弹窗等配色改为 CSS 变量统一管理
- 适配定位卡片、引用气泡、系统提示等聊天消息组件在不同主题下的可读性
- 同步整理首页、解密页、联系人页、朋友圈页等页面背景与交互样式
---
frontend/app.vue | 20 +-
frontend/assets/css/chat.css | 81 +-
frontend/assets/css/tailwind.css | 1581 +++++++++++++++++
frontend/components/ApiStatus.vue | 5 +-
frontend/components/ChatLocationCard.vue | 25 +-
frontend/components/DesktopTitleBar.vue | 12 +-
frontend/components/DesktopUpdateDialog.vue | 2 +-
frontend/components/SettingsDialog.vue | 4 +-
frontend/components/SidebarRail.vue | 131 +-
frontend/components/chat/ChatOverlays.vue | 28 +-
frontend/components/chat/ConversationPane.vue | 12 +-
frontend/components/chat/MessageContent.vue | 2 +-
frontend/components/chat/MessageItem.vue | 6 +-
frontend/components/chat/MessageList.vue | 8 +-
frontend/components/chat/SessionListPanel.vue | 31 +-
frontend/lib/ui-theme.js | 36 +
frontend/pages/chat/[[username]].vue | 8 +-
frontend/pages/contacts.vue | 8 +-
frontend/pages/decrypt-result.vue | 4 +-
frontend/pages/decrypt.vue | 2 +-
frontend/pages/detection-result.vue | 2 +-
frontend/pages/edits/[[username]].vue | 6 +-
frontend/pages/index.vue | 2 +-
frontend/pages/sns.vue | 6 +-
frontend/pages/wrapped/index.vue | 7 +-
frontend/stores/theme.js | 46 +
26 files changed, 1928 insertions(+), 147 deletions(-)
create mode 100644 frontend/lib/ui-theme.js
create mode 100644 frontend/stores/theme.js
diff --git a/frontend/app.vue b/frontend/app.vue
index ea71c78..bfa818a 100644
--- a/frontend/app.vue
+++ b/frontend/app.vue
@@ -30,12 +30,18 @@
+
+
diff --git a/frontend/components/chat/ChatOverlays.vue b/frontend/components/chat/ChatOverlays.vue
index efd53e2..98f6131 100644
--- a/frontend/components/chat/ChatOverlays.vue
+++ b/frontend/components/chat/ChatOverlays.vue
@@ -826,51 +826,51 @@
-
+
-
-
+
+
没有可显示的聊天记录
{{ rec.senderDisplayName || rec.sourcename }}
-
+
{{ rec.fullTime || rec.sourcetime }}
@@ -1273,7 +1273,7 @@
-
+
导出聊天记录(离线 ZIP)