"use client"; import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { ResponsiveLayout } from "@/components/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { fetchAIConfigs, createAIConfig, updateAIConfig, deleteAIConfig, type AIConfig, type CreateAIConfigRequest, type UpdateAIConfigRequest, } from "@/features/agent/services/aiConfigApi"; import { fetchEmbeddingConfig, updateEmbeddingConfig, type EmbeddingConfig, type UpdateEmbeddingConfigRequest, } from "@/features/agent/services/embeddingConfigApi"; import { useProfile } from "@/features/agent/hooks/useProfile"; import { apiUrl } from "@/lib/config"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { toast } from "@/hooks/useToast"; import type { I18nKey } from "@/lib/i18n/dict"; import { useI18n } from "@/lib/i18n/provider"; export default function SettingsPage(props: any = {}) { const { embedded = false } = props; const router = useRouter(); const { t } = useI18n(); const modelTypeLabel = (mt: string) => { const map: Record = { text: "agent.settings.modelType.text", image: "agent.settings.modelType.image", audio: "agent.settings.modelType.audio", video: "agent.settings.modelType.video", }; const k = map[mt]; return k ? t(k) : mt; }; const [userId, setUserId] = useState(null); const [configs, setConfigs] = useState([]); const [loading, setLoading] = useState(true); const [editingId, setEditingId] = useState(null); const [formData, setFormData] = useState({ provider: "", api_url: "", api_key: "", model: "", model_type: "text", is_active: true, is_public: false, description: "", }); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); // 知识库向量配置(平台级,仅管理员可修改) const [embeddingConfig, setEmbeddingConfig] = useState(null); const [embeddingForm, setEmbeddingForm] = useState({ embedding_type: "openai", api_url: "", api_key: "", model: "text-embedding-3-small", customer_can_use_kb: true, visitor_web_search_enabled: false, web_search_source: "custom" as "vendor" | "custom", }); const [embeddingLoading, setEmbeddingLoading] = useState(false); const [embeddingSubmitting, setEmbeddingSubmitting] = useState(false); const [embeddingError, setEmbeddingError] = useState(""); // 检查登录状态 useEffect(() => { const storedUserId = localStorage.getItem("agent_user_id"); if (!storedUserId) { router.push("/"); return; } setUserId(Number.parseInt(storedUserId, 10)); }, [router]); // 加载个人资料(用于获取和更新 AI 对话接收设置) const { profile, loading: profileLoading, update: updateProfile, } = useProfile({ userId: userId ?? null, enabled: Boolean(userId), }); // 加载配置列表 const loadConfigs = async () => { if (!userId) return; try { setLoading(true); const data = await fetchAIConfigs(userId); setConfigs(data); } catch (error) { console.error("加载配置失败:", error); setError(t("agent.settings.error.loadConfigs")); } finally { setLoading(false); } }; useEffect(() => { if (userId) { loadConfigs(); } }, [userId]); // 加载知识库向量配置 const loadEmbeddingConfig = async () => { if (!userId) return; try { setEmbeddingLoading(true); const data = await fetchEmbeddingConfig(userId); setEmbeddingConfig(data); setEmbeddingForm({ embedding_type: data.embedding_type || "openai", api_url: data.api_url || "", api_key: "", model: data.model || "text-embedding-3-small", customer_can_use_kb: data.customer_can_use_kb ?? true, visitor_web_search_enabled: data.visitor_web_search_enabled ?? false, web_search_source: data.web_search_source === "vendor" ? "vendor" : "custom", }); } catch (e) { console.error("加载知识库向量配置失败:", e); setEmbeddingError(t("agent.settings.error.loadEmbedding")); } finally { setEmbeddingLoading(false); } }; useEffect(() => { if (userId) { loadEmbeddingConfig(); } }, [userId]); // 保存知识库向量配置(仅管理员;保存后立即生效,无需重启) const handleSaveEmbeddingConfig = async (e: React.FormEvent) => { e.preventDefault(); if (!userId) return; setEmbeddingSubmitting(true); setEmbeddingError(""); try { const data: UpdateEmbeddingConfigRequest = { embedding_type: embeddingForm.embedding_type, api_url: embeddingForm.api_url || undefined, model: embeddingForm.model || undefined, customer_can_use_kb: embeddingForm.customer_can_use_kb, visitor_web_search_enabled: embeddingForm.visitor_web_search_enabled, web_search_source: embeddingForm.web_search_source, }; if (embeddingForm.api_key) { data.api_key = embeddingForm.api_key; } await updateEmbeddingConfig(userId, data); await loadEmbeddingConfig(); toast.success(t("agent.settings.toast.embeddingSaved")); } catch (err) { setEmbeddingError((err as Error).message); } finally { setEmbeddingSubmitting(false); } }; // 重置表单 const resetForm = () => { setFormData({ provider: "", api_url: "", api_key: "", model: "", model_type: "text", is_active: true, is_public: false, description: "", }); setEditingId(null); setError(""); }; // 开始编辑 const handleEdit = (config: AIConfig) => { setFormData({ provider: config.provider, api_url: config.api_url, api_key: "", // 不显示 API Key(已加密) model: config.model, model_type: config.model_type, is_active: config.is_active, is_public: config.is_public, description: config.description, }); setEditingId(config.id); }; // 提交表单 const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!userId) return; setSubmitting(true); setError(""); try { if (editingId) { // 更新配置 const updateData: UpdateAIConfigRequest = { provider: formData.provider, api_url: formData.api_url, model: formData.model, model_type: formData.model_type, is_active: formData.is_active, is_public: formData.is_public, description: formData.description, }; // 如果提供了新的 API Key,才更新 if (formData.api_key) { updateData.api_key = formData.api_key; } await updateAIConfig(userId, editingId, updateData); } else { // 创建配置 await createAIConfig(userId, formData); } resetForm(); await loadConfigs(); } catch (error) { setError((error as Error).message || t("agent.settings.error.operation")); } finally { setSubmitting(false); } }; // 删除配置 const handleDelete = async (id: number) => { if (!userId) return; if (!confirm(t("agent.settings.confirmDeleteConfig"))) return; try { await deleteAIConfig(userId, id); await loadConfigs(); } catch (error) { setError((error as Error).message || t("agent.settings.error.delete")); } }; // 退出登录 const handleLogout = async () => { try { await fetch(apiUrl("/logout"), { method: "POST" }); } catch (error) { console.error("退出登录失败:", error); } finally { localStorage.removeItem("agent_user_id"); localStorage.removeItem("agent_username"); localStorage.removeItem("agent_role"); router.push("/"); } }; if (!userId) { return null; } // 构建头部内容 const headerContent = (

