index.vue 6.0 KB

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