diff --git a/desktop/src/main.cjs b/desktop/src/main.cjs index 8616cf2..753601e 100644 --- a/desktop/src/main.cjs +++ b/desktop/src/main.cjs @@ -382,6 +382,33 @@ function registerWindowIpc() { const win = getWin(event); return !!win?.isMaximized(); }); + + ipcMain.handle("app:getAutoLaunch", () => { + try { + const settings = app.getLoginItemSettings(); + return !!(settings?.openAtLogin || settings?.executableWillLaunchAtLogin); + } catch (err) { + logMain(`[main] getAutoLaunch failed: ${err?.message || err}`); + return false; + } + }); + + ipcMain.handle("app:setAutoLaunch", (_event, enabled) => { + const on = !!enabled; + try { + app.setLoginItemSettings({ openAtLogin: on }); + } catch (err) { + logMain(`[main] setAutoLaunch(${on}) failed: ${err?.message || err}`); + return false; + } + + try { + const settings = app.getLoginItemSettings(); + return !!(settings?.openAtLogin || settings?.executableWillLaunchAtLogin); + } catch { + return on; + } + }); } async function main() { diff --git a/desktop/src/preload.cjs b/desktop/src/preload.cjs index bbb8bca..39681c4 100644 --- a/desktop/src/preload.cjs +++ b/desktop/src/preload.cjs @@ -5,5 +5,7 @@ contextBridge.exposeInMainWorld("wechatDesktop", { toggleMaximize: () => ipcRenderer.invoke("window:toggleMaximize"), close: () => ipcRenderer.invoke("window:close"), isMaximized: () => ipcRenderer.invoke("window:isMaximized"), -}); + getAutoLaunch: () => ipcRenderer.invoke("app:getAutoLaunch"), + setAutoLaunch: (enabled) => ipcRenderer.invoke("app:setAutoLaunch", !!enabled), +}); diff --git a/frontend/pages/chat/[[username]].vue b/frontend/pages/chat/[[username]].vue index 5182983..07ada82 100644 --- a/frontend/pages/chat/[[username]].vue +++ b/frontend/pages/chat/[[username]].vue @@ -25,6 +25,23 @@ + + +
+ + + + +
@@ -1543,6 +1560,85 @@ + + +
+
+
+
设置
+ +
+ +
+
+
+
开机自启动
+
系统登录后自动启动桌面端
+
+ +
+
+ {{ desktopAutoLaunchError }} +
+ +
+
+
启动后自动开启实时获取
+
进入聊天页后自动打开“实时开关”(默认关闭)
+
+ +
+ +
+
+
有数据时默认进入聊天页
+
有已解密账号时,打开应用默认跳转到 /chat(默认关闭)
+
+ +
+
+ +
+ +
+
+
+ @@ -1607,6 +1703,103 @@ const selectedContact = ref(null) // 隐私模式 const privacyMode = ref(false) +// 桌面端设置(仅 Electron 环境可见) +const isDesktopEnv = ref(false) +const desktopSettingsOpen = ref(false) + +const DESKTOP_SETTING_AUTO_REALTIME_KEY = 'desktop.settings.autoRealtime' +const DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY = 'desktop.settings.defaultToChatWhenData' + +const desktopAutoRealtime = ref(false) +const desktopDefaultToChatWhenData = ref(false) + +const desktopAutoLaunch = ref(false) +const desktopAutoLaunchLoading = ref(false) +const desktopAutoLaunchError = ref('') + +const readLocalBool = (key) => { + if (!process.client) return false + try { + return localStorage.getItem(key) === 'true' + } catch { + return false + } +} + +const writeLocalBool = (key, value) => { + if (!process.client) return + try { + localStorage.setItem(key, value ? 'true' : 'false') + } catch {} +} + +// 尽量早读本地设置,避免首次加载联系人时拿不到 autoRealtime 选项 +if (process.client) { + desktopAutoRealtime.value = readLocalBool(DESKTOP_SETTING_AUTO_REALTIME_KEY) + desktopDefaultToChatWhenData.value = readLocalBool(DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY) +} + +const refreshDesktopAutoLaunch = async () => { + if (!process.client || typeof window === 'undefined') return + if (!window.wechatDesktop?.getAutoLaunch) return + desktopAutoLaunchLoading.value = true + desktopAutoLaunchError.value = '' + try { + desktopAutoLaunch.value = !!(await window.wechatDesktop.getAutoLaunch()) + } catch (e) { + desktopAutoLaunchError.value = e?.message || '读取开机自启动状态失败' + } finally { + desktopAutoLaunchLoading.value = false + } +} + +const setDesktopAutoLaunch = async (enabled) => { + if (!process.client || typeof window === 'undefined') return + if (!window.wechatDesktop?.setAutoLaunch) return + desktopAutoLaunchLoading.value = true + desktopAutoLaunchError.value = '' + try { + desktopAutoLaunch.value = !!(await window.wechatDesktop.setAutoLaunch(!!enabled)) + } catch (e) { + desktopAutoLaunchError.value = e?.message || '设置开机自启动失败' + await refreshDesktopAutoLaunch() + } finally { + desktopAutoLaunchLoading.value = false + } +} + +const openDesktopSettings = async () => { + desktopSettingsOpen.value = true + await refreshDesktopAutoLaunch() +} + +const closeDesktopSettings = () => { + desktopSettingsOpen.value = false +} + +const onDesktopAutoLaunchToggle = async (ev) => { + const checked = !!ev?.target?.checked + await setDesktopAutoLaunch(checked) +} + +const onDesktopAutoRealtimeToggle = async (ev) => { + const checked = !!ev?.target?.checked + desktopAutoRealtime.value = checked + writeLocalBool(DESKTOP_SETTING_AUTO_REALTIME_KEY, checked) + if (checked) { + // 开启后尝试立即启用实时模式(不可用则静默忽略) + try { + await tryEnableRealtimeAuto() + } catch {} + } +} + +const onDesktopDefaultToChatToggle = (ev) => { + const checked = !!ev?.target?.checked + desktopDefaultToChatWhenData.value = checked + writeLocalBool(DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY, checked) +} + // 联系人数据 const contacts = ref([]) @@ -3406,6 +3599,9 @@ const applyRouteSelection = async () => { // 默认选择第一个联系人 onMounted(() => { + if (process.client && typeof window !== 'undefined') { + isDesktopEnv.value = !!window.wechatDesktop + } loadContacts() loadSearchHistory() }) @@ -3436,6 +3632,8 @@ const loadContacts = async () => { } finally { isLoadingContacts.value = false } + + await tryEnableRealtimeAuto() } const loadSessionsForSelectedAccount = async () => { @@ -4546,15 +4744,18 @@ const startRealtimeStream = () => { } } -const toggleRealtime = async () => { +const toggleRealtime = async (opts = {}) => { + const silent = !!opts?.silent if (!process.client || typeof window === 'undefined') return if (!selectedAccount.value) return if (!realtimeEnabled.value) { await fetchRealtimeStatus() if (!realtimeAvailable.value) { - window.alert(realtimeStatusError.value || '实时模式不可用:缺少密钥或 db_storage 路径。') - return + if (!silent) { + window.alert(realtimeStatusError.value || '实时模式不可用:缺少密钥或 db_storage 路径。') + } + return false } realtimeEnabled.value = true startRealtimeStream() @@ -4562,7 +4763,7 @@ const toggleRealtime = async () => { if (selectedContact.value?.username) { await refreshSelectedMessages() } - return + return true } realtimeEnabled.value = false @@ -4571,6 +4772,19 @@ const toggleRealtime = async () => { if (selectedContact.value?.username) { await refreshSelectedMessages() } + return true +} + +const tryEnableRealtimeAuto = async () => { + if (!process.client || typeof window === 'undefined') return + if (!isDesktopEnv.value) return + if (!desktopAutoRealtime.value) return + if (realtimeEnabled.value) return + if (!selectedAccount.value) return + + try { + await toggleRealtime({ silent: true }) + } catch {} } watch(selectedAccount, async () => { diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue index 068dd7f..cf06df5 100644 --- a/frontend/pages/index.vue +++ b/frontend/pages/index.vue @@ -55,6 +55,31 @@