Skip to content

Commit 78ace41

Browse files
committed
feat(desktop): desktop settings
1 parent 91e475f commit 78ace41

4 files changed

Lines changed: 274 additions & 6 deletions

File tree

desktop/src/main.cjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,33 @@ function registerWindowIpc() {
382382
const win = getWin(event);
383383
return !!win?.isMaximized();
384384
});
385+
386+
ipcMain.handle("app:getAutoLaunch", () => {
387+
try {
388+
const settings = app.getLoginItemSettings();
389+
return !!(settings?.openAtLogin || settings?.executableWillLaunchAtLogin);
390+
} catch (err) {
391+
logMain(`[main] getAutoLaunch failed: ${err?.message || err}`);
392+
return false;
393+
}
394+
});
395+
396+
ipcMain.handle("app:setAutoLaunch", (_event, enabled) => {
397+
const on = !!enabled;
398+
try {
399+
app.setLoginItemSettings({ openAtLogin: on });
400+
} catch (err) {
401+
logMain(`[main] setAutoLaunch(${on}) failed: ${err?.message || err}`);
402+
return false;
403+
}
404+
405+
try {
406+
const settings = app.getLoginItemSettings();
407+
return !!(settings?.openAtLogin || settings?.executableWillLaunchAtLogin);
408+
} catch {
409+
return on;
410+
}
411+
});
385412
}
386413

387414
async function main() {

desktop/src/preload.cjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ contextBridge.exposeInMainWorld("wechatDesktop", {
55
toggleMaximize: () => ipcRenderer.invoke("window:toggleMaximize"),
66
close: () => ipcRenderer.invoke("window:close"),
77
isMaximized: () => ipcRenderer.invoke("window:isMaximized"),
8-
});
98

9+
getAutoLaunch: () => ipcRenderer.invoke("app:getAutoLaunch"),
10+
setAutoLaunch: (enabled) => ipcRenderer.invoke("app:setAutoLaunch", !!enabled),
11+
});

frontend/pages/chat/[[username]].vue

Lines changed: 218 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@
2525
<circle v-if="!privacyMode" cx="12" cy="12" r="3" />
2626
</svg>
2727
</div>
28+
29+
<!-- 设置按钮(仅桌面端) -->
30+
<div
31+
v-if="isDesktopEnv"
32+
class="w-16 h-12 flex items-center justify-center cursor-pointer transition-colors text-gray-500 hover:text-gray-700"
33+
@click="openDesktopSettings"
34+
title="设置"
35+
>
36+
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
37+
<path
38+
stroke-linecap="round"
39+
stroke-linejoin="round"
40+
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
41+
/>
42+
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
43+
</svg>
44+
</div>
2845
</div>
2946
</div>
3047

@@ -1543,6 +1560,85 @@
15431560
</div>
15441561
</div>
15451562
</div>
1563+
1564+
<!-- 桌面端设置弹窗 -->
1565+
<div
1566+
v-if="desktopSettingsOpen"
1567+
class="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
1568+
@click.self="closeDesktopSettings"
1569+
>
1570+
<div class="w-full max-w-md bg-white rounded-lg shadow-lg">
1571+
<div class="px-5 py-4 border-b border-gray-200 flex items-center justify-between">
1572+
<div class="text-base font-medium text-gray-900">设置</div>
1573+
<button
1574+
type="button"
1575+
class="text-gray-500 hover:text-gray-700"
1576+
@click="closeDesktopSettings"
1577+
title="关闭"
1578+
>
1579+
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1580+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
1581+
</svg>
1582+
</button>
1583+
</div>
1584+
1585+
<div class="px-5 py-4 space-y-4">
1586+
<div class="flex items-center justify-between gap-4">
1587+
<div class="min-w-0">
1588+
<div class="text-sm font-medium text-gray-900">开机自启动</div>
1589+
<div class="text-xs text-gray-500">系统登录后自动启动桌面端</div>
1590+
</div>
1591+
<input
1592+
type="checkbox"
1593+
class="h-4 w-4"
1594+
:checked="desktopAutoLaunch"
1595+
:disabled="desktopAutoLaunchLoading"
1596+
@change="onDesktopAutoLaunchToggle"
1597+
/>
1598+
</div>
1599+
<div v-if="desktopAutoLaunchError" class="text-xs text-red-600 whitespace-pre-wrap">
1600+
{{ desktopAutoLaunchError }}
1601+
</div>
1602+
1603+
<div class="flex items-center justify-between gap-4">
1604+
<div class="min-w-0">
1605+
<div class="text-sm font-medium text-gray-900">启动后自动开启实时获取</div>
1606+
<div class="text-xs text-gray-500">进入聊天页后自动打开“实时开关”(默认关闭)</div>
1607+
</div>
1608+
<input
1609+
type="checkbox"
1610+
class="h-4 w-4"
1611+
:checked="desktopAutoRealtime"
1612+
@change="onDesktopAutoRealtimeToggle"
1613+
/>
1614+
</div>
1615+
1616+
<div class="flex items-center justify-between gap-4">
1617+
<div class="min-w-0">
1618+
<div class="text-sm font-medium text-gray-900">有数据时默认进入聊天页</div>
1619+
<div class="text-xs text-gray-500">有已解密账号时,打开应用默认跳转到 /chat(默认关闭)</div>
1620+
</div>
1621+
<input
1622+
type="checkbox"
1623+
class="h-4 w-4"
1624+
:checked="desktopDefaultToChatWhenData"
1625+
@change="onDesktopDefaultToChatToggle"
1626+
/>
1627+
</div>
1628+
</div>
1629+
1630+
<div class="px-5 py-4 border-t border-gray-200 flex items-center justify-end gap-2">
1631+
<button
1632+
type="button"
1633+
class="text-sm px-3 py-2 rounded-md bg-white border border-gray-200 hover:bg-gray-50"
1634+
@click="closeDesktopSettings"
1635+
>
1636+
关闭
1637+
</button>
1638+
</div>
1639+
</div>
1640+
</div>
1641+
15461642
</div>
15471643
</template>
15481644

