Merge pull request #9 from xunchahaha/main

fix: 导出时无需再去设置里面选择位置
This commit is contained in:
xuncha
2026-01-10 23:56:03 +08:00
committed by GitHub
4 changed files with 71 additions and 61 deletions

View File

@@ -1,9 +1,9 @@
{ {
"name": "weflow", "name": "weflow",
"version": "1.0.2", "version": "1.0.3",
"description": "WeFlow - 微信聊天记录查看工具", "description": "WeFlow - 微信聊天记录查看工具",
"main": "dist-electron/main.js", "main": "dist-electron/main.js",
"author": "cc", "author": "cc",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build && electron-builder", "build": "tsc && vite build && electron-builder",
@@ -64,7 +64,7 @@
}, },
"nsis": { "nsis": {
"oneClick": false, "oneClick": false,
"differentialPackage":false, "differentialPackage": false,
"allowToChangeInstallationDirectory": true, "allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true, "createDesktopShortcut": true,
"unicode": true, "unicode": true,
@@ -98,4 +98,4 @@
"dist-electron/**/*" "dist-electron/**/*"
] ]
} }
} }

View File

@@ -463,6 +463,43 @@
margin: 8px 0 0; margin: 8px 0 0;
} }
.select-folder-btn {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 16px;
margin-top: 12px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
cursor: pointer;
transition: all 0.2s;
&:hover {
background: var(--bg-hover);
border-color: var(--primary);
color: var(--primary);
svg {
color: var(--primary);
}
}
&:active {
transform: scale(0.98);
}
svg {
color: var(--text-secondary);
transition: color 0.2s;
}
}
.export-action { .export-action {
padding: 20px 24px; padding: 20px 24px;
border-top: 1px solid var(--border-color); border-top: 1px solid var(--border-color);

View File

@@ -362,7 +362,26 @@ function ExportPage() {
<FolderOpen size={16} /> <FolderOpen size={16} />
<span>{exportFolder || '未设置'}</span> <span>{exportFolder || '未设置'}</span>
</div> </div>
<p className="path-hint"></p> <button
className="select-folder-btn"
onClick={async () => {
try {
const result = await window.electronAPI.dialog.openFile({
title: '选择导出目录',
properties: ['openDirectory']
})
if (!result.canceled && result.filePaths.length > 0) {
setExportFolder(result.filePaths[0])
await configService.setExportPath(result.filePaths[0])
}
} catch (e) {
console.error('选择目录失败:', e)
}
}}
>
<FolderOpen size={16} />
<span></span>
</button>
</div> </div>
</div> </div>

View File

@@ -3,19 +3,18 @@ import { useAppStore } from '../stores/appStore'
import { useThemeStore, themes } from '../stores/themeStore' import { useThemeStore, themes } from '../stores/themeStore'
import { dialog } from '../services/ipc' import { dialog } from '../services/ipc'
import * as configService from '../services/config' import * as configService from '../services/config'
import { import {
Eye, EyeOff, FolderSearch, FolderOpen, Search, Copy, Eye, EyeOff, FolderSearch, FolderOpen, Search, Copy,
RotateCcw, Trash2, Save, Plug, Check, Sun, Moon, RotateCcw, Trash2, Save, Plug, Check, Sun, Moon,
Palette, Database, Download, HardDrive, Info, RefreshCw Palette, Database, Download, HardDrive, Info, RefreshCw
} from 'lucide-react' } from 'lucide-react'
import './SettingsPage.scss' import './SettingsPage.scss'
type SettingsTab = 'appearance' | 'database' | 'export' | 'cache' | 'about' type SettingsTab = 'appearance' | 'database' | 'cache' | 'about'
const tabs: { id: SettingsTab; label: string; icon: React.ElementType }[] = [ const tabs: { id: SettingsTab; label: string; icon: React.ElementType }[] = [
{ id: 'appearance', label: '外观', icon: Palette }, { id: 'appearance', label: '外观', icon: Palette },
{ id: 'database', label: '数据库连接', icon: Database }, { id: 'database', label: '数据库连接', icon: Database },
{ id: 'export', label: '导出', icon: Download },
{ id: 'cache', label: '缓存', icon: HardDrive }, { id: 'cache', label: '缓存', icon: HardDrive },
{ id: 'about', label: '关于', icon: Info } { id: 'about', label: '关于', icon: Info }
] ]
@@ -31,10 +30,8 @@ function SettingsPage() {
const [dbPath, setDbPath] = useState('') const [dbPath, setDbPath] = useState('')
const [wxid, setWxid] = useState('') const [wxid, setWxid] = useState('')
const [cachePath, setCachePath] = useState('') const [cachePath, setCachePath] = useState('')
const [exportPath, setExportPath] = useState('')
const [defaultExportPath, setDefaultExportPath] = useState('')
const [logEnabled, setLogEnabled] = useState(false) const [logEnabled, setLogEnabled] = useState(false)
const [isLoading, setIsLoadingState] = useState(false) const [isLoading, setIsLoadingState] = useState(false)
const [isTesting, setIsTesting] = useState(false) const [isTesting, setIsTesting] = useState(false)
const [isDetectingPath, setIsDetectingPath] = useState(false) const [isDetectingPath, setIsDetectingPath] = useState(false)
@@ -53,7 +50,6 @@ function SettingsPage() {
useEffect(() => { useEffect(() => {
loadConfig() loadConfig()
loadDefaultExportPath()
loadAppVersion() loadAppVersion()
}, []) }, [])
@@ -80,12 +76,11 @@ function SettingsPage() {
const savedLogEnabled = await configService.getLogEnabled() const savedLogEnabled = await configService.getLogEnabled()
const savedImageXorKey = await configService.getImageXorKey() const savedImageXorKey = await configService.getImageXorKey()
const savedImageAesKey = await configService.getImageAesKey() const savedImageAesKey = await configService.getImageAesKey()
if (savedKey) setDecryptKey(savedKey) if (savedKey) setDecryptKey(savedKey)
if (savedPath) setDbPath(savedPath) if (savedPath) setDbPath(savedPath)
if (savedWxid) setWxid(savedWxid) if (savedWxid) setWxid(savedWxid)
if (savedCachePath) setCachePath(savedCachePath) if (savedCachePath) setCachePath(savedCachePath)
if (savedExportPath) setExportPath(savedExportPath)
if (savedImageXorKey != null) { if (savedImageXorKey != null) {
setImageXorKey(`0x${savedImageXorKey.toString(16).toUpperCase().padStart(2, '0')}`) setImageXorKey(`0x${savedImageXorKey.toString(16).toUpperCase().padStart(2, '0')}`)
} }
@@ -96,14 +91,7 @@ function SettingsPage() {
} }
} }
const loadDefaultExportPath = async () => {
try {
const downloadsPath = await window.electronAPI.app.getDownloadsPath()
setDefaultExportPath(downloadsPath)
} catch (e) {
console.error('获取默认导出路径失败:', e)
}
}
const loadAppVersion = async () => { const loadAppVersion = async () => {
try { try {
@@ -166,7 +154,7 @@ function SettingsPage() {
setDbPath(result.path) setDbPath(result.path)
await configService.setDbPath(result.path) await configService.setDbPath(result.path)
showMessage(`自动检测成功:${result.path}`, true) showMessage(`自动检测成功:${result.path}`, true)
const wxids = await window.electronAPI.dbPath.scanWxids(result.path) const wxids = await window.electronAPI.dbPath.scanWxids(result.path)
if (wxids.length === 1) { if (wxids.length === 1) {
setWxid(wxids[0].wxid) setWxid(wxids[0].wxid)
@@ -230,18 +218,7 @@ function SettingsPage() {
} }
} }
const handleSelectExportPath = async () => {
try {
const result = await dialog.openFile({ title: '选择导出目录', properties: ['openDirectory'] })
if (!result.canceled && result.filePaths.length > 0) {
setExportPath(result.filePaths[0])
await configService.setExportPath(result.filePaths[0])
showMessage('已设置导出目录', true)
}
} catch (e) {
showMessage('选择目录失败', false)
}
}
const handleAutoGetDbKey = async () => { const handleAutoGetDbKey = async () => {
if (isFetchingDbKey) return if (isFetchingDbKey) return
@@ -303,16 +280,7 @@ function SettingsPage() {
} }
} }
const handleResetExportPath = async () => {
try {
const downloadsPath = await window.electronAPI.app.getDownloadsPath()
setExportPath(downloadsPath)
await configService.setExportPath(downloadsPath)
showMessage('已恢复为下载目录', true)
} catch (e) {
showMessage('恢复默认失败', false)
}
}
const handleTestConnection = async () => { const handleTestConnection = async () => {
if (!dbPath) { showMessage('请先选择数据库目录', false); return } if (!dbPath) { showMessage('请先选择数据库目录', false); return }
@@ -396,7 +364,6 @@ function SettingsPage() {
setDbPath('') setDbPath('')
setWxid('') setWxid('')
setCachePath('') setCachePath('')
setExportPath('')
setLogEnabled(false) setLogEnabled(false)
setDbConnected(false) setDbConnected(false)
await window.electronAPI.window.openOnboardingWindow() await window.electronAPI.window.openOnboardingWindow()
@@ -562,19 +529,7 @@ function SettingsPage() {
</div> </div>
) )
const renderExportTab = () => (
<div className="tab-content">
<div className="form-group">
<label></label>
<span className="form-hint"></span>
<input type="text" placeholder={defaultExportPath || '系统下载目录'} value={exportPath || defaultExportPath} onChange={(e) => setExportPath(e.target.value)} />
<div className="btn-row">
<button className="btn btn-secondary" onClick={handleSelectExportPath}><FolderOpen size={16} /> </button>
<button className="btn btn-secondary" onClick={handleResetExportPath}><RotateCcw size={16} /> </button>
</div>
</div>
</div>
)
const renderCacheTab = () => ( const renderCacheTab = () => (
<div className="tab-content"> <div className="tab-content">
@@ -603,7 +558,7 @@ function SettingsPage() {
<h2 className="about-name">WeFlow</h2> <h2 className="about-name">WeFlow</h2>
<p className="about-slogan">WeFlow</p> <p className="about-slogan">WeFlow</p>
<p className="about-version">v{appVersion || '...'}</p> <p className="about-version">v{appVersion || '...'}</p>
<div className="about-update"> <div className="about-update">
{updateInfo?.hasUpdate ? ( {updateInfo?.hasUpdate ? (
<> <>
@@ -672,7 +627,6 @@ function SettingsPage() {
<div className="settings-body"> <div className="settings-body">
{activeTab === 'appearance' && renderAppearanceTab()} {activeTab === 'appearance' && renderAppearanceTab()}
{activeTab === 'database' && renderDatabaseTab()} {activeTab === 'database' && renderDatabaseTab()}
{activeTab === 'export' && renderExportTab()}
{activeTab === 'cache' && renderCacheTab()} {activeTab === 'cache' && renderCacheTab()}
{activeTab === 'about' && renderAboutTab()} {activeTab === 'about' && renderAboutTab()}
</div> </div>