From bca387c54bcf33dd81ee66a8a42ad80bd4bb198f Mon Sep 17 00:00:00 2001 From: xuncha <1658671838@qq.com> Date: Fri, 30 Jan 2026 20:19:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=8A=A8=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SettingsPage.tsx | 121 +++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 33 deletions(-) diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 8f1c78a..edbb8ff 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -96,6 +96,7 @@ function SettingsPage() { const [isClearingAnalyticsCache, setIsClearingAnalyticsCache] = useState(false) const [isClearingImageCache, setIsClearingImageCache] = useState(false) const [isClearingAllCache, setIsClearingAllCache] = useState(false) + const saveTimersRef = useRef>>({}) // 安全设置 state const [authEnabled, setAuthEnabled] = useState(false) @@ -125,6 +126,9 @@ function SettingsPage() { useEffect(() => { loadConfig() loadAppVersion() + return () => { + Object.values(saveTimersRef.current).forEach((timer) => clearTimeout(timer)) + } }, []) // 点击外部关闭下拉框 @@ -334,6 +338,12 @@ function SettingsPage() { imageAesKey: imageAesKey || '' }) + const buildKeysFromInputs = (overrides?: { decryptKey?: string; imageXorKey?: string; imageAesKey?: string }): WxidKeys => ({ + decryptKey: overrides?.decryptKey ?? decryptKey ?? '', + imageXorKey: parseImageXorKey(overrides?.imageXorKey ?? imageXorKey), + imageAesKey: overrides?.imageAesKey ?? imageAesKey ?? '' + }) + const buildKeysFromConfig = (wxidConfig: configService.WxidConfig | null): WxidKeys => ({ decryptKey: wxidConfig?.decryptKey || '', imageXorKey: typeof wxidConfig?.imageXorKey === 'number' ? wxidConfig.imageXorKey : null, @@ -444,7 +454,9 @@ function SettingsPage() { try { const result = await dialog.openFile({ title: '选择微信数据库根目录', properties: ['openDirectory'] }) if (!result.canceled && result.filePaths.length > 0) { - setDbPath(result.filePaths[0]) + const selectedPath = result.filePaths[0] + setDbPath(selectedPath) + await configService.setDbPath(selectedPath) showMessage('已选择数据库目录', true) } } catch (e: any) { @@ -488,7 +500,9 @@ function SettingsPage() { try { const result = await dialog.openFile({ title: '选择缓存目录', properties: ['openDirectory'] }) if (!result.canceled && result.filePaths.length > 0) { - setCachePath(result.filePaths[0]) + const selectedPath = result.filePaths[0] + setCachePath(selectedPath) + await configService.setCachePath(selectedPath) showMessage('已选择缓存目录', true) } } catch (e: any) { @@ -575,12 +589,25 @@ function SettingsPage() { handleAutoGetDbKey() } - // Helper to sync current keys to wxid config - const syncCurrentKeys = async () => { - const keys = buildKeysFromState() + // Debounce config writes to avoid excessive disk IO + const scheduleConfigSave = (key: string, task: () => Promise | void, delay = 300) => { + const timers = saveTimersRef.current + if (timers[key]) { + clearTimeout(timers[key]) + } + timers[key] = setTimeout(() => { + Promise.resolve(task()).catch((e) => { + console.error('保存配置失败:', e) + }) + }, delay) + } + + const syncCurrentKeys = async (options?: { decryptKey?: string; imageXorKey?: string; imageAesKey?: string; wxid?: string }) => { + const keys = buildKeysFromInputs(options) await syncKeysToConfig(keys) - if (wxid) { - await configService.setWxidConfig(wxid, { + const wxidToUse = options?.wxid ?? wxid + if (wxidToUse) { + await configService.setWxidConfig(wxidToUse, { decryptKey: keys.decryptKey, imageXorKey: typeof keys.imageXorKey === 'number' ? keys.imageXorKey : 0, imageAesKey: keys.imageAesKey @@ -804,11 +831,12 @@ function SettingsPage() { type={showDecryptKey ? 'text' : 'password'} placeholder="例如: a1b2c3d4e5f6..." value={decryptKey} - onChange={(e) => setDecryptKey(e.target.value)} - onBlur={async () => { - if (decryptKey && decryptKey.length === 64) { - await syncCurrentKeys() - // showMessage('解密密钥已保存', true) + onChange={(e) => { + const value = e.target.value + setDecryptKey(value) + if (value && value.length === 64) { + scheduleConfigSave('keys', () => syncCurrentKeys({ decryptKey: value })) + // showMessage('解密密钥已保存', true) } }} /> @@ -839,11 +867,14 @@ function SettingsPage() { type="text" placeholder="例如: C:\Users\xxx\Documents\xwechat_files" value={dbPath} - onChange={(e) => setDbPath(e.target.value)} - onBlur={async () => { - if (dbPath) { - await configService.setDbPath(dbPath) - } + onChange={(e) => { + const value = e.target.value + setDbPath(value) + scheduleConfigSave('dbPath', async () => { + if (value) { + await configService.setDbPath(value) + } + }) }} />
@@ -862,12 +893,15 @@ function SettingsPage() { type="text" placeholder="例如: wxid_xxxxxx" value={wxid} - onChange={(e) => setWxid(e.target.value)} - onBlur={async () => { - if (wxid) { - await configService.setMyWxid(wxid) - await syncCurrentKeys() // Sync keys to the new wxid entry - } + onChange={(e) => { + const value = e.target.value + setWxid(value) + scheduleConfigSave('wxid', async () => { + if (value) { + await configService.setMyWxid(value) + await syncCurrentKeys({ wxid: value }) // Sync keys to the new wxid entry + } + }) }} />
@@ -881,8 +915,14 @@ function SettingsPage() { type="text" placeholder="例如: 0xA4" value={imageXorKey} - onChange={(e) => setImageXorKey(e.target.value)} - onBlur={syncCurrentKeys} + onChange={(e) => { + const value = e.target.value + setImageXorKey(value) + const parsed = parseImageXorKey(value) + if (value === '' || parsed !== null) { + scheduleConfigSave('keys', () => syncCurrentKeys({ imageXorKey: value })) + } + }} /> @@ -893,8 +933,11 @@ function SettingsPage() { type="text" placeholder="16 位 AES 密钥" value={imageAesKey} - onChange={(e) => setImageAesKey(e.target.value)} - onBlur={syncCurrentKeys} + onChange={(e) => { + const value = e.target.value + setImageAesKey(value) + scheduleConfigSave('keys', () => syncCurrentKeys({ imageAesKey: value })) + }} /> @@ -1252,14 +1298,23 @@ function SettingsPage() { type="text" placeholder="留空使用默认目录" value={cachePath} - onChange={(e) => setCachePath(e.target.value)} - onBlur={async () => { - await configService.setCachePath(cachePath) + onChange={(e) => { + const value = e.target.value + setCachePath(value) + scheduleConfigSave('cachePath', () => configService.setCachePath(value)) }} />
- +