Feat: 增加项目和 Prompt 偏好设置
This commit is contained in:
@@ -15,10 +15,11 @@ type Props = {
|
||||
onOpenChange: (next: boolean) => void;
|
||||
languages: string[];
|
||||
path: string;
|
||||
prompt: string | undefined;
|
||||
onConfirm: (result: Record<string, string>) => Promise<void> | void;
|
||||
};
|
||||
|
||||
function AiTranslateModalImpl({ open, onOpenChange, languages, path, onConfirm }: Props) {
|
||||
function AiTranslateModalImpl({ open, onOpenChange, languages, path, prompt, onConfirm }: Props) {
|
||||
const [text, setText] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -37,7 +38,7 @@ function AiTranslateModalImpl({ open, onOpenChange, languages, path, onConfirm }
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const result = await requestTranslations({ text: payload, languages });
|
||||
const result = await requestTranslations({ text: payload, languages, prompt });
|
||||
// 二次校验 keys 完整性
|
||||
for (const l of languages) {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, l)) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import type { Project } from "@/lib/db";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
onOpenChange: (v: boolean) => void;
|
||||
project: Project | null;
|
||||
onSave: (update: { name: string; preferences: { aiPrompt?: string } }) => Promise<void> | void;
|
||||
onDelete: () => Promise<void> | void;
|
||||
};
|
||||
|
||||
function ProjectSettingsModalImpl({ open, onOpenChange, project, onSave, onDelete }: Props) {
|
||||
const [name, setName] = useState("");
|
||||
const [aiPrompt, setAiPrompt] = useState<string>("");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setName(project?.name ?? "");
|
||||
setAiPrompt(project?.preferences?.aiPrompt ?? "");
|
||||
setSaving(false);
|
||||
setErr(null);
|
||||
}
|
||||
}, [open, project]);
|
||||
|
||||
async function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) return setErr("项目名称不能为空");
|
||||
setSaving(true);
|
||||
try {
|
||||
await onSave({
|
||||
name: trimmed,
|
||||
preferences: { aiPrompt },
|
||||
});
|
||||
onOpenChange(false);
|
||||
} catch (e) {
|
||||
setErr((e as Error)?.message ?? "保存失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete() {
|
||||
if (!confirm("确认删除整个项目?该操作不可恢复!")) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
await onDelete();
|
||||
onOpenChange(false);
|
||||
} catch (e) {
|
||||
setErr((e as Error)?.message ?? "删除失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(v) => { if (!saving) onOpenChange(v); }}>
|
||||
<DialogContent className="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>项目设置</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm text-muted-foreground">项目名称</label>
|
||||
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder="输入项目名称" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm text-muted-foreground">翻译偏好设置(AI Prompt)</label>
|
||||
<textarea
|
||||
className="mt-1 w-full min-h-32 rounded-md border border-input bg-transparent px-3 py-2 text-sm outline-none font-mono"
|
||||
placeholder="请输入提供给 AI 的提示词(Prompt),描述风格、用词偏好、命名规则等..."
|
||||
value={aiPrompt}
|
||||
onChange={(e) => setAiPrompt(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{err && <div className="text-sm text-red-600">{err}</div>}
|
||||
<DialogFooter>
|
||||
<Button className="mr-auto" variant="destructive" onClick={handleDelete} disabled={saving}>删除项目</Button>
|
||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)} disabled={saving}>取消</Button>
|
||||
<Button type="submit" disabled={saving}>保存</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export const ProjectSettingsModal = memo(ProjectSettingsModalImpl);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user