|
|
@@ -0,0 +1,541 @@
|
|
|
+<template>
|
|
|
+ <div class="orchestration-container">
|
|
|
+ <div class="header">
|
|
|
+ <div>
|
|
|
+ <h1>流程设计</h1>
|
|
|
+ <p class="subtitle">把一堆"要做的事",按顺序、条件、规则,自动地串起来执行</p>
|
|
|
+ </div>
|
|
|
+ <div class="header-actions">
|
|
|
+ <el-button type="primary" @click="createWorkflow">
|
|
|
+ <SvgIcon name="workflow" size="16" />
|
|
|
+ 创建工作流
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="openTemplateModal">
|
|
|
+ <SvgIcon name="box" size="16" />
|
|
|
+ 导入模板
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 编排核心概念 -->
|
|
|
+ <div class="core-concepts">
|
|
|
+ <div class="concept-card sequence">
|
|
|
+ <div class="concept-icon">🔗</div>
|
|
|
+ <div class="concept-info">
|
|
|
+ <div class="concept-title">按顺序</div>
|
|
|
+ <div class="concept-desc">节点A → 节点B → 节点C,串联执行</div>
|
|
|
+ </div>
|
|
|
+ <div class="concept-example">开始 → HTTP请求 → 数据处理 → 发送通知</div>
|
|
|
+ </div>
|
|
|
+ <div class="concept-card condition">
|
|
|
+ <div class="concept-icon">🔀</div>
|
|
|
+ <div class="concept-info">
|
|
|
+ <div class="concept-title">按条件</div>
|
|
|
+ <div class="concept-desc">判断结果,走不同的分支路径</div>
|
|
|
+ </div>
|
|
|
+ <div class="concept-example">if (状态=成功) → 路径A else → 路径B</div>
|
|
|
+ </div>
|
|
|
+ <div class="concept-card rule">
|
|
|
+ <div class="concept-icon">⚙️</div>
|
|
|
+ <div class="concept-info">
|
|
|
+ <div class="concept-title">按规则</div>
|
|
|
+ <div class="concept-desc">触发器、定时任务、事件驱动自动执行</div>
|
|
|
+ </div>
|
|
|
+ <div class="concept-example">每天9点 | Webhook触发 | 数据变化时</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="stats-grid">
|
|
|
+ <div class="stat-card">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <SvgIcon name="workflow" size="22" />
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">28</div>
|
|
|
+ <div class="stat-label">工作流总数</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card">
|
|
|
+ <div class="stat-icon green">
|
|
|
+ <SvgIcon name="clock" size="22" />
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">6</div>
|
|
|
+ <div class="stat-label">近 7 天更新</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card">
|
|
|
+ <div class="stat-icon purple">
|
|
|
+ <SvgIcon name="box" size="22" />
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">12</div>
|
|
|
+ <div class="stat-label">模板可用</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-card">
|
|
|
+ <div class="stat-icon orange">
|
|
|
+ <SvgIcon name="sparkle" size="22" />
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-value">18</div>
|
|
|
+ <div class="stat-label">节点类型</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="content-grid">
|
|
|
+ <div class="panel">
|
|
|
+ <div class="panel-title">最近编排</div>
|
|
|
+ <div class="workflow-list">
|
|
|
+ <div class="workflow-item" v-for="item in recentWorkflows" :key="item.id">
|
|
|
+ <div class="workflow-main">
|
|
|
+ <div class="workflow-title">{{ item.name }}</div>
|
|
|
+ <div class="workflow-meta">{{ item.updatedAt }} · {{ item.owner }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="workflow-tags">
|
|
|
+ <el-tag size="small" type="info">{{ item.tag }}</el-tag>
|
|
|
+ <el-button text size="small" @click="openWorkflow(item.id)">打开</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="panel">
|
|
|
+ <div class="panel-title">可用节点类型</div>
|
|
|
+ <div class="node-types">
|
|
|
+ <div class="node-type-group">
|
|
|
+ <div class="node-type-label">流程控制</div>
|
|
|
+ <div class="node-type-tags">
|
|
|
+ <el-tag size="small" type="info">开始节点</el-tag>
|
|
|
+ <el-tag size="small" type="warning">条件分支</el-tag>
|
|
|
+ <el-tag size="small" type="danger">循环节点</el-tag>
|
|
|
+ <el-tag size="small" type="success">结束节点</el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="node-type-group">
|
|
|
+ <div class="node-type-label">数据操作</div>
|
|
|
+ <div class="node-type-tags">
|
|
|
+ <el-tag size="small">HTTP请求</el-tag>
|
|
|
+ <el-tag size="small">数据库查询</el-tag>
|
|
|
+ <el-tag size="small">代码执行</el-tag>
|
|
|
+ <el-tag size="small">数据转换</el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="node-type-group">
|
|
|
+ <div class="node-type-label">触发规则</div>
|
|
|
+ <div class="node-type-tags">
|
|
|
+ <el-tag size="small" type="info">定时触发</el-tag>
|
|
|
+ <el-tag size="small" type="info">Webhook</el-tag>
|
|
|
+ <el-tag size="small" type="info">事件监听</el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="panel full">
|
|
|
+ <div class="panel-title">推荐模板</div>
|
|
|
+ <div class="template-grid">
|
|
|
+ <div class="template-card" v-for="item in templates" :key="item.id">
|
|
|
+ <div class="template-title">{{ item.name }}</div>
|
|
|
+ <div class="template-desc">{{ item.desc }}</div>
|
|
|
+ <div class="template-footer">
|
|
|
+ <el-tag size="small">{{ item.category }}</el-tag>
|
|
|
+ <el-button text size="small" @click="useTemplate(item.id)">查看模板</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 模板弹窗 -->
|
|
|
+ <TemplateModal
|
|
|
+ :visible="templateModalVisible"
|
|
|
+ @close="closeTemplateModal"
|
|
|
+ @select="selectTemplate"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import SvgIcon from '@/components/SvgIcon/index.vue'
|
|
|
+import TemplateModal from '@/components/TemplateModal/index.vue'
|
|
|
+import { v4 } from 'uuid'
|
|
|
+
|
|
|
+const router = useRouter()
|
|
|
+
|
|
|
+const templateModalVisible = ref(false)
|
|
|
+
|
|
|
+const recentWorkflows = ref([
|
|
|
+ { id: '1', name: '客户支持自动分派', updatedAt: '今天 10:12', owner: '张伟', tag: '客服' },
|
|
|
+ { id: '2', name: '内容生成与审核', updatedAt: '昨天 18:20', owner: '李娜', tag: '内容' },
|
|
|
+ { id: '3', name: 'RAG 知识库同步', updatedAt: '1 月 27 日', owner: '王强', tag: '知识库' },
|
|
|
+ { id: '4', name: '财务报表汇总', updatedAt: '1 月 26 日', owner: '赵敏', tag: '财务' }
|
|
|
+])
|
|
|
+
|
|
|
+const templates = ref([
|
|
|
+ {
|
|
|
+ id: 't1',
|
|
|
+ name: '客户意图识别',
|
|
|
+ desc: '接收消息 → AI分析意图 → 条件判断 → 自动分派客服/机器人',
|
|
|
+ category: '客服'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 't2',
|
|
|
+ name: '日报自动汇总',
|
|
|
+ desc: '定时触发 → 查询数据表 → 汇总计算 → 生成报告 → 通知团队',
|
|
|
+ category: '运营'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 't3',
|
|
|
+ name: '合同审查助手',
|
|
|
+ desc: '上传合同 → OCR识别 → AI审查 → 风险分类 → 生成报告',
|
|
|
+ category: '法务'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 't4',
|
|
|
+ name: '线索打标',
|
|
|
+ desc: 'Webhook接收 → 数据清洗 → 规则匹配 → 打标签 → 写入CRM',
|
|
|
+ category: '增长'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+const createWorkflow = () => {
|
|
|
+ const id = v4()
|
|
|
+ router.push(`/workflow/${id}`)
|
|
|
+}
|
|
|
+
|
|
|
+const openTemplateModal = () => {
|
|
|
+ templateModalVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const closeTemplateModal = () => {
|
|
|
+ templateModalVisible.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const selectTemplate = () => {
|
|
|
+ templateModalVisible.value = false
|
|
|
+ // 根据模板ID创建新工作流
|
|
|
+ const id = v4()
|
|
|
+ router.push(`/templates/${id}`)
|
|
|
+}
|
|
|
+
|
|
|
+const openWorkflow = (id: string) => {
|
|
|
+ router.push(`/workflow/${id}`)
|
|
|
+}
|
|
|
+
|
|
|
+const useTemplate = (id: string) => {
|
|
|
+ router.push(`/templates/${id}`)
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+.orchestration-container {
|
|
|
+ max-width: 1200px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 24px;
|
|
|
+
|
|
|
+ .header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 24px;
|
|
|
+
|
|
|
+ h1 {
|
|
|
+ font-size: 28px;
|
|
|
+ margin: 0;
|
|
|
+ color: #222;
|
|
|
+ }
|
|
|
+
|
|
|
+ .subtitle {
|
|
|
+ margin: 6px 0 0;
|
|
|
+ color: #666;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ :deep(.el-button) {
|
|
|
+ &:not(:last-child) {
|
|
|
+ margin-right: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .core-concepts {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+
|
|
|
+ @media (max-width: 900px) {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+
|
|
|
+ .concept-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
+ border-left: 4px solid #3b82f6;
|
|
|
+
|
|
|
+ &.sequence {
|
|
|
+ border-left-color: #3b82f6;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.condition {
|
|
|
+ border-left-color: #f59e0b;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.rule {
|
|
|
+ border-left-color: #10b981;
|
|
|
+ }
|
|
|
+
|
|
|
+ .concept-icon {
|
|
|
+ font-size: 32px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .concept-info {
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .concept-title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1f2937;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .concept-desc {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #6b7280;
|
|
|
+ line-height: 1.6;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .concept-example {
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 10px 12px;
|
|
|
+ background: #f9fafb;
|
|
|
+ border-radius: 8px;
|
|
|
+ color: #374151;
|
|
|
+ font-family: 'Consolas', 'Monaco', monospace;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .stats-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+
|
|
|
+ .stat-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 18px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
+
|
|
|
+ .stat-icon {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 10px;
|
|
|
+ background: #eef2ff;
|
|
|
+ color: #4f46e5;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ &.green {
|
|
|
+ background: #ecfdf3;
|
|
|
+ color: #10b981;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.purple {
|
|
|
+ background: #f5f3ff;
|
|
|
+ color: #7c3aed;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.orange {
|
|
|
+ background: #fff7ed;
|
|
|
+ color: #f97316;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-info {
|
|
|
+ .stat-value {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1f2937;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #6b7280;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .content-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 2fr 1fr;
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ @media (max-width: 900px) {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .panel {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
+
|
|
|
+ &.full {
|
|
|
+ margin-top: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .panel-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ color: #1f2937;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .workflow-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ .workflow-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12px;
|
|
|
+ border-radius: 10px;
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
+
|
|
|
+ .workflow-title {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #111827;
|
|
|
+ }
|
|
|
+
|
|
|
+ .workflow-meta {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #6b7280;
|
|
|
+ margin-top: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .workflow-tags {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .feature-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+
|
|
|
+ .feature-item {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: flex-start;
|
|
|
+
|
|
|
+ .feature-icon {
|
|
|
+ width: 36px;
|
|
|
+ height: 36px;
|
|
|
+ border-radius: 10px;
|
|
|
+ background: #f3f4f6;
|
|
|
+ color: #374151;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .feature-title {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #111827;
|
|
|
+ }
|
|
|
+
|
|
|
+ .feature-desc {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #6b7280;
|
|
|
+ margin-top: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .node-types {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+
|
|
|
+ .node-type-group {
|
|
|
+ .node-type-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #6b7280;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .node-type-tags {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .template-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
|
+ gap: 16px;
|
|
|
+
|
|
|
+ .template-card {
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 16px;
|
|
|
+ background: #fcfcff;
|
|
|
+
|
|
|
+ .template-title {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #111827;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .template-desc {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #6b7280;
|
|
|
+ line-height: 1.6;
|
|
|
+ min-height: 54px;
|
|
|
+ font-family: 'Consolas', 'Monaco', monospace;
|
|
|
+ background: #f9fafb;
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 6px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .template-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|