diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 50f7574..4f57e85 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -26,7 +26,7 @@ type SidebarProps = { const mainItems: Array<{ key: NavKey; label: string; - icon: React.ComponentType<{ size?: number }>; + icon: React.ComponentType<{ size?: number; className?: string }>; }> = [ { key: "home", label: "首页", icon: Home }, { key: "test", label: "测试助手", icon: PlayCircle }, @@ -35,7 +35,7 @@ const mainItems: Array<{ const componentSubItems: Array<{ key: NavKey; label: string; - icon: React.ComponentType<{ size?: number }>; + icon: React.ComponentType<{ size?: number; className?: string }>; }> = [ { key: "components-models", label: "模型资源", icon: Brain }, { key: "components-knowledge", label: "知识库", icon: Database }, @@ -45,7 +45,7 @@ const componentSubItems: Array<{ const monitorSubItems: Array<{ key: NavKey; label: string; - icon: React.ComponentType<{ size?: number }>; + icon: React.ComponentType<{ size?: number; className?: string }>; }> = [ { key: "history", label: "历史记录", icon: Clock3 }, { key: "dashboard", label: "数据看板", icon: Database }, @@ -294,7 +294,7 @@ function NavButton({ }: { active: boolean; collapsed: boolean; - icon: React.ComponentType<{ size?: number }>; + icon: React.ComponentType<{ size?: number; className?: string }>; label: string; onClick: () => void; small?: boolean; diff --git a/src/components/pages/AssistantPage.tsx b/src/components/pages/AssistantPage.tsx index a0866ec..f8059d5 100644 --- a/src/components/pages/AssistantPage.tsx +++ b/src/components/pages/AssistantPage.tsx @@ -74,6 +74,25 @@ type AssistantForm = { enableInterrupt: boolean; }; +type FastGptForm = { + name: string; + appId: string; + apiUrl: string; + apiKey: string; + asr: string; + voice: string; + enableInterrupt: boolean; +}; + +type DifyForm = { + name: string; + apiUrl: string; + apiKey: string; + asr: string; + voice: string; + enableInterrupt: boolean; +}; + type AssistantType = "提示词" | "工作流" | "Dify" | "FastGPT"; const assistantTypes: AssistantType[] = ["提示词", "工作流", "Dify", "FastGPT"]; @@ -83,7 +102,7 @@ type AssistantTypeOption = { label: string; description: string; icon: React.ReactNode; - /** 提示词类型已落地,其余三种暂时显示占位页 */ + /** 提示词、Dify、FastGPT 类型已落地,工作流暂时显示占位页 */ available: boolean; }; @@ -107,14 +126,14 @@ const assistantTypeOptions: AssistantTypeOption[] = [ label: "使用 Dify 构建", description: "对接 Dify 应用,复用其编排能力与知识库配置。", icon: , - available: false, + available: true, }, { type: "FastGPT", label: "使用 FastGPT 构建", description: "对接 FastGPT 应用,复用其知识库问答与工作流能力。", icon: , - available: false, + available: true, }, ]; @@ -220,9 +239,26 @@ export function AssistantPage() { knowledgeBase: "无", enableInterrupt: true, }); - const [view, setView] = useState<"list" | "choose" | "create" | "placeholder">( - "list", - ); + const [fastGptForm, setFastGptForm] = useState({ + name: "FastGPT 售后咨询助手", + appId: "", + apiUrl: "https://api.fastgpt.in/api/v1/chat/completions", + apiKey: "", + asr: "SenseVoice", + voice: "晓宁", + enableInterrupt: true, + }); + const [difyForm, setDifyForm] = useState({ + name: "Dify 知识库问答助手", + apiUrl: "https://api.dify.ai/v1/chat-messages", + apiKey: "", + asr: "SenseVoice", + voice: "晓宁", + enableInterrupt: true, + }); + const [view, setView] = useState< + "list" | "choose" | "create" | "create-dify" | "create-fastgpt" | "placeholder" + >("list"); const [searchQuery, setSearchQuery] = useState(""); const [typeFilter, setTypeFilter] = useState("全部"); const [currentPage, setCurrentPage] = useState(1); @@ -238,6 +274,25 @@ export function AssistantPage() { setView("choose"); } + // 编辑:根据助手类型进入对应的构建/编辑页,并把名称带入相应表单 + function handleEdit(assistant: AssistantListItem) { + if (assistant.type === "提示词") { + updateForm("name", assistant.name); + setView("create"); + } else if (assistant.type === "FastGPT") { + updateFastGptForm("name", assistant.name); + setView("create-fastgpt"); + } else if (assistant.type === "Dify") { + updateDifyForm("name", assistant.name); + setView("create-dify"); + } else { + // 工作流:暂时显示占位页 + setDraftName(assistant.name); + setDraftType(assistant.type); + setView("placeholder"); + } + } + function confirmType() { if (!draftName.trim() || !draftType) { return; @@ -247,8 +302,16 @@ export function AssistantPage() { // 提示词类型:复用现有创建表单,并把已填的名称带过去 updateForm("name", draftName.trim()); setView("create"); + } else if (draftType === "FastGPT") { + // FastGPT 类型:进入 FastGPT 构建表单,并把已填的名称带过去 + updateFastGptForm("name", draftName.trim()); + setView("create-fastgpt"); + } else if (draftType === "Dify") { + // Dify 类型:进入 Dify 构建表单,并把已填的名称带过去 + updateDifyForm("name", draftName.trim()); + setView("create-dify"); } else { - // 工作流 / Dify / FastGPT:暂时显示占位页 + // 工作流:暂时显示占位页 setView("placeholder"); } } @@ -296,6 +359,26 @@ export function AssistantPage() { [key]: value, })); } + + function updateFastGptForm( + key: K, + value: FastGptForm[K], + ) { + setFastGptForm((prev) => ({ + ...prev, + [key]: value, + })); + } + + function updateDifyForm( + key: K, + value: DifyForm[K], + ) { + setDifyForm((prev) => ({ + ...prev, + [key]: value, + })); + } if (view === "list") { return (
@@ -400,7 +483,7 @@ export function AssistantPage() { variant="outline" size="sm" className="gap-1.5 border-hairline-strong text-xs text-muted-foreground hover:text-foreground" - onClick={() => setView("create")} + onClick={() => handleEdit(assistant)} > 编辑 @@ -680,6 +763,194 @@ export function AssistantPage() { ); } + if (view === "create-dify") { + return ( +
+
+
+ updateDifyForm("name", value)} + /> +
+ +
+ + + +
+
+ +
+
+ } + title="Dify 应用配置" + description="填写 API URL 与 API Key 以对接 Dify 应用。开场白、知识库、提示词等对话编排请在 Dify 平台配置,本页不重复设置。" + > + updateDifyForm("apiUrl", value)} + placeholder="https://api.dify.ai/v1/chat-messages" + /> + updateDifyForm("apiKey", value)} + placeholder="请输入 Dify API Key" + type="password" + /> + + + } + title="语音配置" + description="配置本平台的语音识别与播报音色。大模型、知识库与开场白由 Dify 应用提供,请前往 Dify 平台配置。" + > + updateDifyForm("asr", value)} + options={["SenseVoice", "Paraformer", "Whisper", "FunASR"]} + /> + updateDifyForm("voice", value)} + options={["晓宁", "晓美", "晓宇", "晓晨"]} + /> + + + } + title="交互策略" + description="设置实时视频对话时的交互体验" + > + + updateDifyForm("enableInterrupt", checked) + } + /> + +
+ + +
+
+ ); + } + + if (view === "create-fastgpt") { + return ( +
+
+
+ updateFastGptForm("name", value)} + /> +
+ +
+ + + +
+
+ +
+
+ } + title="FastGPT 应用配置" + description="填写 App ID、API URL 与 API Key 以对接 FastGPT 应用。开场白、知识库、提示词等对话编排请在 FastGPT 平台配置,本页不重复设置。" + > + updateFastGptForm("appId", value)} + placeholder="请输入 FastGPT 应用 ID" + /> + updateFastGptForm("apiUrl", value)} + placeholder="https://api.fastgpt.in/api/v1/chat/completions" + /> + updateFastGptForm("apiKey", value)} + placeholder="请输入 FastGPT API Key" + type="password" + /> + + + } + title="语音配置" + description="配置本平台的语音识别与播报音色。大模型、知识库与开场白由 FastGPT 应用提供,请前往 FastGPT 平台配置。" + > + updateFastGptForm("asr", value)} + options={["SenseVoice", "Paraformer", "Whisper", "FunASR"]} + /> + updateFastGptForm("voice", value)} + options={["晓宁", "晓美", "晓宇", "晓晨"]} + /> + + + } + title="交互策略" + description="设置实时视频对话时的交互体验" + > + + updateFastGptForm("enableInterrupt", checked) + } + /> + +
+ + +
+
+ ); + } + return (
@@ -1148,6 +1419,35 @@ function SectionCard({ ); } +function InputField({ + label, + value, + placeholder, + type = "text", + onChange, +}: { + label?: string; + value: string; + placeholder?: string; + type?: string; + onChange: (value: string) => void; +}) { + return ( + + ); +} + function TextAreaField({ label, value,