"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 { API_BASE_URL } from "@/lib/config"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { toast } from "@/hooks/useToast"; export default function SettingsPage(props: any = {}) { const { embedded = false } = props; const router = useRouter(); 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, }); 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("加载配置失败"); } 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, }); } catch (e) { console.error("加载知识库向量配置失败:", e); setEmbeddingError("加载失败"); } 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, }; if (embeddingForm.api_key) { data.api_key = embeddingForm.api_key; } await updateEmbeddingConfig(userId, data); await loadEmbeddingConfig(); toast.success("保存成功,配置已立即生效。"); } 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 || "操作失败"); } finally { setSubmitting(false); } }; // 删除配置 const handleDelete = async (id: number) => { if (!userId) return; if (!confirm("确定要删除这个配置吗?")) return; try { await deleteAIConfig(userId, id); await loadConfigs(); } catch (error) { setError((error as Error).message || "删除失败"); } }; // 退出登录 const handleLogout = async () => { try { await fetch(`${API_BASE_URL}/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 = (

AI 配置管理

管理 AI 服务商配置
{!embedded && (
)}
); // 构建主内容区 const mainContent = (
{/* 全局设置 */} 全局设置
{ if (userId) { try { await updateProfile({ receive_ai_conversations: !checked, }); } catch (error) { console.error("更新设置失败:", error); toast.error("更新设置失败,请重试"); } } }} disabled={profileLoading} />

开启后,AI 对话将不会显示在对话列表中,也不会收到 AI 消息通知。 但您仍可以在会话页面手动开启"显示 AI 消息"来查看 AI 对话历史。

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

用于知识库文档向量化与 RAG 检索。仅管理员可修改;保存后立即生效,无需重启。

{embeddingLoading ? (
加载中...
) : (
{embeddingError && (
{embeddingError}
)}
setEmbeddingForm({ ...embeddingForm, api_url: e.target.value }) } placeholder="https://api.openai.com/v1 或兼容地址" />
setEmbeddingForm({ ...embeddingForm, api_key: e.target.value }) } placeholder={embeddingConfig?.api_key_masked ? "留空则不更新" : "输入 API Key"} />
setEmbeddingForm({ ...embeddingForm, model: e.target.value }) } placeholder="text-embedding-3-small" />
setEmbeddingForm({ ...embeddingForm, customer_can_use_kb: checked === true, }) } />
)}
{/* 配置表单 */} {editingId ? "编辑 AI 配置" : "添加 AI 配置"}
{error && (
{error}
)}
setFormData({ ...formData, provider: e.target.value }) } placeholder="例如:OpenAI、Claude、自定义" required />
setFormData({ ...formData, api_url: e.target.value }) } placeholder="https://api.openai.com/v1/chat/completions" required />
setFormData({ ...formData, api_key: e.target.value }) } placeholder={editingId ? "留空则不更新" : "输入 API Key"} required={!editingId} />
setFormData({ ...formData, model: e.target.value }) } placeholder="例如:gpt-3.5-turbo、gpt-4" required />
setFormData({ ...formData, description: e.target.value }) } placeholder="例如:OpenAI GPT-3.5 Turbo 模型" />
{editingId && ( )}
{/* 配置列表 */} 已配置的 AI 服务 {loading ? (
加载中...
) : configs.length === 0 ? (
暂无配置,请添加
) : (
{configs.map((config) => (

{config.provider} - {config.model}

{config.is_active && ( 启用 )} {config.is_public && ( 开放 )}

API 地址: {config.api_url}

模型类型: {config.model_type}

{config.description && (

描述: {config.description}

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