169 lines
5.8 KiB
TypeScript
169 lines
5.8 KiB
TypeScript
"use client";
|
||
|
||
import {useEffect, useState} from "react";
|
||
import {useToast} from "@/components/ui/toast";
|
||
import {Loader2} from "lucide-react";
|
||
import {Button} from "@/components/ui/button";
|
||
|
||
interface ExportConfig {
|
||
untranslatedColor: string;
|
||
orientation: "portrait" | "landscape";
|
||
}
|
||
|
||
const defaultExportConfig: ExportConfig = {
|
||
untranslatedColor: "CC0000",
|
||
orientation: "landscape",
|
||
};
|
||
|
||
export default function ExportPage() {
|
||
const [config, setConfig] = useState<ExportConfig>(defaultExportConfig);
|
||
const [loading, setLoading] = useState(true);
|
||
const [saving, setSaving] = useState(false);
|
||
const { addToast } = useToast();
|
||
|
||
useEffect(() => {
|
||
fetch("/api/config")
|
||
.then((res) => res.json())
|
||
.then((result) => {
|
||
if (result.success && result.data) {
|
||
setConfig({
|
||
untranslatedColor: result.data.export?.untranslatedColor || defaultExportConfig.untranslatedColor,
|
||
orientation: result.data.export?.orientation || defaultExportConfig.orientation,
|
||
});
|
||
}
|
||
})
|
||
.catch(console.error)
|
||
.finally(() => setLoading(false));
|
||
}, []);
|
||
|
||
const handleSave = async () => {
|
||
setSaving(true);
|
||
try {
|
||
const response = await fetch("/api/config", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ export: config }),
|
||
});
|
||
const result = await response.json();
|
||
if (result.success) {
|
||
addToast({ type: "success", title: "导出设置已保存" });
|
||
} else {
|
||
addToast({ type: "error", title: "保存失败", description: result.error });
|
||
}
|
||
} catch {
|
||
addToast({ type: "error", title: "保存失败" });
|
||
} finally {
|
||
setSaving(false);
|
||
}
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h2 className="text-lg font-semibold">导出设置</h2>
|
||
<p className="text-sm text-muted-foreground">配置导出 Word 文档的格式</p>
|
||
</div>
|
||
<div className="h-40 animate-pulse rounded-lg bg-muted" />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h2 className="text-lg font-semibold">导出设置</h2>
|
||
<p className="text-sm text-muted-foreground">配置导出 Word 文档的格式</p>
|
||
</div>
|
||
|
||
{/* 未翻译文本颜色 */}
|
||
<div className="space-y-4">
|
||
<h3 className="text-sm font-medium">文本颜色</h3>
|
||
<div className="space-y-2">
|
||
<label className="text-sm font-medium">未翻译文本颜色</label>
|
||
<div className="flex items-center gap-3">
|
||
<input
|
||
type="color"
|
||
value={config.untranslatedColor}
|
||
onChange={(e) => setConfig({ ...config, untranslatedColor: e.target.value.toUpperCase() })}
|
||
className="h-10 w-20 cursor-pointer rounded-md border border-input"
|
||
/>
|
||
<input
|
||
type="text"
|
||
value={config.untranslatedColor}
|
||
onChange={(e) => {
|
||
const val = e.target.value.toUpperCase();
|
||
if (/^[0-9A-Fa-f]{0,6}$/.test(val)) {
|
||
setConfig({ ...config, untranslatedColor: val });
|
||
}
|
||
}}
|
||
className="w-24 rounded-md border border-input bg-background px-3 py-2 text-sm font-mono"
|
||
placeholder="CC0000"
|
||
/>
|
||
<span className="text-sm text-muted-foreground">HEX 颜色值(如 CC0000)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 页面设置 */}
|
||
<div className="space-y-4">
|
||
<h3 className="text-sm font-medium">页面设置</h3>
|
||
<div className="space-y-2">
|
||
<label className="text-sm font-medium">页面方向</label>
|
||
<div className="flex gap-4">
|
||
<label className="flex items-center gap-2 cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name="orientation"
|
||
checked={config.orientation === "landscape"}
|
||
onChange={() => setConfig({ ...config, orientation: "landscape" })}
|
||
className="cursor-pointer"
|
||
/>
|
||
<span className="text-sm">横向</span>
|
||
</label>
|
||
<label className="flex items-center gap-2 cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name="orientation"
|
||
checked={config.orientation === "portrait"}
|
||
onChange={() => setConfig({ ...config, orientation: "portrait" })}
|
||
className="cursor-pointer"
|
||
/>
|
||
<span className="text-sm">纵向</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 预览 */}
|
||
<div className="space-y-2">
|
||
<h3 className="text-sm font-medium">预览</h3>
|
||
<div className="rounded-lg border bg-card p-4">
|
||
<div className="flex flex-col gap-2">
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-sm">未翻译示例:</span>
|
||
<span
|
||
className="rounded px-2 py-1 text-sm"
|
||
style={{ color: config.untranslatedColor, border: `1px solid ${config.untranslatedColor}40` }}
|
||
>
|
||
宫保鸡丁
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-sm">页面方向:</span>
|
||
<span className="text-sm">{config.orientation === "landscape" ? "横向 (Landscape)" : "纵向 (Portrait)"}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-3">
|
||
<Button onClick={handleSave} disabled={saving}>
|
||
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||
{saving ? "保存中..." : "保存配置"}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|