Files
AiTool/frontend/app/(main)/settings/cloud-doc-config/page.tsx
2026-03-15 16:38:59 +08:00

210 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect, useCallback } from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
cloudDocConfigApi,
type CloudDocConfigRead,
type CloudDocConfigUpdate,
} from "@/lib/api/client";
import { Loader2, Save, FileStack } from "lucide-react";
import { toast } from "sonner";
export default function SettingsCloudDocConfigPage() {
const [config, setConfig] = useState<CloudDocConfigRead | null>(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [form, setForm] = useState<CloudDocConfigUpdate>({
feishu: { app_id: "", app_secret: "" },
yuque: { token: "", default_repo: "" },
tencent: { client_id: "", client_secret: "" },
});
const load = useCallback(async () => {
setLoading(true);
try {
const data = await cloudDocConfigApi.get();
setConfig(data);
setForm({
feishu: { app_id: data.feishu.app_id, app_secret: "" },
yuque: { token: "", default_repo: data.yuque.default_repo },
tencent: { client_id: data.tencent.client_id, client_secret: "" },
});
} catch {
toast.error("加载云文档配置失败");
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
load();
}, [load]);
const handleSave = async () => {
setSaving(true);
try {
const payload: CloudDocConfigUpdate = {};
if (form.feishu?.app_id !== undefined) payload.feishu = { app_id: form.feishu.app_id };
if (form.feishu?.app_secret !== undefined && form.feishu.app_secret !== "")
payload.feishu = { ...payload.feishu, app_secret: form.feishu.app_secret };
if (form.yuque?.token !== undefined && form.yuque.token !== "")
payload.yuque = { token: form.yuque.token };
if (form.yuque?.default_repo !== undefined)
payload.yuque = { ...payload.yuque, default_repo: form.yuque.default_repo };
if (form.tencent?.client_id !== undefined) payload.tencent = { client_id: form.tencent.client_id };
if (form.tencent?.client_secret !== undefined && form.tencent.client_secret !== "")
payload.tencent = { ...payload.tencent, client_secret: form.tencent.client_secret };
await cloudDocConfigApi.update(payload);
toast.success("已保存");
await load();
} catch (e) {
toast.error(e instanceof Error ? e.message : "保存失败");
} finally {
setSaving(false);
}
};
if (loading) {
return (
<div className="p-6 max-w-2xl flex items-center gap-2">
<Loader2 className="h-5 w-5 animate-spin" />
<span className="text-sm text-muted-foreground"></span>
</div>
);
}
return (
<div className="p-6 max-w-2xl">
<div className="mb-4">
<Link
href="/settings"
className="text-sm text-muted-foreground hover:text-foreground"
>
</Link>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileStack className="h-5 w-5" />
</CardTitle>
<p className="text-sm text-muted-foreground mt-1">
API /线
</p>
</CardHeader>
<CardContent className="space-y-6">
{/* 飞书 */}
<div className="space-y-3">
<h3 className="text-sm font-medium"> (Feishu)</h3>
<div className="grid gap-2">
<Label>App ID</Label>
<Input
value={form.feishu?.app_id ?? config?.feishu?.app_id ?? ""}
onChange={(e) =>
setForm((f) => ({
...f,
feishu: { ...f.feishu, app_id: e.target.value },
}))
}
placeholder="在飞书开放平台创建应用后获取"
/>
</div>
<div className="grid gap-2">
<Label>App Secret</Label>
<Input
type="password"
value={form.feishu?.app_secret ?? ""}
onChange={(e) =>
setForm((f) => ({
...f,
feishu: { ...f.feishu, app_secret: e.target.value },
}))
}
placeholder={config?.feishu?.app_secret_configured ? "已配置,留空不修改" : "必填"}
/>
</div>
</div>
{/* 语雀 */}
<div className="space-y-3">
<h3 className="text-sm font-medium"> (Yuque)</h3>
<div className="grid gap-2">
<Label>Personal Access Token</Label>
<Input
type="password"
value={form.yuque?.token ?? ""}
onChange={(e) =>
setForm((f) => ({
...f,
yuque: { ...f.yuque, token: e.target.value },
}))
}
placeholder={config?.yuque?.token_configured ? "已配置,留空不修改" : "在语雀 设置 → Token 中创建"}
/>
</div>
<div className="grid gap-2">
<Label> (namespace)</Label>
<Input
value={form.yuque?.default_repo ?? config?.yuque?.default_repo ?? ""}
onChange={(e) =>
setForm((f) => ({
...f,
yuque: { ...f.yuque, default_repo: e.target.value },
}))
}
placeholder="如your_username/repo"
/>
</div>
</div>
{/* 腾讯文档 */}
<div className="space-y-3">
<h3 className="text-sm font-medium"> (Tencent)</h3>
<p className="text-xs text-muted-foreground">
OAuth
</p>
<div className="grid gap-2">
<Label>Client ID</Label>
<Input
value={form.tencent?.client_id ?? config?.tencent?.client_id ?? ""}
onChange={(e) =>
setForm((f) => ({
...f,
tencent: { ...f.tencent, client_id: e.target.value },
}))
}
placeholder="开放平台应用 Client ID"
/>
</div>
<div className="grid gap-2">
<Label>Client Secret</Label>
<Input
type="password"
value={form.tencent?.client_secret ?? ""}
onChange={(e) =>
setForm((f) => ({
...f,
tencent: { ...f.tencent, client_secret: e.target.value },
}))
}
placeholder={config?.tencent?.client_secret_configured ? "已配置,留空不修改" : "选填"}
/>
</div>
</div>
<Button onClick={handleSave} disabled={saving}>
{saving ? <Loader2 className="h-4 w-4 animate-spin mr-2" /> : <Save className="h-4 w-4 mr-2" />}
</Button>
</CardContent>
</Card>
</div>
);
}