@@ -1607,6 +1703,103 @@ const selectedContact = ref(null)
16071703
// 隐私模式
16081704
const privacyMode = ref(false)
16091705

1706+
// 桌面端设置(仅 Electron 环境可见)
1707+
const isDesktopEnv = ref(false)
1708+
const desktopSettingsOpen = ref(false)
1709+
1710+
const DESKTOP_SETTING_AUTO_REALTIME_KEY = 'desktop.settings.autoRealtime'
1711+
const DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY = 'desktop.settings.defaultToChatWhenData'
1712+
1713+
const desktopAutoRealtime = ref(false)
1714+
const desktopDefaultToChatWhenData = ref(false)
1715+
1716+
const desktopAutoLaunch = ref(false)
1717+
const desktopAutoLaunchLoading = ref(false)
1718+
const desktopAutoLaunchError = ref('')
1719+
1720+
const readLocalBool = (key) => {
1721+
if (!process.client) return false
1722+
try {
1723+
return localStorage.getItem(key) === 'true'
1724+
} catch {
1725+
return false
1726+
}
1727+
}
1728+
1729+
const writeLocalBool = (key, value) => {
1730+
if (!process.client) return
1731+
try {
1732+
localStorage.setItem(key, value ? 'true' : 'false')
1733+
} catch {}
1734+
}
1735+
1736+
// 尽量早读本地设置,避免首次加载联系人时拿不到 autoRealtime 选项
1737+
if (process.client) {
1738+
desktopAutoRealtime.value = readLocalBool(DESKTOP_SETTING_AUTO_REALTIME_KEY)
1739+
desktopDefaultToChatWhenData.value = readLocalBool(DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY)
1740+
}
1741+
1742+
const refreshDesktopAutoLaunch = async () => {
1743+
if (!process.client || typeof window === 'undefined') return
1744+
if (!window.wechatDesktop?.getAutoLaunch) return
1745+
desktopAutoLaunchLoading.value = true
1746+
desktopAutoLaunchError.value = ''
1747+
try {
1748+
desktopAutoLaunch.value = !!(await window.wechatDesktop.getAutoLaunch())
1749+
} catch (e) {
1750+
desktopAutoLaunchError.value = e?.message || '读取开机自启动状态失败'
1751+
} finally {
1752+
desktopAutoLaunchLoading.value = false
1753+
}
1754+
}
1755+
1756+
const setDesktopAutoLaunch = async (enabled) => {
1757+
if (!process.client || typeof window === 'undefined') return
1758+
if (!window.wechatDesktop?.setAutoLaunch) return
1759+
desktopAutoLaunchLoading.value = true
1760+
desktopAutoLaunchError.value = ''
1761+
try {
1762+
desktopAutoLaunch.value = !!(await window.wechatDesktop.setAutoLaunch(!!enabled))
1763+
} catch (e) {
1764+
desktopAutoLaunchError.value = e?.message || '设置开机自启动失败'
1765+
await refreshDesktopAutoLaunch()
1766+
} finally {
1767+
desktopAutoLaunchLoading.value = false
1768+
}
1769+
}
1770+
1771+
const openDesktopSettings = async () => {
1772+
desktopSettingsOpen.value = true
1773+
await refreshDesktopAutoLaunch()
1774+
}
1775+
1776+
const closeDesktopSettings = () => {
1777+
desktopSettingsOpen.value = false
1778+
}
1779+
1780+
const onDesktopAutoLaunchToggle = async (ev) => {
1781+
const checked = !!ev?.target?.checked
1782+
await setDesktopAutoLaunch(checked)
1783+
}
1784+
1785+
const onDesktopAutoRealtimeToggle = async (ev) => {
1786+
const checked = !!ev?.target?.checked
1787+
desktopAutoRealtime.value = checked
1788+
writeLocalBool(DESKTOP_SETTING_AUTO_REALTIME_KEY, checked)
1789+
if (checked) {
1790+
// 开启后尝试立即启用实时模式(不可用则静默忽略)
1791+
try {
1792+
await tryEnableRealtimeAuto()
1793+
} catch {}
1794+
}
1795+
}
1796+
1797+
const onDesktopDefaultToChatToggle = (ev) => {
1798+
const checked = !!ev?.target?.checked
1799+
desktopDefaultToChatWhenData.value = checked
1800+
writeLocalBool(DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY, checked)
1801+
}
1802+
16101803
// 联系人数据
16111804
const contacts = ref([])
16121805