{t("agent.settings.title")}

{t("agent.settings.subtitle")}
{!embedded && (
)}
); // 构建主内容区 const mainContent = (
{/* 全局设置 */} {t("agent.settings.section.global")}
{ if (userId) { try { await updateProfile({ receive_ai_conversations: !checked, }); } catch (error) { console.error("更新设置失败:", error); toast.error(t("agent.settings.toast.profileUpdateFailed")); } } }} disabled={profileLoading} />

{t("agent.settings.global.noReceiveAiHint")}

{/* 知识库向量模型(平台级,仅管理员可修改;保存后立即生效) */} {t("agent.settings.embedding.title")}

{t("agent.settings.embedding.lead")}

{embeddingLoading ? (
{t("common.loading")}
) : (
{embeddingError && (
{embeddingError}
)}
setEmbeddingForm({ ...embeddingForm, api_url: e.target.value }) } placeholder={t("agent.settings.embedding.apiUrlPh")} />
setEmbeddingForm({ ...embeddingForm, api_key: e.target.value }) } placeholder={ embeddingConfig?.api_key_masked ? t("agent.settings.embedding.apiKeyKeepEmpty") : t("agent.settings.embedding.apiKeyInput") } />
setEmbeddingForm({ ...embeddingForm, model: e.target.value }) } placeholder={t("agent.settings.embedding.modelPh")} />
setEmbeddingForm({ ...embeddingForm, customer_can_use_kb: checked === true, }) } />
)}
{/* 联网搜索设置(与知识库向量模型独立;实际仍写入同一配置,仅 UI 分离) */} {t("agent.settings.webSearch.title")}

{t("agent.settings.webSearch.lead")}

{embeddingLoading ? (
{t("common.loading")}
) : (
{embeddingError && (
{embeddingError}
)}

{t("agent.settings.webSearch.modeHint")}

setEmbeddingForm({ ...embeddingForm, visitor_web_search_enabled: checked === true, }) } />
)}
{/* 配置表单 */} {editingId ? t("agent.settings.aiCard.titleEdit") : t("agent.settings.aiCard.titleAdd")}
{error && (
{error}
)}
setFormData({ ...formData, provider: e.target.value }) } placeholder={t("agent.settings.aiForm.providerPh")} required />
setFormData({ ...formData, api_url: e.target.value }) } placeholder={t("agent.settings.aiForm.apiUrlPh")} required />
setFormData({ ...formData, api_key: e.target.value }) } placeholder={ editingId ? t("agent.settings.embedding.apiKeyKeepEmpty") : t("agent.settings.embedding.apiKeyInput") } required={!editingId} />
setFormData({ ...formData, model: e.target.value }) } placeholder={t("agent.settings.aiForm.modelPh")} required />
setFormData({ ...formData, description: e.target.value }) } placeholder={t("agent.settings.aiForm.descPh")} />
{editingId && ( )}
{/* 配置列表 */} {t("agent.settings.list.title")} {loading ? (
{t("common.loading")}
) : configs.length === 0 ? (
{t("agent.settings.list.empty")}
) : (
{configs.map((config) => (

{config.provider} - {config.model}

{config.is_active && ( {t("agent.settings.badge.active")} )} {config.is_public && ( {t("agent.settings.badge.public")} )}

{t("agent.settings.list.apiUrlLabel")} {config.api_url}

{t("agent.settings.list.modelTypeLabel")} {modelTypeLabel(config.model_type)}

{config.description && (

{t("agent.settings.list.descLabel")} {config.description}

)}
))}
)}
); // 如果是嵌入模式,只返回内容,不包含 ResponsiveLayout if (embedded) { return (
{headerContent} {mainContent}
); } return ( ); }