mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-06-29 09:55:56 +08:00
refactor: 抽取 Modal 组件、支持 JSON/CSV 导出、清理冗余代码
- 新增 Modal 组件,消除 3 个 Dialog 中重复的遮罩层/Escape/stopPropagation 代码 - PathEditDialog/HelpDialog/ImportDialog 改用 Modal 包裹 - handleExport 支持 JSON/CSV 两种格式(CSV 导出代码之前存在但从未接线) - App.tsx 移除冗余的 initDarkMode 后重复设 store 的逻辑 - ErrorBoundary 添加 componentDidCatch 日志和 console.error Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
|
||||
interface PathEditDialogProps {
|
||||
open: boolean;
|
||||
@@ -13,61 +14,27 @@ export function PathEditDialog({ open, title, initialValue, onConfirm, onCancel
|
||||
const { t } = useTranslation();
|
||||
const [value, setValue] = useState(initialValue);
|
||||
|
||||
// 每次打开时同步 initialValue(解决 React 复用实例导致空白的问题)
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setValue(initialValue);
|
||||
}
|
||||
}, [open, initialValue]);
|
||||
|
||||
if (!open) return null;
|
||||
useEffect(() => { if (open) setValue(initialValue); }, [open, initialValue]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center"
|
||||
style={{ backgroundColor: 'rgba(0,0,0,0.4)' }}
|
||||
onClick={onCancel}
|
||||
>
|
||||
<div
|
||||
className="rounded-lg p-6 min-w-[400px]"
|
||||
style={{ backgroundColor: 'var(--app-bg)', color: 'var(--app-fg)' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h2 className="text-lg font-semibold mb-4">{title}</h2>
|
||||
<label className="text-sm mb-2 block">{t('dialog.pathLabel')}</label>
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') onConfirm(value);
|
||||
if (e.key === 'Escape') onCancel();
|
||||
}}
|
||||
className="w-full px-3 py-2 rounded border text-sm outline-none"
|
||||
style={{
|
||||
backgroundColor: 'var(--app-list-bg)',
|
||||
color: 'var(--app-fg)',
|
||||
borderColor: 'var(--app-border)',
|
||||
}}
|
||||
/>
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
<button
|
||||
className="px-4 py-1.5 text-sm rounded border"
|
||||
style={{ borderColor: 'var(--app-border)' }}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t('dialog.cancel')}
|
||||
</button>
|
||||
<button
|
||||
className="px-4 py-1.5 text-sm rounded text-white"
|
||||
style={{ backgroundColor: '#2563eb' }}
|
||||
onClick={() => onConfirm(value)}
|
||||
>
|
||||
{t('dialog.confirm')}
|
||||
</button>
|
||||
</div>
|
||||
<Modal open={open} onClose={onCancel}>
|
||||
<h2 className="text-lg font-semibold mb-4">{title}</h2>
|
||||
<label className="text-sm mb-2 block">{t('dialog.pathLabel')}</label>
|
||||
<input
|
||||
type="text" autoFocus value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') onConfirm(value); }}
|
||||
className="w-full min-w-[400px] px-3 py-2 rounded border text-sm outline-none"
|
||||
style={{ backgroundColor: 'var(--app-list-bg)', color: 'var(--app-fg)', borderColor: 'var(--app-border)' }}
|
||||
/>
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
<button className="px-4 py-1.5 text-sm rounded border" style={{ borderColor: 'var(--app-border)' }} onClick={onCancel}>
|
||||
{t('dialog.cancel')}
|
||||
</button>
|
||||
<button className="px-4 py-1.5 text-sm rounded text-white" style={{ backgroundColor: '#2563eb' }} onClick={() => onConfirm(value)}>
|
||||
{t('dialog.confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user