@@ -3406,6 +3599,9 @@ const applyRouteSelection = async () => {
34063599

34073600
// 默认选择第一个联系人
34083601
onMounted(() => {
3602+
if (process.client && typeof window !== 'undefined') {
3603+
isDesktopEnv.value = !!window.wechatDesktop
3604+
}
34093605
loadContacts()
34103606
loadSearchHistory()
34113607
})
@@ -3436,6 +3632,8 @@ const loadContacts = async () => {
34363632
} finally {
34373633
isLoadingContacts.value = false
34383634
}
3635+
3636+
await tryEnableRealtimeAuto()
34393637
}
34403638

34413639
const loadSessionsForSelectedAccount = async () => {
@@ -4546,23 +4744,26 @@ const startRealtimeStream = () => {
45464744
}
45474745
}
45484746

4549-
const toggleRealtime = async () => {
4747+
const toggleRealtime = async (opts = {}) => {
4748+
const silent = !!opts?.silent
45504749
if (!process.client || typeof window === 'undefined') return
45514750
if (!selectedAccount.value) return
45524751

45534752
if (!realtimeEnabled.value) {
45544753
await fetchRealtimeStatus()
45554754
if (!realtimeAvailable.value) {
4556-
window.alert(realtimeStatusError.value || '实时模式不可用:缺少密钥或 db_storage 路径。')
4557-
return
4755+
if (!silent) {
4756+
window.alert(realtimeStatusError.value || '实时模式不可用:缺少密钥或 db_storage 路径。')
4757+
}
4758+
return false
45584759
}
45594760
realtimeEnabled.value = true
45604761
startRealtimeStream()
45614762
queueRealtimeSessionsRefresh()
45624763
if (selectedContact.value?.username) {
45634764
await refreshSelectedMessages()
45644765
}
4565-
return
4766+
return true
45664767
}
45674768

45684769
realtimeEnabled.value = false
@@ -4571,6 +4772,19 @@ const toggleRealtime = async () => {
45714772
if (selectedContact.value?.username) {
45724773
await refreshSelectedMessages()
45734774
}
4775+
return true
4776+
}
4777+
4778+
const tryEnableRealtimeAuto = async () => {
4779+
if (!process.client || typeof window === 'undefined') return
4780+
if (!isDesktopEnv.value) return
4781+
if (!desktopAutoRealtime.value) return
4782+
if (realtimeEnabled.value) return
4783+
if (!selectedAccount.value) return
4784+
4785+
try {
4786+
await toggleRealtime({ silent: true })
4787+
} catch {}
45744788
}
45754789

45764790
watch(selectedAccount, async () => {

frontend/pages/index.vue

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,31 @@
5555
</template>
5656

5757
<script setup>
58+
import { onMounted } from 'vue'
59+
import { useApi } from '~/composables/useApi'
60+
61+
const DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY = 'desktop.settings.defaultToChatWhenData'
62+
63+
onMounted(async () => {
64+
if (!process.client || typeof window === 'undefined') return
65+
if (!window.wechatDesktop) return
66+
67+
let enabled = false
68+
try {
69+
enabled = localStorage.getItem(DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY) === 'true'
70+
} catch {}
71+
if (!enabled) return
72+
73+
try {
74+
const api = useApi()
75+
const resp = await api.listChatAccounts()
76+
const accounts = resp?.accounts || []
77+
if (accounts.length) {
78+
await navigateTo('/chat', { replace: true })
79+
}
80+
} catch {}
81+
})
82+
5883
// 开始检测并跳转到结果页面
5984
const startDetection = async () => {
6085
// 直接跳转到检测结果页面,让该页面处理检测
@@ -100,4 +125,4 @@ const startDetection = async () => {
100125
linear-gradient(90deg, rgba(7, 193, 96, 0.1) 1px, transparent 1px);
101126
background-size: 50px 50px;
102127
}
103-
</style>
128+
</style>

0 commit comments

Comments
 (0)