fix(*): 插件工作流 - 统一启停状态并闭环校验生成链路

This commit is contained in:
ddCat
2026-08-26 21:36:10 +08:00
parent 3a4a295523
commit c79e984bc3
23 changed files with 351 additions and 65 deletions

View File

@@ -226,30 +226,34 @@ export function ChannelSettingsPane({ onOpenModels, onOpenRunningHub, onOpenComf
<Button className="h-10 flex-1 sm:h-8 sm:flex-none" type="primary" icon={<Plus className="size-4" />} onClick={addChannel}>新增渠道</Button>
</div>
</div>
<section className="settings-section mb-3">
{onOpenRunningHub || onOpenComfyUI ? <section className="settings-section mb-3">
<div className="mb-3">
<h3 className="text-sm font-semibold">个人工作流渠道</h3>
<p className="mt-1 text-xs text-foreground/55">RunningHub 和 ComfyUI 使用各自的工作流参数与执行通道,配置入口统一放在个人渠道中。</p>
</div>
<div className="grid gap-2 lg:grid-cols-2">
<WorkflowChannelEntry
icon={<Workflow className="size-4" />}
title="RunningHub"
description="云端工作流和 RunningHub App"
status={runningHubReady ? `${config.runningHub.workflows.length} 个工作流已配置` : config.runningHub.enabled ? "待完成连接和工作流配置" : "未启用"}
ready={runningHubReady}
onOpen={onOpenRunningHub}
/>
<WorkflowChannelEntry
icon={<MonitorUp className="size-4" />}
title="ComfyUI"
description="通过 Bridge 连接本机或远程 ComfyUI"
status={comfyBridgeReady ? `${config.comfyBridge.workflows.length} 个工作流已配置` : config.comfyBridge.enabled ? "待选择 Bridge 和工作流" : "未启用"}
ready={comfyBridgeReady}
onOpen={onOpenComfyUI}
/>
{onOpenRunningHub ? (
<WorkflowChannelEntry
icon={<Workflow className="size-4" />}
title="RunningHub"
description="云端工作流和 RunningHub App"
status={runningHubReady ? `${config.runningHub.workflows.length} 个工作流已配置` : config.runningHub.enabled ? "待完成连接和工作流配置" : "未启用"}
ready={runningHubReady}
onOpen={onOpenRunningHub}
/>
) : null}
{onOpenComfyUI ? (
<WorkflowChannelEntry
icon={<MonitorUp className="size-4" />}
title="ComfyUI"
description="通过 Bridge 连接本机或远程 ComfyUI"
status={comfyBridgeReady ? `${config.comfyBridge.workflows.length} 个工作流已配置` : config.comfyBridge.enabled ? "待选择 Bridge 和工作流" : "未启用"}
ready={comfyBridgeReady}
onOpen={onOpenComfyUI}
/>
) : null}
</div>
</section>
</section> : null}
{userChannels.length ? (
<div className="settings-channel-list space-y-2">
{userChannels.map((channel) => {

View File

@@ -1,6 +1,6 @@
import { App, Button, Form, Input, InputNumber, Select } from "antd";
import { ArrowLeft, Boxes, Bug, Cloud, MessageSquareText, MonitorUp, RadioTower, SlidersHorizontal, SquareTerminal, Workflow } from "lucide-react";
import { useEffect, useState, type ReactNode } from "react";
import { useEffect, useMemo, useState, type ReactNode } from "react";
import { useNavigate, useSearchParams } from "react-router";
import { UserOSSSettingsForm } from "@/components/layout/user-oss-settings-form";
@@ -16,6 +16,8 @@ import { LocalCliSettings } from "./local-cli-settings";
import { PromptPreferencesPane } from "./prompt-preferences-pane";
import DiagnosticsPanel from "./diagnostics-panel";
import { RunningHubSettingsPane } from "./runninghub-settings-pane";
import { COMFYUI_PLUGIN_ID, RUNNINGHUB_PLUGIN_ID } from "@/lib/plugins/builtin/workflows";
import { usePluginStore } from "@/stores/use-plugin-store";
type ConfigSectionKey = "local-cli" | "channels" | "models" | "runninghub" | "comfyui" | "preferences" | "prompts" | "storage" | "diagnostics";
@@ -41,7 +43,13 @@ export default function SettingsPage() {
const [searchParams, setSearchParams] = useSearchParams();
const requestedSection = searchParams.get("section");
const customChannelsEnabled = useUserStore((state) => state.features.customChannelsEnabled);
const initialSection = isConfigSection(requestedSection) ? requestedSection : customChannelsEnabled ? "channels" : "models";
const runtimeStatuses = usePluginStore((state) => state.runtimeStatuses);
const runningHubPluginEnabled = runtimeStatuses[RUNNINGHUB_PLUGIN_ID] === "enabled";
const comfyUIPluginEnabled = runtimeStatuses[COMFYUI_PLUGIN_ID] === "enabled";
const requestedSectionEnabled = requestedSection !== "runninghub" && requestedSection !== "comfyui"
|| requestedSection === "runninghub" && runningHubPluginEnabled
|| requestedSection === "comfyui" && comfyUIPluginEnabled;
const initialSection = isConfigSection(requestedSection) && requestedSectionEnabled ? requestedSection : customChannelsEnabled ? "channels" : "models";
const [activeTab, setActiveTab] = useState<ConfigSectionKey>(initialSection === "channels" && !customChannelsEnabled ? "models" : initialSection);
const config = useConfigStore((state) => state.config);
const effectiveConfig = useEffectiveConfig();
@@ -49,15 +57,19 @@ export default function SettingsPage() {
const shouldPromptContinue = searchParams.get("continue") === "1";
const userId = useUserStore((state) => state.user?.id);
const userChannels = config.channels.filter((channel) => channel.scope !== "system");
const visibleConfigSections = customChannelsEnabled ? configSections : configSections.filter((section) => section.key !== "channels");
const visibleConfigSections = useMemo(() => (customChannelsEnabled ? configSections : configSections.filter((section) => section.key !== "channels"))
.filter((section) => section.key !== "runninghub" || runningHubPluginEnabled)
.filter((section) => section.key !== "comfyui" || comfyUIPluginEnabled), [comfyUIPluginEnabled, customChannelsEnabled, runningHubPluginEnabled]);
const isVisibleConfigSection = (value: string | null): value is ConfigSectionKey => isConfigSection(value) && visibleConfigSections.some((section) => section.key === value);
useEffect(() => {
if (isConfigSection(requestedSection) && (requestedSection !== "channels" || customChannelsEnabled)) {
if (isVisibleConfigSection(requestedSection)) {
setActiveTab(requestedSection);
return;
}
if (!customChannelsEnabled) setActiveTab((current) => (current === "channels" ? "models" : current));
}, [customChannelsEnabled, requestedSection]);
setActiveTab((current) => visibleConfigSections.some((section) => section.key === current) ? current : customChannelsEnabled ? "channels" : "models");
}, [customChannelsEnabled, requestedSection, visibleConfigSections]);
useEffect(() => {
if (!userId) return;
@@ -71,6 +83,7 @@ export default function SettingsPage() {
}, [message, userId]);
const selectSection = (section: ConfigSectionKey) => {
if ((section === "runninghub" && !runningHubPluginEnabled) || (section === "comfyui" && !comfyUIPluginEnabled)) return;
setActiveTab(section);
const next = new URLSearchParams(searchParams);
next.set("section", section);
@@ -87,8 +100,8 @@ export default function SettingsPage() {
}
const hasReadyLocalRuntime = effectiveConfig.channels.some((channel) => channel.transport === "local-runtime" && channel.enabled !== false && Boolean(channel.localModels?.length));
const workflowReady = Boolean(
(config.runningHub.enabled && config.runningHub.workflowId.trim() && config.runningHub.baseUrl.trim() && config.runningHub.apiKey.trim())
|| (config.comfyBridge.enabled && config.comfyBridge.bridgeId.trim() && config.comfyBridge.workflowId.trim()),
(runningHubPluginEnabled && config.runningHub.enabled && config.runningHub.workflowId.trim() && config.runningHub.baseUrl.trim() && config.runningHub.apiKey.trim())
|| (comfyUIPluginEnabled && config.comfyBridge.enabled && config.comfyBridge.bridgeId.trim() && config.comfyBridge.workflowId.trim()),
);
if (!effectiveConfig.channels.some(isChannelReady) && !hasReadyLocalRuntime && !workflowReady) {
selectSection(customChannelsEnabled ? "channels" : "models");
@@ -101,7 +114,7 @@ export default function SettingsPage() {
const panes: Record<ConfigSectionKey, ReactNode> = {
"local-cli": <SettingsPane><LocalCliSettings /></SettingsPane>,
channels: <SettingsPane><ChannelSettingsPane onOpenModels={() => selectSection("models")} onOpenRunningHub={() => selectSection("runninghub")} onOpenComfyUI={() => selectSection("comfyui")} /></SettingsPane>,
channels: <SettingsPane><ChannelSettingsPane onOpenModels={() => selectSection("models")} onOpenRunningHub={runningHubPluginEnabled ? () => selectSection("runninghub") : undefined} onOpenComfyUI={comfyUIPluginEnabled ? () => selectSection("comfyui") : undefined} /></SettingsPane>,
models: (
<SettingsPane>
<div className="settings-pane-header">