feat(ui): 新增支持缩放、拖拽及键盘控制的图片预览组件并优化样式,更新安装程序支持 VC++ 运行库自动安装,多账号支持列表选择。

This commit is contained in:
Forrest
2026-01-16 22:16:55 +08:00
parent b7eb19aad6
commit 6eabd707f8
8 changed files with 456 additions and 44 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useRef } from 'react'
import { useAppStore } from '../stores/appStore'
import { useThemeStore, themes } from '../stores/themeStore'
import { dialog } from '../services/ipc'
@@ -6,7 +6,7 @@ import * as configService from '../services/config'
import {
Eye, EyeOff, FolderSearch, FolderOpen, Search, Copy,
RotateCcw, Trash2, Save, Plug, Check, Sun, Moon,
Palette, Database, Download, HardDrive, Info, RefreshCw
Palette, Database, Download, HardDrive, Info, RefreshCw, ChevronDown
} from 'lucide-react'
import './SettingsPage.scss'
@@ -19,6 +19,11 @@ const tabs: { id: SettingsTab; label: string; icon: React.ElementType }[] = [
{ id: 'about', label: '关于', icon: Info }
]
interface WxidOption {
wxid: string
modifiedTime: number
}
function SettingsPage() {
const { setDbConnected, setLoading, reset } = useAppStore()
const { currentTheme, themeMode, setTheme, setThemeMode } = useThemeStore()
@@ -29,6 +34,9 @@ function SettingsPage() {
const [imageAesKey, setImageAesKey] = useState('')
const [dbPath, setDbPath] = useState('')
const [wxid, setWxid] = useState('')
const [wxidOptions, setWxidOptions] = useState<WxidOption[]>([])
const [showWxidSelect, setShowWxidSelect] = useState(false)
const wxidDropdownRef = useRef<HTMLDivElement>(null)
const [cachePath, setCachePath] = useState('')
const [logEnabled, setLogEnabled] = useState(false)
@@ -53,6 +61,17 @@ function SettingsPage() {
loadAppVersion()
}, [])
// 点击外部关闭下拉框
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (showWxidSelect && wxidDropdownRef.current && !wxidDropdownRef.current.contains(e.target as Node)) {
setShowWxidSelect(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [showWxidSelect])
useEffect(() => {
const removeDb = window.electronAPI.key.onDbKeyStatus((payload) => {
setDbKeyStatus(payload.message)
@@ -156,12 +175,14 @@ function SettingsPage() {
showMessage(`自动检测成功:${result.path}`, true)
const wxids = await window.electronAPI.dbPath.scanWxids(result.path)
setWxidOptions(wxids)
if (wxids.length === 1) {
setWxid(wxids[0].wxid)
await configService.setMyWxid(wxids[0].wxid)
showMessage(`已检测到账号:${wxids[0].wxid}`, true)
} else if (wxids.length > 1) {
showMessage(`检测到 ${wxids.length} 个账号,请手动选择`, true)
// 多账号时弹出选择对话框
setShowWxidSelect(true)
}
} else {
showMessage(result.error || '未能自动检测到数据库目录', false)
@@ -192,12 +213,14 @@ function SettingsPage() {
}
try {
const wxids = await window.electronAPI.dbPath.scanWxids(dbPath)
setWxidOptions(wxids)
if (wxids.length === 1) {
setWxid(wxids[0].wxid)
await configService.setMyWxid(wxids[0].wxid)
if (!silent) showMessage(`已检测到账号:${wxids[0].wxid}`, true)
} else if (wxids.length > 1) {
if (!silent) showMessage(`检测到 ${wxids.length} 个账号,请手动选择`, true)
// 多账号时弹出选择对话框
setShowWxidSelect(true)
} else {
if (!silent) showMessage('未检测到账号目录,请检查路径', false)
}
@@ -206,6 +229,13 @@ function SettingsPage() {
}
}
const handleSelectWxid = async (selectedWxid: string) => {
setWxid(selectedWxid)
await configService.setMyWxid(selectedWxid)
setShowWxidSelect(false)
showMessage(`已选择账号:${selectedWxid}`, true)
}
const handleSelectCachePath = async () => {
try {
const result = await dialog.openFile({ title: '选择缓存目录', properties: ['openDirectory'] })
@@ -466,7 +496,38 @@ function SettingsPage() {
<div className="form-group">
<label> wxid</label>
<span className="form-hint"></span>
<input type="text" placeholder="例如: wxid_xxxxxx" value={wxid} onChange={(e) => setWxid(e.target.value)} />
<div className="wxid-input-wrapper" ref={wxidDropdownRef}>
<input
type="text"
placeholder="例如: wxid_xxxxxx"
value={wxid}
onChange={(e) => setWxid(e.target.value)}
/>
<button
type="button"
className={`wxid-dropdown-btn ${showWxidSelect ? 'open' : ''}`}
onClick={() => wxidOptions.length > 0 ? setShowWxidSelect(!showWxidSelect) : handleScanWxid()}
title={wxidOptions.length > 0 ? "选择已检测到的账号" : "扫描账号"}
>
<ChevronDown size={16} />
</button>
{showWxidSelect && wxidOptions.length > 0 && (
<div className="wxid-dropdown">
{wxidOptions.map((opt) => (
<div
key={opt.wxid}
className={`wxid-option ${opt.wxid === wxid ? 'active' : ''}`}
onClick={() => handleSelectWxid(opt.wxid)}
>
<span className="wxid-value">{opt.wxid}</span>
<span className="wxid-time">
{new Date(opt.modifiedTime).toLocaleDateString()}
</span>
</div>
))}
</div>
)}
</div>
<button className="btn btn-secondary btn-sm" onClick={() => handleScanWxid()}><Search size={14} /> wxid</button>
</div>
@@ -604,6 +665,33 @@ function SettingsPage() {
<div className="settings-page">
{message && <div className={`message-toast ${message.success ? 'success' : 'error'}`}>{message.text}</div>}
{/* 多账号选择对话框 */}
{showWxidSelect && wxidOptions.length > 1 && (
<div className="wxid-dialog-overlay" onClick={() => setShowWxidSelect(false)}>
<div className="wxid-dialog" onClick={(e) => e.stopPropagation()}>
<div className="wxid-dialog-header">
<h3></h3>
<p>使</p>
</div>
<div className="wxid-dialog-list">
{wxidOptions.map((opt) => (
<div
key={opt.wxid}
className={`wxid-dialog-item ${opt.wxid === wxid ? 'active' : ''}`}
onClick={() => handleSelectWxid(opt.wxid)}
>
<span className="wxid-id">{opt.wxid}</span>
<span className="wxid-date">: {new Date(opt.modifiedTime).toLocaleString()}</span>
</div>
))}
</div>
<div className="wxid-dialog-footer">
<button className="btn btn-secondary" onClick={() => setShowWxidSelect(false)}></button>
</div>
</div>
</div>
)}
<div className="settings-header">
<h1></h1>
<div className="settings-actions">