diff --git a/frontend/components/SidebarRail.vue b/frontend/components/SidebarRail.vue index 7612e7b..e704e40 100644 --- a/frontend/components/SidebarRail.vue +++ b/frontend/components/SidebarRail.vue @@ -6,7 +6,12 @@
-
+
+
@@ -164,22 +169,116 @@
- -
-
- - - - +
+ +
+
+ +
+ + +
+
+ + + + +
+
+
+
+
+ +
+
+
+
当前账号信息
+ +
+ +
+
正在加载账号信息...
+ + +
+ 仅删除本项目中的该账号解析数据/缓存/编辑记录,不会删除微信客户端中的任何聊天内容或账号数据。 +
+ + +
删除成功后将自动返回引导页。
+ +
{{ accountInfoError }}
+
{{ accountDeleteError }}
@@ -202,9 +301,130 @@ const { privacyMode } = storeToRefs(privacyStore) const realtimeStore = useChatRealtimeStore() const { enabled: realtimeEnabled, available: realtimeAvailable, checking: realtimeChecking, statusError: realtimeStatusError, toggling: realtimeToggling } = storeToRefs(realtimeStore) const { open: settingsDialogOpen, openDialog: openSettingsDialog } = useSettingsDialog() +const { getChatAccountInfo, deleteChatAccount } = useApi() + +const accountDialogOpen = ref(false) +const accountInfoLoading = ref(false) +const accountInfoError = ref('') +const accountInfo = ref(null) +const accountDeleteLoading = ref(false) +const accountDeleteError = ref('') +const accountInfoApiUnsupported = ref(false) +const deleteAccountApiUnsupported = ref(false) + +const sessionUpdatedAtText = computed(() => { + const ts = Number(accountInfo.value?.session_updated_at || 0) + if (!Number.isFinite(ts) || ts <= 0) return '—' + try { + return new Date(ts * 1000).toLocaleString('zh-CN') + } catch { + return '—' + } +}) + +const isNotFoundError = (error) => { + const status = Number( + error?.statusCode + ?? error?.status + ?? error?.response?.status + ?? error?.data?.statusCode + ?? 0 + ) + return status === 404 +} + +const loadAccountInfoByDesktopBridge = async (account) => { + if (!process.client || typeof window === 'undefined') return null + if (!window.wechatDesktop?.getAccountInfo) return null + const res = await window.wechatDesktop.getAccountInfo(account) + return res && typeof res === 'object' ? res : null +} + +const loadAccountInfo = async () => { + accountInfoLoading.value = true + accountInfoError.value = '' + const account = String(selectedAccount.value || '').trim() + if (!account) { + accountInfo.value = null + accountInfoLoading.value = false + return + } + try { + let lastError = null + if (!accountInfoApiUnsupported.value) { + try { + const res = await getChatAccountInfo({ account }) + if (res?.status !== 'success') { + throw new Error(res?.message || '读取账号信息失败') + } + accountInfo.value = res + return + } catch (e) { + lastError = e + if (isNotFoundError(e)) { + accountInfoApiUnsupported.value = true + } + } + } + + try { + const fallback = await loadAccountInfoByDesktopBridge(account) + if (fallback?.status === 'success') { + accountInfo.value = fallback + accountInfoError.value = '' + return + } + if (fallback && fallback?.status && fallback.status !== 'success') { + lastError = new Error(fallback?.message || '读取账号信息失败') + } else if (!lastError) { + lastError = new Error('读取账号信息失败') + } + } catch (fallbackErr) { + if (!lastError) { + lastError = fallbackErr + } + } + + accountInfo.value = null + accountInfoError.value = lastError?.message || '读取账号信息失败' + } finally { + accountInfoLoading.value = false + } +} + +const deleteAccountDataByDesktopBridge = async (account) => { + if (!process.client || typeof window === 'undefined') return null + if (!window.wechatDesktop?.deleteAccountData) return null + const res = await window.wechatDesktop.deleteAccountData(account) + return res && typeof res === 'object' ? res : { status: 'success' } +} + +const openAccountDialog = async () => { + accountDialogOpen.value = true + accountDeleteError.value = '' + await loadAccountInfo() +} + +const closeAccountDialog = () => { + if (accountDeleteLoading.value) return + accountDialogOpen.value = false +} + +watch(selectedAccount, () => { + if (!accountDialogOpen.value) return + void loadAccountInfo() +}) onMounted(async () => { await chatAccounts.ensureLoaded() + if (process.client && typeof window !== 'undefined') { + window.addEventListener('keydown', onWindowKeydown) + } +}) + +onBeforeUnmount(() => { + if (!process.client || typeof window === 'undefined') return + window.removeEventListener('keydown', onWindowKeydown) }) const apiBase = useApiBase() @@ -240,10 +460,73 @@ const goWrapped = async () => { await navigateTo('/wrapped') } +const goGuide = async () => { + await navigateTo('/') +} + const goSettings = () => { openSettingsDialog() } +const onWindowKeydown = (event) => { + if (event?.key !== 'Escape') return + if (!accountDialogOpen.value) return + event.preventDefault() + closeAccountDialog() +} + +const deleteCurrentAccountData = async () => { + const account = String(selectedAccount.value || '').trim() + if (!account || accountDeleteLoading.value) return + + if (process.client && typeof window !== 'undefined') { + const confirmed = window.confirm( + '将删除当前账号在本项目中的数据(解析缓存、编辑记录、导出缓存等),不会删除微信客户端内容。确认删除吗?' + ) + if (!confirmed) return + } + + accountDeleteLoading.value = true + accountDeleteError.value = '' + try { + let deleted = false + let lastError = null + + if (!deleteAccountApiUnsupported.value) { + try { + const apiRes = await deleteChatAccount({ account }) + if (apiRes?.status && apiRes.status !== 'success') { + throw new Error(apiRes?.message || '删除账号数据失败') + } + deleted = true + } catch (apiErr) { + lastError = apiErr + if (isNotFoundError(apiErr)) { + deleteAccountApiUnsupported.value = true + } + } + } + + if (!deleted) { + const desktopRes = await deleteAccountDataByDesktopBridge(account) + if (!desktopRes) { + throw lastError || new Error('删除账号数据失败') + } + if (desktopRes?.status && desktopRes.status !== 'success') { + throw new Error(desktopRes?.message || '删除账号数据失败') + } + } + + accountDialogOpen.value = false + await chatAccounts.ensureLoaded({ force: true }) + await navigateTo('/') + } catch (e) { + accountDeleteError.value = e?.message || '删除账号数据失败' + } finally { + accountDeleteLoading.value = false + } +} + const realtimeBusy = computed(() => !!realtimeChecking.value || !!realtimeToggling.value) const realtimeTitle = computed(() => {