Update BossAI workflow and workspace experience
This commit is contained in:
+61
-75
@@ -9,9 +9,11 @@ const DOCKER_WORKFLOW_URL = '/api/comfyui/proxy/';
|
||||
|
||||
export default function Workflows() {
|
||||
const [ready, setReady] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [message, setMessage] = React.useState('正在加载工作流前端...');
|
||||
const [workflowUrl, setWorkflowUrl] = React.useState(LOCAL_WORKFLOW_URL);
|
||||
const [isCloud, setIsCloud] = React.useState(false);
|
||||
const [backendLabel, setBackendLabel] = React.useState('');
|
||||
const [reloadKey, setReloadKey] = React.useState(0);
|
||||
const iframeRef = React.useRef<HTMLIFrameElement | null>(null);
|
||||
|
||||
const openFullscreen = () => {
|
||||
@@ -20,8 +22,13 @@ export default function Workflows() {
|
||||
|
||||
const reloadFrame = () => {
|
||||
if (iframeRef.current) {
|
||||
setLoading(true);
|
||||
iframeRef.current.src = workflowUrl;
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setMessage('正在重新检查工作流入口...');
|
||||
setReloadKey((value) => value + 1);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -30,57 +37,71 @@ export default function Workflows() {
|
||||
const loadRuntime = async () => {
|
||||
const runtimeResponse = await fetch('/api/workflow-ui/runtime', { signal: controller.signal });
|
||||
const runtime = runtimeResponse.ok ? ((await runtimeResponse.json()) as WorkflowRuntime) : {};
|
||||
const cloudMode = runtime.mode === 'cloud';
|
||||
const dockerMode = runtime.mode === 'docker';
|
||||
const targetUrl = dockerMode ? DOCKER_WORKFLOW_URL : LOCAL_WORKFLOW_URL;
|
||||
if (cancelled) return;
|
||||
setIsCloud(cloudMode);
|
||||
setBackendLabel(resolveBackendLabel(runtime.mode));
|
||||
setWorkflowUrl(targetUrl);
|
||||
const response = await fetch(targetUrl, { signal: controller.signal });
|
||||
if (cancelled) return;
|
||||
setReady(response.ok);
|
||||
setLoading(false);
|
||||
setMessage(response.ok ? '' : 'Workflow Studio 前端未构建,请先执行 workflow-ui:install');
|
||||
};
|
||||
void loadRuntime().catch((error) => {
|
||||
if (cancelled || error?.name === 'AbortError') return;
|
||||
setReady(false);
|
||||
setLoading(false);
|
||||
setMessage('工作流入口不可用,请检查服务配置');
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
controller.abort();
|
||||
};
|
||||
}, []);
|
||||
}, [reloadKey]);
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: 'calc(100vh - 56px)', display: 'flex', flexDirection: 'column' }}>
|
||||
<div style={toolbarStyle}>
|
||||
<div style={toolbarTitleStyle}>工作流</div>
|
||||
{!isCloud && workflowUrl === DOCKER_WORKFLOW_URL && <div style={cloudBadgeStyle}>Docker 完整版 ComfyUI</div>}
|
||||
{isCloud && <div style={cloudBadgeStyle}>Comfy Cloud API 后端</div>}
|
||||
<div style={toolbarActionsStyle}>
|
||||
<button type="button" onClick={reloadFrame} disabled={!ready} style={toolbarButtonStyle}>
|
||||
<div className="workflow-shell">
|
||||
<div className="workflow-toolbar">
|
||||
<div className="workflow-toolbar-main">
|
||||
<div className="workflow-title">工作流</div>
|
||||
{backendLabel && <div className="workflow-backend-badge">{backendLabel}</div>}
|
||||
</div>
|
||||
<div className="workflow-actions">
|
||||
<button type="button" onClick={reloadFrame} disabled={!ready} className="btn btn-link workflow-action-btn">
|
||||
刷新
|
||||
</button>
|
||||
<button type="button" onClick={openFullscreen} style={primaryToolbarButtonStyle}>
|
||||
<button type="button" onClick={openFullscreen} className="btn btn-primary workflow-primary-btn">
|
||||
新窗口打开
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{ready ? (
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
src={workflowUrl}
|
||||
title="Workflow Studio"
|
||||
allow="clipboard-read; clipboard-write"
|
||||
referrerPolicy="no-referrer"
|
||||
style={{ width: '100%', height: '100%', border: 'none', display: 'block' }}
|
||||
/>
|
||||
<div className="workflow-frame-wrap">
|
||||
{loading && (
|
||||
<div className="workflow-frame-loading" aria-live="polite">
|
||||
<div className="workflow-loading-bar" />
|
||||
<span>正在连接工作流前端...</span>
|
||||
</div>
|
||||
)}
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
src={workflowUrl}
|
||||
title="Workflow Studio"
|
||||
allow="clipboard-read; clipboard-write"
|
||||
referrerPolicy="no-referrer"
|
||||
onLoad={() => setLoading(false)}
|
||||
className="workflow-frame"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ flex: 1, display: 'grid', placeItems: 'center', padding: 24, textAlign: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 18, fontWeight: 700, marginBottom: 8 }}>Workflow Studio</div>
|
||||
<div style={{ color: 'var(--color-text-secondary)', lineHeight: 1.7 }}>{message}</div>
|
||||
<div className="workflow-empty">
|
||||
<div className="workflow-empty-panel">
|
||||
<div className="workflow-empty-title">Workflow Studio</div>
|
||||
<div className="workflow-empty-desc">{message}</div>
|
||||
<button type="button" onClick={reloadFrame} className="btn btn-soft-primary workflow-empty-btn">
|
||||
重新检查
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -88,54 +109,19 @@ export default function Workflows() {
|
||||
);
|
||||
}
|
||||
|
||||
const toolbarStyle: React.CSSProperties = {
|
||||
minHeight: 44,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
padding: '8px 12px',
|
||||
borderBottom: '1px solid var(--color-border-light)',
|
||||
background: 'var(--color-surface)',
|
||||
};
|
||||
|
||||
const cloudBadgeStyle: React.CSSProperties = {
|
||||
marginRight: 'auto',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: 999,
|
||||
padding: '3px 8px',
|
||||
color: 'var(--color-text-secondary)',
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
};
|
||||
|
||||
const toolbarTitleStyle: React.CSSProperties = {
|
||||
fontSize: 14,
|
||||
fontWeight: 800,
|
||||
color: 'var(--color-text-primary)',
|
||||
};
|
||||
|
||||
const toolbarActionsStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
};
|
||||
|
||||
const toolbarButtonStyle: React.CSSProperties = {
|
||||
minHeight: 30,
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: 8,
|
||||
padding: '5px 10px',
|
||||
background: 'var(--color-bg)',
|
||||
color: 'var(--color-text-primary)',
|
||||
cursor: 'pointer',
|
||||
fontSize: 12,
|
||||
};
|
||||
|
||||
const primaryToolbarButtonStyle: React.CSSProperties = {
|
||||
...toolbarButtonStyle,
|
||||
borderColor: 'var(--color-primary)',
|
||||
background: 'var(--color-primary)',
|
||||
color: '#fff',
|
||||
fontWeight: 700,
|
||||
};
|
||||
function resolveBackendLabel(mode?: string) {
|
||||
switch (mode) {
|
||||
case 'cloud':
|
||||
return 'Comfy Cloud API 后端';
|
||||
case 'runninghub':
|
||||
return 'RunningHub API 后端';
|
||||
case 'external':
|
||||
return '外部 ComfyUI API 后端';
|
||||
case 'docker':
|
||||
return 'Docker 完整版 ComfyUI';
|
||||
case 'boosapi':
|
||||
return 'BoosAPI 工作流执行器';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user