Skip to content

Commit 9262fe5

Browse files
committed
feat: AI工作流优化
1 parent 0539300 commit 9262fe5

File tree

7 files changed

+384
-225
lines changed

7 files changed

+384
-225
lines changed

src/router/modules/remaining.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,16 +668,27 @@ const remainingRouter: AppRouteRecordRaw[] = [
668668
activeMenu: '/ai/knowledge'
669669
}
670670
},
671-
// TODO @lesan::type =》 design 设计 AI 工作流
671+
{
672+
path: 'console/workflow/create',
673+
component: () => import('@/views/ai/workflow/form/index.vue'),
674+
name: 'AiWorkflowCreate',
675+
meta: {
676+
noCache: true,
677+
hidden: true,
678+
canTo: true,
679+
title: '设计 AI 工作流',
680+
activeMenu: '/ai/console/workflow'
681+
}
682+
},
672683
{
673684
path: 'console/workflow/:type/:id',
674-
component: () => import('@/views/ai/workflow/manager/WorkflowModelForm.vue'),
685+
component: () => import('@/views/ai/workflow/form/index.vue'),
675686
name: 'AiWorkflowUpdate',
676687
meta: {
677688
noCache: true,
678689
hidden: true,
679690
canTo: true,
680-
title: '修改 AI 工作流',
691+
title: '设计 AI 工作流',
681692
activeMenu: '/ai/console/workflow'
682693
}
683694
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<template>
2+
<el-form ref="formRef" :model="modelData" :rules="formRules" label-width="120px">
3+
<el-row>
4+
<el-col :span="24">
5+
<el-form-item label="流程标识" prop="code">
6+
<el-input v-model="modelData.code" placeholder="请输入流程标识" />
7+
</el-form-item>
8+
</el-col>
9+
<el-col :span="24">
10+
<el-form-item label="流程名称" prop="name">
11+
<el-input v-model="modelData.name" placeholder="请输入流程名称" />
12+
</el-form-item>
13+
</el-col>
14+
<el-col :span="24">
15+
<el-form-item label="状态" prop="status">
16+
<el-select v-model="modelData.status" placeholder="请选择状态">
17+
<el-option
18+
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
19+
:key="dict.value"
20+
:label="dict.label"
21+
:value="dict.value"
22+
/>
23+
</el-select>
24+
</el-form-item>
25+
</el-col>
26+
<el-col :span="24">
27+
<el-form-item label="备注" prop="remark">
28+
<el-input v-model="modelData.remark" :rows="2" type="textarea" placeholder="请输入备注" />
29+
</el-form-item>
30+
</el-col>
31+
</el-row>
32+
</el-form>
33+
</template>
34+
<script lang="ts" setup>
35+
import { FormRules } from 'element-plus'
36+
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
37+
38+
const modelData = defineModel<any>()
39+
40+
const formRef = ref() // 表单 Ref
41+
const formRules = reactive<FormRules>({
42+
code: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
43+
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
44+
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
45+
})
46+
47+
/** 表单校验 */
48+
const validate = async () => {
49+
await formRef.value?.validate()
50+
}
51+
defineExpose({
52+
validate
53+
})
54+
</script>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<template>
2+
<div class="relative" style="width: 100%; height: 700px">
3+
<Tinyflow
4+
v-if="workflowData"
5+
ref="tinyflowRef"
6+
:className="'custom-class'"
7+
:style="{ width: '100%', height: '100%' }"
8+
:data="workflowData"
9+
:provider="provider"
10+
/>
11+
<div class="absolute top-30px right-30px">
12+
<el-button @click="testWorkflowModel" type="primary" v-hasPermi="['ai:workflow:test']">
13+
测试
14+
</el-button>
15+
</div>
16+
</div>
17+
</template>
18+
19+
<script setup lang="ts">
20+
import Tinyflow from '@/components/Tinyflow/Tinyflow.vue'
21+
22+
defineProps<{
23+
provider: any
24+
}>()
25+
26+
const tinyflowRef = ref()
27+
const workflowData = inject('workflowData') as Ref
28+
29+
const testWorkflowModel = () => {
30+
// TODO @lesan 测试
31+
}
32+
33+
/** 表单校验 */
34+
const validate = async () => {
35+
try {
36+
// 获取最新的流程数据
37+
if (!workflowData.value) {
38+
throw new Error('请设计流程')
39+
}
40+
workflowData.value = tinyflowRef.value.getData()
41+
return true
42+
} catch (error) {
43+
throw error
44+
}
45+
}
46+
defineExpose({
47+
validate
48+
})
49+
</script>

src/views/ai/workflow/form/index.vue

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
<template>
2+
<ContentWrap>
3+
<div class="mx-auto">
4+
<!-- 头部导航栏 -->
5+
<div
6+
class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px"
7+
>
8+
<!-- 左侧标题 -->
9+
<div class="w-200px flex items-center overflow-hidden">
10+
<Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" />
11+
<span class="ml-10px text-16px truncate" :title="formData.name || '创建流程'">
12+
{{ formData.name || '创建流程' }}
13+
</span>
14+
</div>
15+
16+
<!-- 步骤条 -->
17+
<div class="flex-1 flex items-center justify-center h-full">
18+
<div class="w-400px flex items-center justify-between h-full">
19+
<div
20+
v-for="(step, index) in steps"
21+
:key="index"
22+
class="flex items-center cursor-pointer mx-15px relative h-full"
23+
:class="[
24+
currentStep === index
25+
? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid'
26+
: 'text-gray-500'
27+
]"
28+
@click="handleStepClick(index)"
29+
>
30+
<div
31+
class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px"
32+
:class="[
33+
currentStep === index
34+
? 'bg-[#3473ff] text-white border-[#3473ff]'
35+
: 'border-gray-300 bg-white text-gray-500'
36+
]"
37+
>
38+
{{ index + 1 }}
39+
</div>
40+
<span class="text-16px font-bold whitespace-nowrap">{{ step.title }}</span>
41+
</div>
42+
</div>
43+
</div>
44+
45+
<!-- 右侧按钮 -->
46+
<div class="w-200px flex items-center justify-end gap-2">
47+
<el-button type="primary" @click="handleSave"> 保 存 </el-button>
48+
</div>
49+
</div>
50+
51+
<!-- 主体内容 -->
52+
<div class="mt-50px">
53+
<!-- 第一步:基本信息 -->
54+
<div v-if="currentStep === 0" class="mx-auto w-560px">
55+
<BasicInfo v-model="formData" ref="basicInfoRef" />
56+
</div>
57+
58+
<!-- 第二步:工作流设计 -->
59+
<WorkflowDesign
60+
v-if="currentStep === 1"
61+
v-model="formData"
62+
:provider="provider"
63+
ref="workflowDesignRef"
64+
/>
65+
</div>
66+
</div>
67+
</ContentWrap>
68+
</template>
69+
70+
<script setup lang="ts">
71+
import { useTagsViewStore } from '@/store/modules/tagsView'
72+
import { CommonStatusEnum } from '@/utils/constants'
73+
import * as WorkflowApi from '@/api/ai/workflow'
74+
import BasicInfo from './BasicInfo.vue'
75+
import WorkflowDesign from './WorkflowDesign.vue'
76+
import { ApiKeyApi } from '@/api/ai/model/apiKey'
77+
78+
const router = useRouter()
79+
const { delView } = useTagsViewStore()
80+
const route = useRoute()
81+
const message = useMessage()
82+
83+
const basicInfoRef = ref()
84+
const workflowDesignRef = ref()
85+
86+
const validateBasic = async () => {
87+
await basicInfoRef.value?.validate()
88+
}
89+
const validateWorkflow = async () => {
90+
await workflowDesignRef.value?.validate()
91+
}
92+
93+
const currentStep = ref(-1)
94+
const steps = [
95+
{ title: '基本信息', validator: validateBasic },
96+
{ title: '工作流设计', validator: validateWorkflow }
97+
]
98+
99+
const formData: any = ref({
100+
id: undefined,
101+
name: '',
102+
code: '',
103+
remark: '',
104+
graph: '',
105+
status: CommonStatusEnum.ENABLE
106+
})
107+
// TODO @lesan:待接入
108+
const provider = ref<any>()
109+
const workflowData = ref<any>({})
110+
provide('workflowData', workflowData)
111+
112+
/** 初始化数据 */
113+
const actionType = route.params.type as string
114+
const initData = async () => {
115+
if (actionType === 'update') {
116+
const workflowId = route.params.id as string
117+
formData.value = await WorkflowApi.getWorkflow(workflowId)
118+
workflowData.value = JSON.parse(formData.value.graph)
119+
}
120+
121+
const apiKeys = await ApiKeyApi.getApiKeySimpleList()
122+
provider.value = {
123+
llm: () =>
124+
apiKeys.map(({ id, name }) => ({
125+
value: id,
126+
label: name
127+
})),
128+
knowledge: () => [],
129+
internal: () => []
130+
}
131+
132+
currentStep.value = 0
133+
}
134+
135+
/** 校验所有步骤数据是否完整 */
136+
const validateAllSteps = async () => {
137+
try {
138+
// 基本信息校验
139+
try {
140+
await validateBasic()
141+
} catch (error) {
142+
currentStep.value = 0
143+
throw new Error('请完善基本信息')
144+
}
145+
146+
// 工作流设计校验
147+
try {
148+
await validateWorkflow()
149+
} catch (error) {
150+
currentStep.value = 1
151+
throw new Error('请完善工作流信息')
152+
}
153+
return true
154+
} catch (error) {
155+
throw error
156+
}
157+
}
158+
159+
/** 保存操作 */
160+
const handleSave = async () => {
161+
try {
162+
// 保存前校验所有步骤的数据
163+
await validateAllSteps()
164+
165+
// 更新表单数据
166+
const data = {
167+
...formData.value
168+
}
169+
170+
data.graph = JSON.stringify(workflowData.value)
171+
172+
if (actionType === 'update') {
173+
await WorkflowApi.updateWorkflow(data)
174+
} else {
175+
await WorkflowApi.createWorkflow(data)
176+
}
177+
178+
delView(unref(router.currentRoute))
179+
await router.push({ name: 'AiWorkflow' })
180+
} catch (error: any) {
181+
console.error('保存失败:', error)
182+
message.warning(error.message || '请完善所有步骤的必填信息')
183+
}
184+
}
185+
186+
/** 步骤切换处理 */
187+
const handleStepClick = async (index: number) => {
188+
try {
189+
if (index !== 0) {
190+
await validateBasic()
191+
}
192+
if (index !== 1) {
193+
await validateWorkflow()
194+
}
195+
196+
// 切换步骤
197+
currentStep.value = index
198+
} catch (error) {
199+
console.error('步骤切换失败:', error)
200+
message.warning('请先完善当前步骤必填信息')
201+
}
202+
}
203+
204+
/** 返回列表页 */
205+
const handleBack = () => {
206+
// 先删除当前页签
207+
delView(unref(router.currentRoute))
208+
// 跳转到列表页
209+
router.push({ name: 'AiWorkflow' })
210+
}
211+
212+
/** 初始化 */
213+
onMounted(async () => {
214+
await initData()
215+
})
216+
</script>
217+
218+
<style lang="scss" scoped>
219+
.border-bottom {
220+
border-bottom: 1px solid #dcdfe6;
221+
}
222+
223+
.text-primary {
224+
color: #3473ff;
225+
}
226+
227+
.bg-primary {
228+
background-color: #3473ff;
229+
}
230+
231+
.border-primary {
232+
border-color: #3473ff;
233+
}
234+
</style>

0 commit comments

Comments
 (0)