File size: 3,635 Bytes
55847d9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | import { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { FolderOpen, Sparkles } from 'lucide-react'
import { useProjectStore } from '../../stores/project-store'
import { ipc } from '../../services/ipc-client'
import {
Dialog, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription,
} from '../ui/Dialog'
import { Button } from '../ui/Button'
import { Input } from '../ui/Input'
import { Label } from '../ui/Label'
interface NewProjectDialogProps {
open: boolean
onClose: () => void
}
/** 新建项目对话框 */
export default function NewProjectDialog({ open, onClose }: NewProjectDialogProps) {
const { t } = useTranslation('dialogs')
const createProject = useProjectStore((s) => s.createProject)
const [name, setName] = useState('')
const [path, setPath] = useState('')
const [creating, setCreating] = useState(false)
/** 对话框每次打开时重置名称 */
useEffect(() => {
if (!open) return
let mounted = true
Promise.resolve().then(() => { if (mounted) setName('') })
return () => { mounted = false }
}, [open])
/** 选择文件夹 */
const handleSelectFolder = async () => {
const selected = await ipc.invoke('dialog:select-folder')
if (selected) setPath(selected)
}
/** 创建项目(类型/受众留空,在小说配置页面填写) */
const handleCreate = async () => {
if (!name.trim() || !path.trim()) return
setCreating(true)
const success = await createProject({
name: name.trim(),
path: path.trim(),
genre: '',
targetAudience: '',
})
setCreating(false)
if (success) {
onClose()
}
}
return (
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
<DialogContent className="max-w-[420px]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Sparkles size={18} className="text-[var(--color-accent)]" />
{t('newProject.title')}
</DialogTitle>
<DialogDescription>{t('newProject.description')}</DialogDescription>
</DialogHeader>
{/* 表单 */}
<div className="px-5 py-4 space-y-4">
{/* 项目名称 */}
<div>
<Label>{t('newProject.nameLabel')}</Label>
<Input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder={t('newProject.namePlaceholder')}
autoFocus
onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
/>
</div>
{/* 保存路径 */}
<div>
<Label>{t('newProject.pathLabel')}</Label>
<div className="flex gap-2">
<Input
value={path}
onChange={(e) => setPath(e.target.value)}
placeholder={t('newProject.pathPlaceholder')}
className="flex-1"
/>
<Button variant="outline" onClick={handleSelectFolder}>
<FolderOpen size={14} />
{t('newProject.selectFolder')}
</Button>
</div>
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={onClose}>{t('newProject.cancel')}</Button>
<Button
onClick={handleCreate}
disabled={creating || !name.trim() || !path.trim()}
>
<Sparkles size={14} />
{creating ? t('newProject.creating') : t('newProject.createProject')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
|