mirror of
https://github.com/yudaocode/yudao-ui-admin-vue3.git
synced 2026-04-19 08:07:27 +00:00
251 lines
6.9 KiB
Vue
251 lines
6.9 KiB
Vue
<template>
|
||
<div class="relative" style="width: 100%; height: 700px">
|
||
<Tinyflow
|
||
v-if="workflowData"
|
||
ref="tinyflowRef"
|
||
:className="'custom-class'"
|
||
:style="{ width: '100%', height: '100%' }"
|
||
:data="workflowData"
|
||
:provider="provider"
|
||
/>
|
||
<div class="absolute top-30px right-30px">
|
||
<el-button @click="testWorkflowModel" type="primary" v-hasPermi="['ai:workflow:test']">
|
||
测试
|
||
</el-button>
|
||
</div>
|
||
|
||
<!-- 测试窗口 -->
|
||
<el-drawer v-model="showTestDrawer" title="工作流测试" :modal="false">
|
||
<fieldset>
|
||
<legend class="ml-15px"><h3>运行参数配置</h3></legend>
|
||
<div class="p-20px">
|
||
<div
|
||
class="flex justify-around mb-10px"
|
||
v-for="(param, index) in params4Test"
|
||
:key="index"
|
||
>
|
||
<el-select class="w-200px!" v-model="param.key" placeholder="参数名">
|
||
<el-option
|
||
v-for="(value, key) in paramsOfStartNode"
|
||
:key="key"
|
||
:label="value?.description || key"
|
||
:value="key"
|
||
:disabled="!!value?.disabled"
|
||
/>
|
||
</el-select>
|
||
<el-input class="w-200px!" v-model="param.value" placeholder="参数值" />
|
||
<el-button type="danger" plain :icon="Delete" circle @click="removeParam(index)" />
|
||
</div>
|
||
<!-- TODO @lesan:是不是不用添加和删除参数,直接把必填和选填列出来,然后加上参数校验? -->
|
||
<el-button type="primary" plain @click="addParam">添加参数</el-button>
|
||
</div>
|
||
</fieldset>
|
||
<fieldset class="mt-20px bg-#f8f9fa">
|
||
<legend class="ml-15px"><h3>运行结果</h3></legend>
|
||
<div class="p-20px">
|
||
<div v-if="loading"> <el-text type="primary">执行中...</el-text></div>
|
||
<div v-else-if="error">
|
||
<el-text type="danger">{{ error }}</el-text>
|
||
</div>
|
||
<pre v-else-if="testResult" class="result-content"
|
||
>{{ JSON.stringify(testResult, null, 2) }}
|
||
</pre>
|
||
<div v-else> <el-text type="info">点击运行查看结果</el-text> </div>
|
||
</div>
|
||
</fieldset>
|
||
<el-button class="mt-20px w-100%" size="large" type="success" @click="goRun">
|
||
运行流程
|
||
</el-button>
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import Tinyflow from '@/components/Tinyflow/Tinyflow.vue'
|
||
import * as WorkflowApi from '@/api/ai/workflow'
|
||
// TODO @lesan:要不使用 ICon 哪个组件哈
|
||
import { Delete } from '@element-plus/icons-vue'
|
||
|
||
defineProps<{
|
||
provider: any
|
||
}>()
|
||
|
||
const tinyflowRef = ref()
|
||
const workflowData = inject('workflowData') as Ref
|
||
const showTestDrawer = ref(false)
|
||
const params4Test = ref([])
|
||
const paramsOfStartNode = ref({})
|
||
const testResult = ref(null)
|
||
const loading = ref(false)
|
||
const error = ref(null)
|
||
|
||
/** 展示工作流测试抽屉 */
|
||
const testWorkflowModel = () => {
|
||
showTestDrawer.value = !showTestDrawer.value
|
||
}
|
||
|
||
/** 运行流程 */
|
||
const goRun = async () => {
|
||
try {
|
||
const val = tinyflowRef.value.getData()
|
||
loading.value = true
|
||
error.value = null
|
||
testResult.value = null
|
||
/// 查找start节点
|
||
const startNode = getStartNode()
|
||
|
||
// 获取参数定义
|
||
const parameters = startNode.data?.parameters || []
|
||
const paramDefinitions = {}
|
||
parameters.forEach((param) => {
|
||
paramDefinitions[param.name] = param.dataType
|
||
})
|
||
|
||
// 参数类型转换
|
||
const convertedParams = {}
|
||
for (const { key, value } of params4Test.value) {
|
||
const paramKey = key.trim()
|
||
if (!paramKey) continue
|
||
|
||
let dataType = paramDefinitions[paramKey]
|
||
if (!dataType) {
|
||
dataType = 'String'
|
||
}
|
||
|
||
try {
|
||
convertedParams[paramKey] = convertParamValue(value, dataType)
|
||
} catch (e) {
|
||
throw new Error(`参数 ${paramKey} 转换失败: ${e.message}`)
|
||
}
|
||
}
|
||
|
||
const data = {
|
||
graph: JSON.stringify(val),
|
||
params: convertedParams
|
||
}
|
||
|
||
const response = await WorkflowApi.testWorkflow(data)
|
||
testResult.value = response
|
||
} catch (err) {
|
||
error.value = err.response?.data?.message || '运行失败,请检查参数和网络连接'
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
/** 监听测试抽屉的开启,获取开始节点参数列表 */
|
||
watch(showTestDrawer, (value) => {
|
||
if (!value) return
|
||
|
||
/// 查找start节点
|
||
const startNode = getStartNode()
|
||
|
||
// 获取参数定义
|
||
const parameters = startNode.data?.parameters || []
|
||
const paramDefinitions = {}
|
||
|
||
// 加入参数选项方便用户添加非必须参数
|
||
parameters.forEach((param) => {
|
||
paramDefinitions[param.name] = param
|
||
})
|
||
|
||
function mergeIfRequiredButNotSet(target) {
|
||
let needPushList = []
|
||
for (let key in paramDefinitions) {
|
||
let param = paramDefinitions[key]
|
||
|
||
if (param.required) {
|
||
let item = target.find((item) => item.key === key)
|
||
|
||
if (!item) {
|
||
needPushList.push({ key: param.name, value: param.defaultValue || '' })
|
||
}
|
||
}
|
||
}
|
||
target.push(...needPushList)
|
||
}
|
||
// 自动装载需必填的参数
|
||
mergeIfRequiredButNotSet(params4Test.value)
|
||
|
||
paramsOfStartNode.value = paramDefinitions
|
||
})
|
||
|
||
/** 获取开始节点 */
|
||
const getStartNode = () => {
|
||
const val = tinyflowRef.value.getData()
|
||
const startNode = val.nodes.find((node) => node.type === 'startNode')
|
||
if (!startNode) {
|
||
throw new Error('流程缺少开始节点')
|
||
}
|
||
return startNode
|
||
}
|
||
|
||
/** 添加参数项 */
|
||
const addParam = () => {
|
||
params4Test.value.push({ key: '', value: '' })
|
||
}
|
||
|
||
/** 删除参数项 */
|
||
const removeParam = (index) => {
|
||
params4Test.value.splice(index, 1)
|
||
}
|
||
|
||
/** 类型转换函数 */
|
||
const convertParamValue = (value, dataType) => {
|
||
if (value === '') return null // 空值处理
|
||
|
||
switch (dataType) {
|
||
case 'String':
|
||
return String(value)
|
||
case 'Number':
|
||
const num = Number(value)
|
||
if (isNaN(num)) throw new Error('非数字格式')
|
||
return num
|
||
case 'Boolean':
|
||
if (value.toLowerCase() === 'true') return true
|
||
if (value.toLowerCase() === 'false') return false
|
||
throw new Error('必须为 true/false')
|
||
case 'Object':
|
||
case 'Array':
|
||
try {
|
||
return JSON.parse(value)
|
||
} catch (e) {
|
||
throw new Error(`JSON格式错误: ${e.message}`)
|
||
}
|
||
default:
|
||
throw new Error(`不支持的类型: ${dataType}`)
|
||
}
|
||
}
|
||
|
||
/** 表单校验 */
|
||
const validate = async () => {
|
||
try {
|
||
// 获取最新的流程数据
|
||
if (!workflowData.value) {
|
||
throw new Error('请设计流程')
|
||
}
|
||
workflowData.value = tinyflowRef.value.getData()
|
||
return true
|
||
} catch (error) {
|
||
throw error
|
||
}
|
||
}
|
||
defineExpose({
|
||
validate
|
||
})
|
||
</script>
|
||
|
||
<style lang="css" scoped>
|
||
.result-content {
|
||
background: white;
|
||
padding: 12px;
|
||
border-radius: 4px;
|
||
max-height: 300px;
|
||
overflow: auto;
|
||
font-family: Monaco, Consolas, monospace;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
white-space: pre-wrap;
|
||
}
|
||
</style>
|