前端 + AI 进阶 Day 14:工作流模板市场
前端 + AI 进阶学习路线|Week 11-12:智能工作流前端
Day 14:工作流模板市场
学习时间:2026年1月7日(星期三)
关键词:工作流模板、模板保存/导入、模板分享、模板市场、JSON 序列化
📁 项目文件结构
day14-workflow-marketplace/
├── src/
│ ├── components/
│ │ ├── WorkflowCanvas.jsx # 复用 Day 13 画布
│ │ ├── ExecutionPanel.jsx # 复用 Day 13 执行面板
│ │ ├── ControlsPanel.jsx # 扩展:模板管理
│ │ └── TemplateGallery.jsx # 新增:模板画廊
│ ├── lib/
│ │ └── workflowTemplates.js # 模板存储与管理
│ └── App.jsx # 主应用集成
└── public/
✅ 本日核心:让工作流可复用、可分享、可发现——构建前端工作流模板市场
🎯 今日学习目标
- 实现 工作流模板保存/导入(本地 + 在线)
- 构建 模板画廊(展示预设模板)
- 支持 一键应用模板到画布
- 实现 模板分享链接(URL 编码)
💡 为什么需要模板市场?
用户重复造轮子:
- 每次都需从零搭建“图片分析”工作流
- 优秀工作流无法被团队复用
- 新用户不知如何开始
✅ 模板即资产,让工作流从“一次性脚本”变为“可复用解决方案”
📚 核心设计思路
| 功能 | 实现方式 |
|---|---|
| 模板存储 | 本地:localStorage;在线:URL Hash / 后端 API(本日用 URL) |
| 模板结构 | { id, name, description, nodes, edges, createdAt } |
| 分享链接 | encodeURIComponent(JSON.stringify(workflow)) → ?template=... |
| 预设模板 | 内置常用工作流(如“多模态分析”、“代码生成”) |
⚠️ 注意:URL 长度有限(~2000 字符),复杂模板需压缩或后端存储
🔧 动手实践:构建工作流模板市场
步骤 1:创建项目并复用前期组件
npx create-react-app day14-workflow-marketplace
cd day14-workflow-marketplace
npm install reactflow
# 复制 Day 12-13 的 components/ 和 lib/
步骤 2:创建模板管理库
// src/lib/workflowTemplates.js
// 预设模板
export const PRESET_TEMPLATES = [
{
id: 'image-analysis',
name: '🖼️ 多模态图片分析',
description: '上传图片 → AI 标注 → 生成报告',
nodes: [
{ id: '1', type: 'input', position: { x: 50, y: 150 }, { label: '上传图片' } },
{ id: '2', type: 'ai', position: { x: 300, y: 100 }, { label: 'LLaVA 视觉分析' } },
{ id: '3', type: 'ai', position: { x: 300, y: 200 }, { label: '生成结构化报告' } },
{ id: '4', type: 'output', position: { x: 550, y: 150 }, data: { label: '分析报告' } },
],
edges: [
{ id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e1-3', source: '1', target: '3', animated: true },
{ id: 'e2-4', source: '2', target: '4', animated: true },
{ id: 'e3-4', source: '3', target: '4', animated: true },
]
},
{
id: 'code-generation',
name: '💻 代码生成与测试',
description: '描述需求 → 生成代码 → 单元测试',
nodes: [
{ id: '1', type: 'input', position: { x: 50, y: 150 }, { label: '需求描述' } },
{ id: '2', type: 'ai', position: { x: 300, y: 150 }, { label: '生成代码' } },
{ id: '3', type: 'ai', position: { x: 550, y: 150 }, { label: '生成测试用例' } },
{ id: '4', type: 'output', position: { x: 800, y: 150 }, data: { label: '完整代码包' } },
],
edges: [
{ id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e2-3', source: '2', target: '3', animated: true },
{ id: 'e3-4', source: '3', target: '4', animated: true },
]
},
{
id: 'data-summarization',
name: '📊 文本摘要流水线',
description: '输入长文本 → 关键信息提取 → 生成摘要',
nodes: [
{ id: '1', type: 'input', position: { x: 50, y: 150 }, { label: '长文本输入' } },
{ id: '2', type: 'ai', position: { x: 300, y: 150 }, { label: '实体识别' } },
{ id: '3', type: 'ai', position: { x: 550, y: 150 }, { label: '生成摘要' } },
{ id: '4', type: 'output', position: { x: 800, y: 150 }, data: { label: '摘要结果' } },
],
edges: [
{ id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e2-3', source: '2', target: '3', animated: true },
{ id: 'e3-4', source: '3', target: '4', animated: true },
]
}
];
// 保存模板到 URL
export const saveTemplateToUrl = (workflow) => {
try {
const serialized = JSON.stringify({
nodes: workflow.nodes,
edges: workflow.edges
});
const encoded = encodeURIComponent(serialized);
const url = `${window.location.origin}${window.location.pathname}?template=${encoded}`;
return url;
} catch (e) {
console.error('Failed to serialize workflow', e);
return null;
}
};
// 从 URL 加载模板
export const loadTemplateFromUrl = () => {
const urlParams = new URLSearchParams(window.location.search);
const templateParam = urlParams.get('template');
if (templateParam) {
try {
const decoded = decodeURIComponent(templateParam);
const workflow = JSON.parse(decoded);
return workflow;
} catch (e) {
console.error('Failed to parse template from URL', e);
return null;
}
}
return null;
};
// 保存到本地模板库
export const saveTemplateLocally = (template) => {
const existing = JSON.parse(localStorage.getItem('custom_templates') || '[]');
const newTemplate = {
...template,
id: `custom-${Date.now()}`,
name: template.name || '自定义模板',
createdAt: Date.now()
};
localStorage.setItem('custom_templates', JSON.stringify([...existing, newTemplate]));
return newTemplate;
};
// 加载本地模板
export const loadLocalTemplates = () => {
return JSON.parse(localStorage.getItem('custom_templates') || '[]');
};
步骤 3:创建模板画廊组件
// src/components/TemplateGallery.jsx
import { PRESET_TEMPLATES, loadLocalTemplates, saveTemplateToUrl } from '../lib/workflowTemplates';
export default function TemplateGallery({ onApplyTemplate }) {
const localTemplates = loadLocalTemplates();
const allTemplates = [...PRESET_TEMPLATES, ...localTemplates];
const handleShare = (template) => {
const url = saveTemplateToUrl(template);
if (url) {
navigator.clipboard.writeText(url).then(() => {
alert('📋 模板分享链接已复制到剪贴板!');
});
}
};
return (
<div style={{
padding: '16px',
backgroundColor: '#fafafa',
width: '300px',
display: 'flex',
flexDirection: 'column',
gap: '16px'
}}>
<h3 style={{ margin: 0, fontSize: '18px', fontWeight: '600' }}>🧩 工作流模板市场</h3>
{allTemplates.length === 0 ? (
<div style={{ color: '#888', fontSize: '14px' }}>
暂无自定义模板,保存当前工作流以创建模板。
</div>
) : (
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{allTemplates.map((template) => (
<div
key={template.id}
style={{
border: '1px solid #e8e8e8',
borderRadius: '8px',
padding: '12px',
backgroundColor: '#fff',
position: 'relative'
}}
>
<div style={{ fontWeight: '600', marginBottom: '4px' }}>
{template.name}
</div>
<div style={{ fontSize: '12px', color: '#666', marginBottom: '8px' }}>
{template.description}
</div>
<div style={{ display: 'flex', gap: '8px' }}>
<button
onClick={() => onApplyTemplate(template)}
style={{
padding: '4px 8px',
fontSize: '12px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
应用
</button>
<button
onClick={() => handleShare(template)}
style={{
padding: '4px 8px',
fontSize: '12px',
backgroundColor: '#52c41a',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
分享
</button>
</div>
{template.createdAt && (
<div style={{
position: 'absolute',
bottom: '4px',
right: '8px',
fontSize: '10px',
color: '#999'
}}>
自定义
</div>
)}
</div>
))}
</div>
)}
</div>
);
}
步骤 4:扩展控制面板(保存/导入)
// src/components/ControlsPanel.jsx(更新版)
import { saveTemplateLocally, loadTemplateFromUrl } from '../lib/workflowTemplates';
export default function ControlsPanel({
nodes,
edges,
onApplyTemplate,
onResetCanvas
}) {
// 检查 URL 模板
React.useEffect(() => {
const urlTemplate = loadTemplateFromUrl();
if (urlTemplate) {
onApplyTemplate(urlTemplate);
// 清除 URL 参数(可选)
window.history.replaceState({}, document.title, window.location.pathname);
}
}, [onApplyTemplate]);
const handleSaveTemplate = () => {
if (nodes.length === 0) {
alert('请先创建工作流!');
return;
}
const templateName = prompt('模板名称:', '我的工作流');
if (templateName) {
const newTemplate = saveTemplateLocally({
name: templateName,
nodes,
edges
});
alert(`✅ 模板 "${templateName}" 已保存!`);
}
};
const handleImportFromFile = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = (e) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
const workflow = JSON.parse(event.target.result);
onApplyTemplate(workflow);
} catch (e) {
alert('❌ 无效的模板文件');
}
};
reader.readAsText(file);
};
input.click();
};
return (
<div style={{
padding: '16px',
borderRight: '1px solid #e8e8e8',
backgroundColor: '#fafafa',
width: '200px',
display: 'flex',
flexDirection: 'column',
gap: '12px'
}}>
<h3 style={{ margin: 0, fontSize: '16px', fontWeight: '600' }}>🛠️ 操作</h3>
<button
onClick={handleSaveTemplate}
style={{
padding: '8px',
backgroundColor: '#52c41a',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
width: '100%',
fontSize: '14px',
}}
>
💾 保存为模板
</button>
<button
onClick={handleImportFromFile}
style={{
padding: '8px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
width: '100%',
fontSize: '14px',
marginTop: '8px'
}}
>
📂 从文件导入
</button>
<button
onClick={onResetCanvas}
style={{
padding: '8px',
backgroundColor: '#f5222d',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
width: '100%',
fontSize: '14px',
marginTop: '8px'
}}
>
🗑️ 清空画布
</button>
</div>
);
}
步骤 5:在 App.jsx 中集成模板市场
// src/App.jsx
import React, { useState, useEffect } from 'react';
import WorkflowCanvas from './components/WorkflowCanvas';
import ExecutionPanel from './components/ExecutionPanel';
import ControlsPanel from './components/ControlsPanel';
import TemplateGallery from './components/TemplateGallery';
import { PRESET_TEMPLATES } from './lib/workflowTemplates';
function App() {
const [nodes, setNodes] = useState(PRESET_TEMPLATES[0].nodes); // 默认加载第一个模板
const [edges, setEdges] = useState(PRESET_TEMPLATES[0].edges);
const applyTemplate = (template) => {
setNodes(template.nodes || []);
setEdges(template.edges || []);
};
const resetCanvas = () => {
setNodes([]);
setEdges([]);
};
return (
<div style={{
fontFamily: 'Inter, -apple-system, sans-serif',
width: '100vw',
height: '100vh',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
<ControlsPanel
nodes={nodes}
edges={edges}
onApplyTemplate={applyTemplate}
onResetCanvas={resetCanvas}
/>
<WorkflowCanvas
nodes={nodes}
edges={edges}
onNodesChange={setNodes}
onEdgesChange={setEdges}
/>
<TemplateGallery onApplyTemplate={applyTemplate} />
</div>
<ExecutionPanel
nodes={nodes}
edges={edges}
onUpdateNode={(id, status) => {
setNodes(prev => prev.map(n =>
n.id === id ? { ...n, { ...n.data, status } } : n
));
}}
onResetNodes={() => {
setNodes(prev => prev.map(n => ({ ...n, { ...n.data, status: 'idle' } })));
}}
/>
</div>
);
}
export default App;
✅ 效果验证
- ✅ 画布默认加载“多模态图片分析”模板
- ✅ 点击模板画廊中的“应用” → 画布更新为该模板
- ✅ 点击“💾 保存为模板” → 输入名称 → 模板出现在画廊
- ✅ 点击“分享” → 链接复制到剪贴板(格式:
?template=...) - ✅ 打开分享链接 → 自动加载模板
- ✅ “从文件导入” → 选择 JSON 文件 → 加载工作流
🤔 思考与延伸
-
模板压缩:如何缩短 URL 长度?
→ 使用lz-string压缩 JSON:npm install lz-string -
在线模板库:如何实现真正的模板市场?
→ 后端存储模板 + 搜索/评分/分类 API -
版本管理:如何支持模板更新?
→ 添加version字段,提供“升级”功能
💡 终极目标:将
ai-frontend-kit的工作流能力封装为可嵌入的<WorkflowBuilder />组件
📅 项目收尾
3 个月学习完成!
- 你已掌握 前端 AI 体验优化、多模态交互、AI SDK、调试工具、对话设计、工作流编排 全栈能力
- 所有代码可整合为
ai-frontend-kit开源项目 - 部署到 Vercel,全球可访问!
✍️ 小结
今天,我们为工作流系统画上完美句号!通过模板市场,用户可轻松复用、分享、发现优秀工作流,真正实现 AI 能力的民主化。可复用性,是工程价值的终极体现。
💬 最后建议:将 14 天的成果整合为 monorepo,发布
ai-frontend-kit到 npm,开源到 GitHub!你已具备构建下一代 AI 原生应用的全部能力。🎉

浙公网安备 33010602011771号