|
@@ -0,0 +1,234 @@
|
|
|
|
+<template>
|
|
|
|
+ <ContentWrap>
|
|
|
|
+ <div class="mx-auto">
|
|
|
|
+ <!-- 头部导航栏 -->
|
|
|
|
+ <div
|
|
|
|
+ class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px"
|
|
|
|
+ >
|
|
|
|
+ <!-- 左侧标题 -->
|
|
|
|
+ <div class="w-200px flex items-center overflow-hidden">
|
|
|
|
+ <Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" />
|
|
|
|
+ <span class="ml-10px text-16px truncate" :title="formData.name || '创建流程'">
|
|
|
|
+ {{ formData.name || '创建流程' }}
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 步骤条 -->
|
|
|
|
+ <div class="flex-1 flex items-center justify-center h-full">
|
|
|
|
+ <div class="w-400px flex items-center justify-between h-full">
|
|
|
|
+ <div
|
|
|
|
+ v-for="(step, index) in steps"
|
|
|
|
+ :key="index"
|
|
|
|
+ class="flex items-center cursor-pointer mx-15px relative h-full"
|
|
|
|
+ :class="[
|
|
|
|
+ currentStep === index
|
|
|
|
+ ? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid'
|
|
|
|
+ : 'text-gray-500'
|
|
|
|
+ ]"
|
|
|
|
+ @click="handleStepClick(index)"
|
|
|
|
+ >
|
|
|
|
+ <div
|
|
|
|
+ class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px"
|
|
|
|
+ :class="[
|
|
|
|
+ currentStep === index
|
|
|
|
+ ? 'bg-[#3473ff] text-white border-[#3473ff]'
|
|
|
|
+ : 'border-gray-300 bg-white text-gray-500'
|
|
|
|
+ ]"
|
|
|
|
+ >
|
|
|
|
+ {{ index + 1 }}
|
|
|
|
+ </div>
|
|
|
|
+ <span class="text-16px font-bold whitespace-nowrap">{{ step.title }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 右侧按钮 -->
|
|
|
|
+ <div class="w-200px flex items-center justify-end gap-2">
|
|
|
|
+ <el-button type="primary" @click="handleSave"> 保 存 </el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 主体内容 -->
|
|
|
|
+ <div class="mt-50px">
|
|
|
|
+ <!-- 第一步:基本信息 -->
|
|
|
|
+ <div v-if="currentStep === 0" class="mx-auto w-560px">
|
|
|
|
+ <BasicInfo v-model="formData" ref="basicInfoRef" />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 第二步:工作流设计 -->
|
|
|
|
+ <WorkflowDesign
|
|
|
|
+ v-if="currentStep === 1"
|
|
|
|
+ v-model="formData"
|
|
|
|
+ :provider="provider"
|
|
|
|
+ ref="workflowDesignRef"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </ContentWrap>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
|
|
+import { CommonStatusEnum } from '@/utils/constants'
|
|
|
|
+import * as WorkflowApi from '@/api/ai/workflow'
|
|
|
|
+import BasicInfo from './BasicInfo.vue'
|
|
|
|
+import WorkflowDesign from './WorkflowDesign.vue'
|
|
|
|
+import { ApiKeyApi } from '@/api/ai/model/apiKey'
|
|
|
|
+
|
|
|
|
+const router = useRouter()
|
|
|
|
+const { delView } = useTagsViewStore()
|
|
|
|
+const route = useRoute()
|
|
|
|
+const message = useMessage()
|
|
|
|
+
|
|
|
|
+const basicInfoRef = ref()
|
|
|
|
+const workflowDesignRef = ref()
|
|
|
|
+
|
|
|
|
+const validateBasic = async () => {
|
|
|
|
+ await basicInfoRef.value?.validate()
|
|
|
|
+}
|
|
|
|
+const validateWorkflow = async () => {
|
|
|
|
+ await workflowDesignRef.value?.validate()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const currentStep = ref(-1)
|
|
|
|
+const steps = [
|
|
|
|
+ { title: '基本信息', validator: validateBasic },
|
|
|
|
+ { title: '工作流设计', validator: validateWorkflow }
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+const formData: any = ref({
|
|
|
|
+ id: undefined,
|
|
|
|
+ name: '',
|
|
|
|
+ code: '',
|
|
|
|
+ remark: '',
|
|
|
|
+ graph: '',
|
|
|
|
+ status: CommonStatusEnum.ENABLE
|
|
|
|
+})
|
|
|
|
+// TODO @lesan:待接入
|
|
|
|
+const provider = ref<any>()
|
|
|
|
+const workflowData = ref<any>({})
|
|
|
|
+provide('workflowData', workflowData)
|
|
|
|
+
|
|
|
|
+/** 初始化数据 */
|
|
|
|
+const actionType = route.params.type as string
|
|
|
|
+const initData = async () => {
|
|
|
|
+ if (actionType === 'update') {
|
|
|
|
+ const workflowId = route.params.id as string
|
|
|
|
+ formData.value = await WorkflowApi.getWorkflow(workflowId)
|
|
|
|
+ workflowData.value = JSON.parse(formData.value.graph)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const apiKeys = await ApiKeyApi.getApiKeySimpleList()
|
|
|
|
+ provider.value = {
|
|
|
|
+ llm: () =>
|
|
|
|
+ apiKeys.map(({ id, name }) => ({
|
|
|
|
+ value: id,
|
|
|
|
+ label: name
|
|
|
|
+ })),
|
|
|
|
+ knowledge: () => [],
|
|
|
|
+ internal: () => []
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ currentStep.value = 0
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** 校验所有步骤数据是否完整 */
|
|
|
|
+const validateAllSteps = async () => {
|
|
|
|
+ try {
|
|
|
|
+ // 基本信息校验
|
|
|
|
+ try {
|
|
|
|
+ await validateBasic()
|
|
|
|
+ } catch (error) {
|
|
|
|
+ currentStep.value = 0
|
|
|
|
+ throw new Error('请完善基本信息')
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 工作流设计校验
|
|
|
|
+ try {
|
|
|
|
+ await validateWorkflow()
|
|
|
|
+ } catch (error) {
|
|
|
|
+ currentStep.value = 1
|
|
|
|
+ throw new Error('请完善工作流信息')
|
|
|
|
+ }
|
|
|
|
+ return true
|
|
|
|
+ } catch (error) {
|
|
|
|
+ throw error
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** 保存操作 */
|
|
|
|
+const handleSave = async () => {
|
|
|
|
+ try {
|
|
|
|
+ // 保存前校验所有步骤的数据
|
|
|
|
+ await validateAllSteps()
|
|
|
|
+
|
|
|
|
+ // 更新表单数据
|
|
|
|
+ const data = {
|
|
|
|
+ ...formData.value
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ data.graph = JSON.stringify(workflowData.value)
|
|
|
|
+
|
|
|
|
+ if (actionType === 'update') {
|
|
|
|
+ await WorkflowApi.updateWorkflow(data)
|
|
|
|
+ } else {
|
|
|
|
+ await WorkflowApi.createWorkflow(data)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ delView(unref(router.currentRoute))
|
|
|
|
+ await router.push({ name: 'AiWorkflow' })
|
|
|
|
+ } catch (error: any) {
|
|
|
|
+ console.error('保存失败:', error)
|
|
|
|
+ message.warning(error.message || '请完善所有步骤的必填信息')
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** 步骤切换处理 */
|
|
|
|
+const handleStepClick = async (index: number) => {
|
|
|
|
+ try {
|
|
|
|
+ if (index !== 0) {
|
|
|
|
+ await validateBasic()
|
|
|
|
+ }
|
|
|
|
+ if (index !== 1) {
|
|
|
|
+ await validateWorkflow()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 切换步骤
|
|
|
|
+ currentStep.value = index
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('步骤切换失败:', error)
|
|
|
|
+ message.warning('请先完善当前步骤必填信息')
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** 返回列表页 */
|
|
|
|
+const handleBack = () => {
|
|
|
|
+ // 先删除当前页签
|
|
|
|
+ delView(unref(router.currentRoute))
|
|
|
|
+ // 跳转到列表页
|
|
|
|
+ router.push({ name: 'AiWorkflow' })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** 初始化 */
|
|
|
|
+onMounted(async () => {
|
|
|
|
+ await initData()
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
+.border-bottom {
|
|
|
|
+ border-bottom: 1px solid #dcdfe6;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.text-primary {
|
|
|
|
+ color: #3473ff;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.bg-primary {
|
|
|
|
+ background-color: #3473ff;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.border-primary {
|
|
|
|
+ border-color: #3473ff;
|
|
|
|
+}
|
|
|
|
+</style>
|