feat: 引用

This commit is contained in:
zhanghao 2025-09-19 14:16:28 +08:00
parent 944c0dd0f2
commit 4f4af08bbe
10 changed files with 1254 additions and 989 deletions

View File

@ -201,7 +201,7 @@ export const dynamicRoutes = [
{ {
path: "index/:id", path: "index/:id",
component: () => import("@/views/vi/testProject/plan/index"), component: () => import("@/views/vi/testProject/plan/index"),
name: "Data", name: "Plan",
meta: { title: "方案管理", activeMenu: "/vi/testProject" }, meta: { title: "方案管理", activeMenu: "/vi/testProject" },
}, },
], ],

View File

@ -22,9 +22,10 @@ export const getInput = (nodeId, isStrict = true) => {
const obj = { const obj = {
value: item.id, value: item.id,
label: item.properties.name, label: item.properties.name,
type: 'input',
children: [] children: []
} }
const optionData = transformTree(item.properties.inputParams, obj.children) const optionData = transformTree(item.properties.inputParams, obj.children, 'input')
obj.children.push(optionData) obj.children.push(optionData)
arr.push(obj) arr.push(obj)
} }
@ -32,9 +33,10 @@ export const getInput = (nodeId, isStrict = true) => {
const obj = { const obj = {
value: item.id, value: item.id,
label: item.properties.name + '(输入)', label: item.properties.name + '(输入)',
type: 'input',
children: [] children: []
} }
const optionData = transformTree(item.properties.nodeParams, obj.children) const optionData = transformTree(item.properties.nodeParams, obj.children, 'input')
obj.children.push(optionData) obj.children.push(optionData)
arr.push(obj) arr.push(obj)
} }
@ -43,9 +45,10 @@ export const getInput = (nodeId, isStrict = true) => {
const obj = { const obj = {
value: item.id, value: item.id,
label: item.properties.name + '(输出)', label: item.properties.name + '(输出)',
type: 'output',
children: [] children: []
} }
const optionData = transformTree(item.properties.outputParams, obj.children) const optionData = transformTree(item.properties.outputParams, obj.children, 'output')
obj.children.push(optionData) obj.children.push(optionData)
arr.push(obj) arr.push(obj)
} }
@ -55,11 +58,12 @@ export const getInput = (nodeId, isStrict = true) => {
return flag return flag
} }
const transformTree = (arr, parent = []) => { const transformTree = (arr, parent = [], type) => {
arr.forEach(item => { arr.forEach(item => {
const currentNode = { const currentNode = {
value: item.name, value: item.name,
label: item.name, label: item.name,
type,
children: [] children: []
}; };
parent.push(currentNode); parent.push(currentNode);
@ -67,7 +71,7 @@ const transformTree = (arr, parent = []) => {
if (item.children?.length > 0) { if (item.children?.length > 0) {
transformTree(item.children, currentNode.children); transformTree(item.children, currentNode.children);
} else { } else {
delete currentNode.children delete currentNode.children
} }
}); });
return parent; return parent;

View File

@ -106,7 +106,7 @@ export const collapseList = [
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true } { name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
], ],
outputType: 'img', outputType: 'img',
outputParams: [{ name: 'imageUrl', type: 'Array<String>', desc: '图片地址数组'}] outputParams: [{ name: 'imageUrl', type: 'array<string>', desc: '图片地址数组', disabled: true}]
}, },
{ {
icon: cameraSvg, icon: cameraSvg,
@ -145,7 +145,7 @@ export const collapseList = [
{ name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true } { name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: deviceOptions(), disabled: true }
], ],
outputType: 'video', outputType: 'video',
outputParams: [{ name: 'videoUrl', type: 'Array<String>', desc: '视频播放地址数组'}] outputParams: [{ name: 'videoUrl', type: 'array<string>', desc: '视频播放地址数组'}]
} }
], ],
}, },
@ -254,7 +254,7 @@ export const collapseList = [
}, },
], ],
}, },
{ {
collapseTitle: "大模型", collapseTitle: "大模型",
nodeList: [ nodeList: [
{ {
@ -273,4 +273,21 @@ export const collapseList = [
}, },
], ],
}, },
{
collapseTitle: "语音交互",
nodeList: [
{
icon: microphoneSvg,
name: "唤醒语料",
type: "serviceNode",
desc: "唤醒语料处理",
action: 'VI_WAKE_CORPUS',
nodeParams: [
{ name: "", type: "input", input: ""}
],
outputType: 'json',
outputParams: [{ name: 'coordinates', type: 'Object', desc: '坐标对象'}]
},
],
},
] ]

View File

@ -1,212 +1,212 @@
<!-- components/FormItemRecursive.vue --> <!-- components/FormItemRecursive.vue -->
<template> <template>
<div class="form-item-recursive"> <div class="form-item-recursive">
<!-- 循环渲染当前层级的所有表单项 --> <!-- 循环渲染当前层级的所有表单项 -->
<div v-for="(item, index) in currentList" :key="index"> <div v-for="(item, index) in currentList" :key="index">
<el-row> <el-row>
<!-- 名称输入 --> <!-- 名称输入 -->
<el-form-item <el-form-item
:label="isFirstLevel && index === 0 ? '参数名' : ''" :label="isFirstLevel && index === 0 ? '参数名' : ''"
:prop="`${propPath}.${index}.name`" :prop="`${propPath}.${index}.name`"
:rules="[{ required: true, message: '请输入', trigger: 'blur' }]" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]"
> >
<el-input <el-input
:disabled="isFirstLevel && index === 0" :disabled="isFirstLevel && index === 0"
v-model="item.name" v-model="item.name"
placeholder="请输入" placeholder="请输入"
clearable clearable
/> />
</el-form-item> </el-form-item>
<!-- 类型选择 --> <!-- 类型选择 -->
<el-form-item <el-form-item
:label="isFirstLevel && index === 0 ? '参数类型' : ''" :label="isFirstLevel && index === 0 ? '参数类型' : ''"
:prop="`${propPath}.${index}.type`" :prop="`${propPath}.${index}.type`"
:rules="[{ required: true, message: '请输入', trigger: 'blur' }]" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]"
> >
<el-select <el-select
:disabled="isFirstLevel && index === 0" :disabled="isFirstLevel && index === 0"
v-model="item.type" v-model="item.type"
@change="handleTypeChange(item)" @change="handleTypeChange(item)"
> >
<el-option label="String" value="string" /> <el-option label="String" value="string" />
<el-option label="Number" value="number" /> <el-option label="Number" value="number" />
<el-option label="Boolean" value="boolean" /> <el-option label="Boolean" value="boolean" />
<el-option v-if="depth < endDepth" label="Object" value="object" /> <el-option v-if="depth < endDepth" label="Object" value="object" />
<!-- <el-option label="Array<String>" value="array<string>" /> <el-option label="Array<String>" value="array<string>" />
<el-option label="Array<Number>" value="array<number>" /> <el-option label="Array<Number>" value="array<number>" />
<el-option label="Array<Boolean>" value="array<boolean>" /> <el-option label="Array<Boolean>" value="array<boolean>" />
<el-option v-if="depth < endDepth" label="Array<Object>" value="array<object>" /> --> <el-option v-if="depth < endDepth" label="Array<Object>" value="array<object>" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 描述输入 --> <!-- 描述输入 -->
<el-form-item <el-form-item
:label="isFirstLevel && index === 0 ? '描述' : ''" :label="isFirstLevel && index === 0 ? '描述' : ''"
:prop="`${propPath}.${index}.desc`" :prop="`${propPath}.${index}.desc`"
:rules="[{ required: true, message: '请输入', trigger: 'blur' }]" :rules="[{ required: true, message: '请输入', trigger: 'blur' }]"
> >
<el-input <el-input
:disabled="isFirstLevel && index === 0" :disabled="isFirstLevel && index === 0"
v-model="item.desc" v-model="item.desc"
placeholder="请输入" placeholder="请输入"
clearable clearable
/> />
</el-form-item> </el-form-item>
<!-- 是否必填 --> <!-- 是否必填 -->
<el-form-item :label="isFirstLevel && index === 0 ? '必填' : ''"> <el-form-item :label="isFirstLevel && index === 0 ? '必填' : ''">
<el-switch <el-switch
:disabled="isFirstLevel && index === 0" :disabled="isFirstLevel && index === 0"
v-model="item.required" v-model="item.required"
/> />
</el-form-item> </el-form-item>
<!-- 删除按钮第一层第一个不能删 --> <!-- 删除按钮第一层第一个不能删 -->
<el-button <el-button
v-if="!(isFirstLevel && index === 0)" v-if="!(isFirstLevel && index === 0)"
:icon="Minus" :icon="Minus"
circle circle
size="small" size="small"
@click="handleDelete(index)" @click="handleDelete(index)"
class="deleteBtn" class="deleteBtn"
/> />
<!-- 添加子项按钮当类型是object/array<object> --> <!-- 添加子项按钮当类型是object/array<object> -->
<el-button <el-button
v-if="['object', 'array<object>'].includes(item.type)" v-if="['object', 'array<object>'].includes(item.type)"
:icon="Plus" :icon="Plus"
circle circle
size="small" size="small"
@click="handleAddChild(item)" @click="handleAddChild(item)"
class="addBtn" class="addBtn"
/> />
</el-row> </el-row>
<!-- 递归渲染子项如果有子项且类型匹配 --> <!-- 递归渲染子项如果有子项且类型匹配 -->
<!-- 层级越深缩进越多 --> <!-- 层级越深缩进越多 -->
<div <div
v-if="['object', 'array<object>'].includes(item.type) && item.children.length" v-if="['object', 'array<object>'].includes(item.type) && item.children.length"
class="nested-form-items" class="nested-form-items"
:style="{ marginLeft: `${depth * 20}px` }" :style="{ marginLeft: `${depth * 20}px` }"
> >
<!-- 递归调用自身处理下一层级 --> <!-- 递归调用自身处理下一层级 -->
<!-- 非第一层 --> <!-- 非第一层 -->
<!-- 复用添加逻辑 --> <!-- 复用添加逻辑 -->
<FormItemRecursive <FormItemRecursive
:current-list="item.children" :current-list="item.children"
:prop-path="`${propPath}.${index}.children`" :prop-path="`${propPath}.${index}.children`"
:depth="depth + 1" :depth="depth + 1"
:is-first-level="false" :is-first-level="false"
:endDepth="endDepth" :endDepth="endDepth"
:parent-path="[...parentPath, index] " :parent-path="[...parentPath, index] "
@add-item="handleAddChild(item)" @add-item="handleAddChild(item)"
@delete-item="handleChildDelete" @delete-item="handleChildDelete"
/> />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { Plus, Minus } from "@element-plus/icons-vue"; import { Plus, Minus } from "@element-plus/icons-vue";
// props // props
const { currentList, propPath, depth, isFirstLevel, endDepth, parentPath } = defineProps({ const { currentList, propPath, depth, isFirstLevel, endDepth, parentPath } = defineProps({
currentList: { currentList: {
type: Array, type: Array,
default: () => [] default: () => []
}, },
propPath: { propPath: {
type: String, type: String,
default: '' default: ''
}, },
depth: { depth: {
type: Number, type: Number,
default: 0 default: 0
}, },
isFirstLevel: { isFirstLevel: {
type: Boolean, type: Boolean,
default: true default: true
}, },
endDepth: { endDepth: {
type: Number, type: Number,
default: 2 default: 2
}, },
parentPath: { parentPath: {
type: String, type: String,
default: '' default: ''
}, },
}); });
// emit // emit
const emit = defineEmits(['add-item', 'delete-item']) const emit = defineEmits(['add-item', 'delete-item'])
// //
const handleTypeChange = (item) => { const handleTypeChange = (item) => {
if (!["object", "array<object>"].includes(item.type)) { if (!["object", "array<object>"].includes(item.type)) {
item.children = []; // item.children = []; //
} else if (item.children.length === 0) { } else if (item.children.length === 0) {
handleAddChild(item); // handleAddChild(item); //
} }
}; };
// //
const handleAddChild = (parentItem) => { const handleAddChild = (parentItem) => {
parentItem.children.push({ parentItem.children.push({
name: "", name: "",
type: "", type: "",
desc: "", desc: "",
required: false, required: false,
children: [], children: [],
}); });
// emit("add-item", parentItem); // // emit("add-item", parentItem); //
}; };
// //
const handleDelete = (index) => { const handleDelete = (index) => {
// = + parentPathundefined // = + parentPathundefined
const fullPath = parentPath ? [...parentPath, index] : [index]; const fullPath = parentPath ? [...parentPath, index] : [index];
emit("delete-item", fullPath); // emit("delete-item", fullPath); //
} }
// //
const handleChildDelete = (childFullPath) => { const handleChildDelete = (childFullPath) => {
emit("delete-item", childFullPath); emit("delete-item", childFullPath);
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.nested-form-items { .nested-form-items {
border-left: 1px dashed #ccc; /* 层级连接线 */ border-left: 1px dashed #ccc; /* 层级连接线 */
padding-left: 16px; padding-left: 16px;
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;
} }
.deleteBtn { .deleteBtn {
margin-bottom: 22px; margin-bottom: 22px;
cursor: pointer; cursor: pointer;
} }
.addBtn { .addBtn {
margin-bottom: 22px; margin-bottom: 22px;
cursor: pointer; cursor: pointer;
} }
:deep(.el-row) { :deep(.el-row) {
align-items: end; align-items: end;
.el-input { .el-input {
--el-input-width: 140px; --el-input-width: 140px;
} }
.el-select { .el-select {
--el-select-width: 140px; --el-select-width: 140px;
} }
.el-cascader { .el-cascader {
--el-form-inline-content-width: 140px; --el-form-inline-content-width: 140px;
} }
} }
</style> </style>

View File

@ -1,425 +1,507 @@
<template> <template>
<div class="node__container"> <div class="node__container">
<NodeState :state="nodeOperatingStatus"> <NodeState :state="nodeOperatingStatus">
<template #input> <template #input>
<JsonViewer :value="inputJsonData" copyable boxed sort theme="light" /> <JsonViewer :value="inputJsonData" copyable boxed sort theme="light" />
</template> </template>
<template #output> <template #output>
<JsonViewer v-if="props.properties.outputType === 'json'" :value="outputJsonData" copyable boxed sort theme="light" /> <JsonViewer v-if="props.properties.outputType === 'json'" :value="outputJsonData" copyable boxed sort theme="light" />
<el-image <el-image
v-if="props.properties.outputType === 'img'" v-if="props.properties.outputType === 'img'"
v-for="item in outputJsonData.imageUrl" v-for="item in outputJsonData.imageUrl"
style="width: 60px; height: 60px" style="width: 60px; height: 60px"
:src="item" :src="item"
:preview-src-list="outputJsonData.imageUrl" :preview-src-list="outputJsonData.imageUrl"
:preview-teleported="true" :preview-teleported="true"
show-progress show-progress
fit="fill" fit="fill"
/> />
<IPlayer v-if="props.properties.outputType === 'video'" v-for="item in outputJsonData.videoUrl" :videoUrl="item" /> <IPlayer v-if="props.properties.outputType === 'video'" v-for="item in outputJsonData.videoUrl" :videoUrl="item" />
</template> </template>
</NodeState> </NodeState>
<NodeTitle <NodeTitle
:icon="props.properties.icon" :icon="props.properties.icon"
:nodeId="props.model.id" :nodeId="props.model.id"
:nodeProperties="props.properties" :nodeProperties="props.properties"
:nodeName="props.properties.name" :nodeName="props.properties.name"
:nodeDesc="props.properties.desc" :nodeDesc="props.properties.desc"
/> />
<div class="input__container"> <div class="input__container">
<div class="title"> <div class="title">
<div class="left"> <div class="left">
<div class="tag"></div> <div class="tag"></div>
<div class="text">输入</div> <div class="text">输入</div>
</div> </div>
<div class="right" v-if="properties.properties?.canAddFormItem"> <div class="right" v-if="properties.properties?.canAddFormItem">
<el-button <el-button
:disabled="flowStore.disableForm" :disabled="flowStore.disableForm"
@click="addFormItem" @click="addFormItem"
class="addFormItem" class="addFormItem"
> >
<el-icon :size="20"><Plus /></el-icon> <el-icon :size="20"><Plus /></el-icon>
</el-button> </el-button>
</div> </div>
</div> </div>
<div class="form__container"> <div class="form__container">
<el-form <el-form
:inline="true" :inline="true"
:model="formData" :model="formData"
:rules="rules" :rules="rules"
ref="dynamicForm" ref="dynamicForm"
label-position="top" label-position="top"
label-width="auto" label-width="auto"
:disabled="flowStore.disableForm" :disabled="flowStore.disableForm"
> >
<div v-for="(property, index) in formData.nodeParams" :key="index"> <div v-for="(property, index) in formData.nodeParams" :key="index">
<el-row> <el-row>
<el-form-item <el-form-item
:label="index === 0 ? '参数名' : ''" :label="index === 0 ? '参数名' : ''"
:prop="`nodeParams.${index}.name`" :prop="`nodeParams.${index}.name`"
:rules="[{ required: true, message: '请输入参数名', trigger: 'blur' }]" :rules="[{ required: true, message: '请输入参数名', trigger: 'blur' }]"
> >
<el-input <el-input
:disabled="property?.disabled || false" :disabled="property?.disabled || false"
v-model="property.name" v-model="property.name"
placeholder="请输入" placeholder="请输入"
clearable clearable
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
:label="index === 0 ? '参数值' : ''" :label="index === 0 ? '参数值' : ''"
:prop="`nodeParams.${index}.type`" :prop="`nodeParams.${index}.type`"
> >
<el-select v-model="property.type" @change="handleTypeChange(index)"> <el-select v-model="property.type" @change="handleTypeChange(index)">
<el-option label="引用" value="quote" /> <el-option label="引用" value="quote" />
<el-option label="输入" value="input" /> <el-option label="输入" value="input" />
</el-select> </el-select>
<el-form-item <el-form-item
v-if="property.type === 'input'" v-if="property.type === 'input'"
:rules="[{ required: true, message: '请输入参数值', trigger: 'blur' }]" :rules="[{ required: true, message: '请输入参数值', trigger: 'blur' }]"
:prop="`nodeParams.${index}.input`" :prop="`nodeParams.${index}.input`"
> >
<el-input-number v-if="property.componentType === 'number'" v-model="property.input" :min="0" :controls="false" :step-strictly="true" placeholder="请输入" clearable /> <el-input-number v-if="property.componentType === 'number'" v-model="property.input" :min="0" :controls="false" :step-strictly="true" placeholder="请输入" clearable />
<el-select v-model="property.input" v-else-if="property.componentType === 'select'" > <el-select v-model="property.input" v-else-if="property.componentType === 'select'" >
<el-option v-for="item in property.selectOptions" :key="item" :label="item" :value="item" /> <el-option v-for="item in property.selectOptions" :key="item" :label="item" :value="item" />
</el-select> </el-select>
<el-input v-else v-model="property.input" placeholder="请输入" clearable /> <el-input v-else v-model="property.input" placeholder="请输入" clearable />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="property.type === 'quote'" v-if="property.type === 'quote'"
:rules="[{ required: true, message: '请选择参数值', trigger: 'blur' }]" :rules="[{ required: true, message: '请选择参数值', trigger: 'blur' }]"
:prop="`nodeParams.${index}.quote`" :prop="`nodeParams.${index}.quote`"
> >
<el-cascader <el-cascader
v-model="property.quote" :ref="el => { if (el) cascaderRefs[index] = el }"
:options="quoteOptions" v-model="property.quote"
placeholder="请选择" :options="quoteOptions"
@visible-change="visibleChange" placeholder="请选择"
/> @visible-change="visibleChange"
</el-form-item> @change="(value) => cascaderChange(value, index)"
</el-form-item> />
</el-row> </el-form-item>
</div> </el-form-item>
</el-form> </el-row>
</div> </div>
</div> </el-form>
<div class="output__container" v-if="props.properties?.outputParams?.length > 0"> </div>
<div class="title"> </div>
<div class="tag"></div> <div class="output__container" v-if="props.properties?.outputParams?.length > 0">
<div class="text">输出</div> <div class="title">
</div> <div class="left">
<div class="tag"></div>
<div class="form__container"> <div class="text">输出</div>
<el-form </div>
:inline="true" <div class="right" >
label-position="top" <el-button
label-width="auto" :disabled="flowStore.disableForm"
:disabled="true" @click="addOutputFormItem"
> class="addFormItem"
<el-row v-for="(item, index) in props.properties.outputParams"> >
<el-form-item label="参数名" > <el-icon :size="20"><Plus /></el-icon>
<el-input :value="item.name" /> </el-button>
</el-form-item> </div>
<el-form-item label="参数类型" > </div>
<el-input :value="item.type" />
</el-form-item> <div class="form__container">
<el-form-item label="描述" > <el-form
<el-input :value="item.desc" /> :inline="true"
</el-form-item> :model="formData"
</el-row> label-position="top"
</el-form> label-width="auto"
</div> :rules="outputRules"
</div> ref="outputFormRef"
</div> >
</template> <!-- <el-row v-for="(item, index) in formData.outputParams">
<el-form-item :label="index === 0 ? '参数名' : ''" >
<script setup> <el-input :value="item.name" :disabled="item?.disabled" />
import { ref, reactive, onMounted, onUnmounted } from "vue"; </el-form-item>
import { getInput } from "@/utils/flow"; <el-form-item :label="index === 0 ? '参数类型' : ''" >
import { useFlowStore } from "@/store/modules/flow"; <el-input :value="item.type" />
import { Plus } from "@element-plus/icons-vue"; </el-form-item>
import NodeTitle from "../../components/NodeTitle.vue"; <el-form-item :label="index === 0 ? '描述' : ''">
import NodeState from "../../components/NodeState.vue"; <el-input :value="item.desc" />
import "vue3-json-viewer/dist/index.css"; </el-form-item>
import { emitter } from "@/utils/eventBus"; </el-row> -->
import IPlayer from "@/views/device/register/components/LogicFlow/IPlayer/index.vue"; <FormItemRecursive
:current-list="formData.outputParams"
const props = defineProps({ prop-path="outputParams"
model: Object, :depth="0"
properties: Object, :is-first-level="true"
}); :endDepth="2"
:parent-path="[]"
const emits = defineEmits(["contentChange"]); @delete-item="deleteTopLevelItem"
/>
const flowStore = useFlowStore(); </el-form>
</div>
const formData = reactive({ </div>
nodeParams: [], </div>
}); </template>
const rules = reactive({}); <script setup>
import { ref, reactive, onMounted, onUnmounted } from "vue";
const quoteOptions = ref([]); import { getInput } from "@/utils/flow";
const handleTypeChange = (index) => { import { useFlowStore } from "@/store/modules/flow";
if (formData.nodeParams[index].type === "input") { import { Plus } from "@element-plus/icons-vue";
formData.nodeParams[index].quote = ""; import NodeTitle from "../../components/NodeTitle.vue";
} else { import NodeState from "../../components/NodeState.vue";
formData.nodeParams[index].input = ""; import "vue3-json-viewer/dist/index.css";
const option = getInput(props.model.id); import { emitter } from "@/utils/eventBus";
if (option) { import IPlayer from "@/views/device/register/components/LogicFlow/IPlayer/index.vue";
quoteOptions.value = option; import FormItemRecursive from "./FormItemRecursive.vue";
}
} const props = defineProps({
}; model: Object,
properties: Object,
const dynamicForm = ref(); });
const setNodeProperties = async () => { const emits = defineEmits(["contentChange"]);
try {
const result = await dynamicForm.value.validate(); const flowStore = useFlowStore();
if (result) {
const data = toRaw(formData); const formData = reactive({
const properties = lf.getProperties(props.model.id); nodeParams: [],
lf.setProperties(props.model.id, { outputParams: []
...properties, });
...data,
}); const rules = reactive({});
emits("contentChange");
} const quoteOptions = ref([]);
} catch { const handleTypeChange = (index) => {
dynamicForm.value.clearValidate(); if (formData.nodeParams[index].type === "input") {
} formData.nodeParams[index].quote = "";
}; } else {
formData.nodeParams[index].input = "";
const nodeOperatingStatus = ref("NORMAL"); const option = getInput(props.model.id);
const inputJsonData = ref({}); console.log('option', option)
const outputJsonData = ref({}); if (option) {
quoteOptions.value = option;
const visibleChange = (value) => { }
if (value) { }
const option = getInput(props.model.id); };
if (option) {
quoteOptions.value = option; const cascaderRefs = ref([])
} const outputRules = reactive({});
} const cascaderChange = (value, index) => {
}; const selectedOptions = cascaderRefs.value[index].getCheckedNodes(true);
formData.nodeParams[index].quoteType = selectedOptions[0].data.type
const validateForm = async () => { }
try {
const result = await dynamicForm.value.validate(); const addOutputFormItem = () => {
if (result) { formData.outputParams.push({
return { valid: true, message: "验证通过" }; name: '',
} else { type: '',
return { valid: false, message: "验证失败" }; desc: '',
} children: []
} catch { })
return { valid: false, message: "验证失败" }; }
}
}; const dynamicForm = ref();
const outputFormRef = ref()
const addFormItem = () => { const setNodeProperties = async () => {
formData.nodeParams.push({ try {
name: "", const result = await dynamicForm.value.validate();
type: "", console.log(123456)
desc: "", const test = await outputFormRef.value.validate()
required: false, console.log('test', test)
children: [], if (result) {
}); const data = toRaw(formData);
emits("contentChange"); console.log('data', data)
}; const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
watch( ...properties,
() => props.properties, ...data,
() => { });
if (props.properties.nodeParams && props.properties.nodeParams.length > 0) { emits("contentChange");
formData.nodeParams = props.properties.nodeParams; }
const option = getInput(props.model.id); } catch {
if (option) { // dynamicForm.value.clearValidate();
quoteOptions.value = option; }
} };
}
}, const nodeOperatingStatus = ref("NORMAL");
{ const inputJsonData = ref({});
immediate: true, const outputJsonData = ref({});
deep: true,
} const visibleChange = (value) => {
); if (value) {
const option = getInput(props.model.id);
onMounted(() => { if (option) {
emitter.on("changeNodeState", (data) => { quoteOptions.value = option;
if (data.nodeId === props.model.id) { }
nodeOperatingStatus.value = data.status; }
let inputData = {}; };
let output = {};
try { const validateForm = async () => {
inputData = JSON.parse(data.paramsIn) || {}; try {
output = JSON.parse(data.paramsOut) || {}; const result = await dynamicForm.value.validate();
} catch (error) { if (result) {
inputData = data.paramsIn || {}; return { valid: true, message: "验证通过" };
output = data.paramsOut || {}; } else {
} return { valid: false, message: "验证失败" };
inputJsonData.value = inputData; }
outputJsonData.value = output; } catch {
emits("contentChange"); return { valid: false, message: "验证失败" };
} }
}); };
emitter.on("contentChange", (data) => { const addFormItem = () => {
if (data.id === props.model.id) { formData.nodeParams.push({
nodeOperatingStatus.value = "NORMAL"; name: "",
inputJsonData.value = {}; type: "",
outputJsonData.value = {}; desc: "",
emits("contentChange"); required: false,
} children: [],
}); });
}); emits("contentChange");
};
onUnmounted(() => {
emitter.off("changeNodeState"); //
emitter.off("contentChange"); const deleteTopLevelItem = (fullPath) => {
}); //
let currentLevel = formData.outputParams;
defineExpose({
validateForm, //
setNodeProperties, for (let i = 0; i < fullPath.length - 1; i++) {
}); const index = fullPath[i];
</script> //
currentLevel = currentLevel[index].children;
<style lang="scss" scoped> }
.node__container {
width: 100%; //
height: auto; const lastIndex = fullPath[fullPath.length - 1];
currentLevel.splice(lastIndex, 1);
.input__container { emits("contentChange");
width: 100%; }
background-color: #fafbfc;
padding: 0 16px; watch(
border-radius: 8px; () => props.properties,
box-sizing: border-box; () => {
if (props.properties.nodeParams && props.properties.nodeParams.length > 0) {
.title { formData.nodeParams = props.properties.nodeParams;
height: 32px; const option = getInput(props.model.id);
display: flex; if (option) {
align-items: center; quoteOptions.value = option;
justify-content: space-between; }
}
.left { if (props.properties.outputParams && props.properties.outputParams.length > 0) {
display: flex; formData.outputParams = props.properties.outputParams;
align-items: center; }
},
.tag { {
width: 3px; immediate: true,
height: 16px; deep: true,
background: #1664ff; }
border-radius: 0 4px 4px 0; );
margin-right: 12px;
} onMounted(() => {
emitter.on("changeNodeState", (data) => {
.text { if (data.nodeId === props.model.id) {
font-size: 16px; nodeOperatingStatus.value = data.status;
font-weight: 700; let inputData = {};
color: #0c0d0e; let output = {};
} try {
} inputData = JSON.parse(data.paramsIn) || {};
output = JSON.parse(data.paramsOut) || {};
.right { } catch (error) {
.addFormItem { inputData = data.paramsIn || {};
border: none; output = data.paramsOut || {};
background: transparent; }
cursor: pointer; inputJsonData.value = inputData;
} outputJsonData.value = output;
} emits("contentChange");
} }
});
.form__container {
margin: 12px 0; emitter.on("contentChange", (data) => {
if (data.id === props.model.id) {
.sub-properties { nodeOperatingStatus.value = "NORMAL";
margin-left: 10px; inputJsonData.value = {};
.zw { outputJsonData.value = {};
margin-left: 15px; emits("contentChange");
&::before { }
content: ""; });
position: absolute; });
left: 0;
top: -4px; onUnmounted(() => {
width: 10px; emitter.off("changeNodeState");
border-left: 1px solid gray; emitter.off("contentChange");
border-bottom: 1px solid gray; });
border-bottom-left-radius: 4px;
background-color: transparent; defineExpose({
height: 24px; validateForm,
} setNodeProperties,
} });
} </script>
.gSon-properties { <style lang="scss" scoped>
margin-left: 10px; .node__container {
.zw { width: 100%;
margin-left: 15px; height: auto;
&::before {
content: ""; .input__container {
position: absolute; width: 100%;
left: 0; background-color: #fafbfc;
top: -4px; padding: 0 16px;
width: 10px; border-radius: 8px;
border-left: 1px solid gray; box-sizing: border-box;
border-bottom: 1px solid gray;
border-bottom-left-radius: 4px; .title {
background-color: transparent; height: 32px;
height: 24px; display: flex;
} align-items: center;
} justify-content: space-between;
}
} .left {
} display: flex;
align-items: center;
.output__container {
width: 100%; .tag {
background-color: #fafbfc; width: 3px;
padding: 0 16px; height: 16px;
border-radius: 8px; background: #1664ff;
box-sizing: border-box; border-radius: 0 4px 4px 0;
margin-right: 12px;
.title { }
height: 32px;
display: flex; .text {
align-items: center; font-size: 16px;
font-weight: 700;
.tag { color: #0c0d0e;
width: 3px; }
height: 16px; }
background: #1664ff;
border-radius: 0 4px 4px 0; .right {
margin-right: 12px; .addFormItem {
} border: none;
background: transparent;
.text { cursor: pointer;
font-size: 16px; }
font-weight: 700; }
color: #0c0d0e; }
}
} .form__container {
margin: 12px 0;
.form__container {
margin: 12px 0; .sub-properties {
} margin-left: 10px;
} .zw {
margin-left: 15px;
:deep(.el-row) { &::before {
align-items: end; content: "";
position: absolute;
.el-input { left: 0;
--el-input-width: 170px; top: -4px;
} width: 10px;
border-left: 1px solid gray;
.el-select { border-bottom: 1px solid gray;
--el-select-width: 170px; border-bottom-left-radius: 4px;
} background-color: transparent;
height: 24px;
.el-cascader { }
--el-form-inline-content-width: 170px; }
} }
}
} .gSon-properties {
</style> margin-left: 10px;
.zw {
margin-left: 15px;
&::before {
content: "";
position: absolute;
left: 0;
top: -4px;
width: 10px;
border-left: 1px solid gray;
border-bottom: 1px solid gray;
border-bottom-left-radius: 4px;
background-color: transparent;
height: 24px;
}
}
}
}
}
.output__container {
width: 100%;
background-color: #fafbfc;
padding: 0 16px;
border-radius: 8px;
box-sizing: border-box;
.title {
height: 32px;
display: flex;
align-items: center;
justify-content: space-between;
.left {
display: flex;
align-items: center;
.tag {
width: 3px;
height: 16px;
background: #1664ff;
border-radius: 0 4px 4px 0;
margin-right: 12px;
}
.text {
font-size: 16px;
font-weight: 700;
color: #0c0d0e;
}
}
.right {
.addFormItem {
border: none;
background: transparent;
cursor: pointer;
}
}
}
.form__container {
margin: 12px 0;
}
}
:deep(.el-row) {
align-items: end;
.el-input {
--el-input-width: 120px;
}
.el-select {
--el-select-width: 120px;
}
.el-cascader {
--el-form-inline-content-width: 120px;
}
}
}
</style>

View File

@ -5,14 +5,14 @@
<div> <div>
<el-form :inline="true" :model="formData" :rules="rules" ref="dynamicForm" label-position="top" label-width="auto" :disabled="flowStore.disableForm"> <el-form :inline="true" :model="formData" :rules="rules" ref="dynamicForm" label-position="top" label-width="auto" :disabled="flowStore.disableForm">
<div v-for="(property, index) in formData.nodeParams" :key="index"> <div v-for="(property, index) in formData.nodeParams" :key="index">
<el-row> <el-row v-if="property.name !== 'loopType'">
<el-form-item :label="index === 0 ? '参数名' : ''" :prop="`nodeParams.${index}.name`" :rules="[{ required: true, message: '请输入参数名', trigger: 'blur' }]"> <el-form-item :label="index === 0 ? '参数名' : ''" :prop="`nodeParams.${index}.name`" :rules="[{ required: true, message: '请输入参数名', trigger: 'blur' }]">
<el-input :disabled="index === 0" v-model="property.name" placeholder="请输入" clearable /> <el-input :disabled="index === 0" v-model="property.name" placeholder="请输入" clearable />
</el-form-item> </el-form-item>
<el-form-item :label="index === 0 ? '参数值' : ''" :prop="`nodeParams.${index}.type`" > <el-form-item :label="index === 0 ? '参数值' : ''" :prop="`nodeParams.${index}.type`" >
<el-select v-model="property.type" @change="handleTypeChange(index)"> <el-select v-model="property.type" @change="handleTypeChange(index)">
<el-option label="引用" value="quote" /> <el-option label="引用" value="quote" />
<el-option label="输入" value="input" /> <el-option label="输入" value="input" />
</el-select> </el-select>
<el-form-item v-if="property.type === 'input'" :rules="[{ required: true, message: '请输入参数值', trigger: 'blur' }]" :prop="`nodeParams.${index}.input`"> <el-form-item v-if="property.type === 'input'" :rules="[{ required: true, message: '请输入参数值', trigger: 'blur' }]" :prop="`nodeParams.${index}.input`">
<el-input-number v-model="property.input" :min="0" :controls="false" :step-strictly="true" placeholder="请输入" clearable /> <el-input-number v-model="property.input" :min="0" :controls="false" :step-strictly="true" placeholder="请输入" clearable />
@ -22,6 +22,18 @@
</el-form-item> </el-form-item>
</el-form-item> </el-form-item>
</el-row> </el-row>
<el-row v-else>
<el-form-item :label="index === 0 ? '参数名' : ''" :prop="`nodeParams.${index}.name`" :rules="[{ required: true, message: '请输入参数名', trigger: 'blur' }]">
<el-input :disabled="index === 0" v-model="property.name" placeholder="请输入" clearable />
</el-form-item>
<el-form-item :label="index === 0 ? '参数值' : ''" :rules="[{ required: true, message: '请输入参数值', trigger: 'blur' }]" :prop="`nodeParams.${index}.input`" >
<el-select v-model="property.input" @change="handleTypeChange(index)">
<el-option label="普通" value="NORMAL" />
<el-option label="单次和连续" value="MULTIPLE_CORPUS" />
<el-option label="连续多条语料" value="CONTINUOUS_MULTIPLE_CORPUS" />
</el-select>
</el-form-item>
</el-row>
</div> </div>
</el-form> </el-form>
</div> </div>
@ -49,7 +61,8 @@ const props = defineProps({
const flowStore = useFlowStore() const flowStore = useFlowStore()
const formData = reactive({ const formData = reactive({
nodeParams: [ nodeParams: [
{ name: "loopNum", type: "input", input: null, quote: "" } { name: "loopNum", type: "input", input: null, quote: "" },
{ name: "loopType", type: "input", input: 'NORMAL', quote: "" },
] ]
}) })
@ -152,6 +165,7 @@ onMounted(() => {
.group__container { .group__container {
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 420px;
background: #fff; background: #fff;
padding: 12px; padding: 12px;
cursor: default; cursor: default;
@ -167,7 +181,7 @@ onMounted(() => {
} }
:deep(.loop__container) { :deep(.loop__container) {
height: 80px; height: auto;
.el-input { .el-input {
width: 92px; width: 92px;
@ -187,7 +201,7 @@ onMounted(() => {
} }
.child__container { .child__container {
min-height: 80px; min-height: 100px;
flex: 1; flex: 1;
background-color: #f6f8fa; background-color: #f6f8fa;
border-radius: 8px; border-radius: 8px;

View File

@ -1,323 +1,420 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> <div ref="topContainerRef">
<el-form-item label="字典名称" prop="dictName"> <el-form
<el-input :model="queryParams"
v-model="queryParams.dictName" ref="queryRef"
placeholder="请输入字典名称" :inline="true"
clearable v-show="showSearch"
style="width: 240px" label-width="68px"
@keyup.enter="handleQuery" >
/> <el-form-item label="字典名称" prop="dictName">
</el-form-item> <el-input
<el-form-item label="字典类型" prop="dictType"> v-model="queryParams.dictName"
<el-input placeholder="请输入字典名称"
v-model="queryParams.dictType" clearable
placeholder="请输入字典类型" style="width: 240px"
clearable @keyup.enter="handleQuery"
style="width: 240px" />
@keyup.enter="handleQuery" </el-form-item>
/> <el-form-item label="字典类型" prop="dictType">
</el-form-item> <el-input
<el-form-item label="状态" prop="status"> v-model="queryParams.dictType"
<el-select placeholder="请输入字典类型"
v-model="queryParams.status" clearable
placeholder="字典状态" style="width: 240px"
clearable @keyup.enter="handleQuery"
style="width: 240px" />
> </el-form-item>
<el-option <el-form-item label="状态" prop="status">
v-for="dict in sys_normal_disable" <el-select
:key="dict.value" v-model="queryParams.status"
:label="dict.label" placeholder="字典状态"
:value="dict.value" clearable
/> style="width: 240px"
</el-select> >
</el-form-item> <el-option
<el-form-item label="创建时间" style="width: 308px"> v-for="dict in sys_normal_disable"
<el-date-picker :key="dict.value"
v-model="dateRange" :label="dict.label"
value-format="YYYY-MM-DD" :value="dict.value"
type="daterange" />
range-separator="-" </el-select>
start-placeholder="开始日期" </el-form-item>
end-placeholder="结束日期" <el-form-item label="创建时间" style="width: 308px">
></el-date-picker> <el-date-picker
</el-form-item> v-model="dateRange"
<el-form-item> value-format="YYYY-MM-DD"
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> type="daterange"
<el-button icon="Refresh" @click="resetQuery">重置</el-button> range-separator="-"
</el-form-item> start-placeholder="开始日期"
</el-form> end-placeholder="结束日期"
></el-date-picker>
<el-row :gutter="10" class="mb8"> </el-form-item>
<el-col :span="1.5"> <el-form-item>
<el-button <el-button type="primary" icon="Search" @click="handleQuery"
type="primary" >搜索</el-button
plain >
icon="Plus" <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@click="handleAdd" </el-form-item>
v-hasPermi="['system:dict:add']" </el-form>
>新增</el-button>
</el-col> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
type="success" type="primary"
plain plain
icon="Edit" icon="Plus"
:disabled="single" @click="handleAdd"
@click="handleUpdate" v-hasPermi="['system:dict:add']"
v-hasPermi="['system:dict:edit']" >新增</el-button
>修改</el-button> >
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
type="danger" type="success"
plain plain
icon="Delete" icon="Edit"
:disabled="multiple" :disabled="single"
@click="handleDelete" @click="handleUpdate"
v-hasPermi="['system:dict:remove']" v-hasPermi="['system:dict:edit']"
>删除</el-button> >修改</el-button
</el-col> >
<el-col :span="1.5"> </el-col>
<el-button <el-col :span="1.5">
type="warning" <el-button
plain type="danger"
icon="Download" plain
@click="handleExport" icon="Delete"
v-hasPermi="['system:dict:export']" :disabled="multiple"
>导出</el-button> @click="handleDelete"
</el-col> v-hasPermi="['system:dict:remove']"
<el-col :span="1.5"> >删除</el-button
<el-button >
type="danger" </el-col>
plain <el-col :span="1.5">
icon="Refresh" <el-button
@click="handleRefreshCache" type="warning"
v-hasPermi="['system:dict:remove']" plain
>刷新缓存</el-button> icon="Download"
</el-col> @click="handleExport"
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> v-hasPermi="['system:dict:export']"
</el-row> >导出</el-button
>
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange"> </el-col>
<el-table-column type="selection" width="55" align="center" /> <el-col :span="1.5">
<el-table-column label="字典编号" align="center" prop="dictId" /> <el-button
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true"/> type="danger"
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true"> plain
<template #default="scope"> icon="Refresh"
<router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type"> @click="handleRefreshCache"
<span>{{ scope.row.dictType }}</span> v-hasPermi="['system:dict:remove']"
</router-link> >刷新缓存</el-button
</template> >
</el-table-column> </el-col>
<el-table-column label="状态" align="center" prop="status"> <right-toolbar
<template #default="scope"> v-model:showSearch="showSearch"
<dict-tag :options="sys_normal_disable" :value="scope.row.status" /> @queryTable="getList"
</template> ></right-toolbar>
</el-table-column> </el-row>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /> </div>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope"> <div :style="containerHeight">
<span>{{ parseTime(scope.row.createTime) }}</span> <el-table
</template> height="100%"
</el-table-column> v-loading="loading"
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width"> :data="typeList"
<template #default="scope"> @selection-change="handleSelectionChange"
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']">修改</el-button> >
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']">删除</el-button> <el-table-column type="selection" width="55" align="center" />
</template> <el-table-column label="字典编号" align="center" prop="dictId" />
</el-table-column> <el-table-column
</el-table> label="字典名称"
align="center"
<pagination prop="dictName"
v-show="total > 0" :show-overflow-tooltip="true"
:total="total" />
v-model:page="queryParams.pageNum" <el-table-column
v-model:limit="queryParams.pageSize" label="字典类型"
@pagination="getList" align="center"
/> :show-overflow-tooltip="true"
>
<!-- 添加或修改参数配置对话框 --> <template #default="scope">
<el-dialog :title="title" v-model="open" width="500px" append-to-body> <router-link
<el-form ref="dictRef" :model="form" :rules="rules" label-width="80px"> :to="'/system/dict-data/index/' + scope.row.dictId"
<el-form-item label="字典名称" prop="dictName"> class="link-type"
<el-input v-model="form.dictName" placeholder="请输入字典名称" /> >
</el-form-item> <span>{{ scope.row.dictType }}</span>
<el-form-item label="字典类型" prop="dictType"> </router-link>
<el-input v-model="form.dictType" placeholder="请输入字典类型" /> </template>
</el-form-item> </el-table-column>
<el-form-item label="状态" prop="status"> <el-table-column label="状态" align="center" prop="status">
<el-radio-group v-model="form.status"> <template #default="scope">
<el-radio <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
v-for="dict in sys_normal_disable" </template>
:key="dict.value" </el-table-column>
:value="dict.value" <el-table-column
>{{ dict.label }}</el-radio> label="备注"
</el-radio-group> align="center"
</el-form-item> prop="remark"
<el-form-item label="备注" prop="remark"> :show-overflow-tooltip="true"
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> />
</el-form-item> <el-table-column
</el-form> label="创建时间"
<template #footer> align="center"
<div class="dialog-footer"> prop="createTime"
<el-button type="primary" @click="submitForm"> </el-button> width="180"
<el-button @click="cancel"> </el-button> >
</div> <template #default="scope">
</template> <span>{{ parseTime(scope.row.createTime) }}</span>
</el-dialog> </template>
</div> </el-table-column>
</template> <el-table-column
label="操作"
<script setup name="Dict"> align="center"
import useDictStore from '@/store/modules/dict' width="160"
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type"; class-name="small-padding fixed-width"
>
const { proxy } = getCurrentInstance(); <template #default="scope">
const { sys_normal_disable } = proxy.useDict("sys_normal_disable"); <el-button
link
const typeList = ref([]); type="primary"
const open = ref(false); icon="Edit"
const loading = ref(true); @click="handleUpdate(scope.row)"
const showSearch = ref(true); v-hasPermi="['system:dict:edit']"
const ids = ref([]); >修改</el-button
const single = ref(true); >
const multiple = ref(true); <el-button
const total = ref(0); link
const title = ref(""); type="primary"
const dateRange = ref([]); icon="Delete"
@click="handleDelete(scope.row)"
const data = reactive({ v-hasPermi="['system:dict:remove']"
form: {}, >删除</el-button
queryParams: { >
pageNum: 1, </template>
pageSize: 10, </el-table-column>
dictName: undefined, </el-table>
dictType: undefined,
status: undefined <pagination
}, v-show="total > 0"
rules: { :total="total"
dictName: [{ required: true, message: "字典名称不能为空", trigger: "blur" }], v-model:page="queryParams.pageNum"
dictType: [{ required: true, message: "字典类型不能为空", trigger: "blur" }] v-model:limit="queryParams.pageSize"
}, @pagination="getList"
}); />
</div>
const { queryParams, form, rules } = toRefs(data);
<!-- 添加或修改参数配置对话框 -->
/** 查询字典类型列表 */ <el-dialog :title="title" v-model="open" width="500px" append-to-body>
function getList() { <el-form ref="dictRef" :model="form" :rules="rules" label-width="80px">
loading.value = true; <el-form-item label="字典名称" prop="dictName">
listType(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { <el-input v-model="form.dictName" placeholder="请输入字典名称" />
typeList.value = response.rows; </el-form-item>
total.value = response.total; <el-form-item label="字典类型" prop="dictType">
loading.value = false; <el-input v-model="form.dictType" placeholder="请输入字典类型" />
}); </el-form-item>
} <el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
/** 取消按钮 */ <el-radio
function cancel() { v-for="dict in sys_normal_disable"
open.value = false; :key="dict.value"
reset(); :value="dict.value"
} >{{ dict.label }}</el-radio
>
/** 表单重置 */ </el-radio-group>
function reset() { </el-form-item>
form.value = { <el-form-item label="备注" prop="remark">
dictId: undefined, <el-input
dictName: undefined, v-model="form.remark"
dictType: undefined, type="textarea"
status: "0", placeholder="请输入内容"
remark: undefined ></el-input>
}; </el-form-item>
proxy.resetForm("dictRef"); </el-form>
} <template #footer>
<div class="dialog-footer">
/** 搜索按钮操作 */ <el-button type="primary" @click="submitForm"> </el-button>
function handleQuery() { <el-button @click="cancel"> </el-button>
queryParams.value.pageNum = 1; </div>
getList(); </template>
} </el-dialog>
</div>
/** 重置按钮操作 */ </template>
function resetQuery() {
dateRange.value = []; <script setup name="Dict">
proxy.resetForm("queryRef"); import useDictStore from "@/store/modules/dict";
handleQuery(); import {
} listType,
getType,
/** 新增按钮操作 */ delType,
function handleAdd() { addType,
reset(); updateType,
open.value = true; refreshCache,
title.value = "添加字典类型"; } from "@/api/system/dict/type";
}
import { useContainerHeight } from "@/hooks/tableHeight";
/** 多选框选中数据 */
function handleSelectionChange(selection) { const topContainerRef = ref();
ids.value = selection.map(item => item.dictId); const containerHeight = useContainerHeight(topContainerRef);
single.value = selection.length != 1;
multiple.value = !selection.length; const { proxy } = getCurrentInstance();
} const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
/** 修改按钮操作 */ const typeList = ref([]);
function handleUpdate(row) { const open = ref(false);
reset(); const loading = ref(true);
const dictId = row.dictId || ids.value; const showSearch = ref(true);
getType(dictId).then(response => { const ids = ref([]);
form.value = response.data; const single = ref(true);
open.value = true; const multiple = ref(true);
title.value = "修改字典类型"; const total = ref(0);
}); const title = ref("");
} const dateRange = ref([]);
/** 提交按钮 */ const data = reactive({
function submitForm() { form: {},
proxy.$refs["dictRef"].validate(valid => { queryParams: {
if (valid) { pageNum: 1,
if (form.value.dictId != undefined) { pageSize: 10,
updateType(form.value).then(response => { dictName: undefined,
proxy.$modal.msgSuccess("修改成功"); dictType: undefined,
open.value = false; status: undefined,
getList(); },
}); rules: {
} else { dictName: [
addType(form.value).then(response => { { required: true, message: "字典名称不能为空", trigger: "blur" },
proxy.$modal.msgSuccess("新增成功"); ],
open.value = false; dictType: [
getList(); { required: true, message: "字典类型不能为空", trigger: "blur" },
}); ],
} },
} });
});
} const { queryParams, form, rules } = toRefs(data);
/** 删除按钮操作 */ /** 查询字典类型列表 */
function handleDelete(row) { function getList() {
const dictIds = row.dictId || ids.value; loading.value = true;
proxy.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?').then(function() { listType(proxy.addDateRange(queryParams.value, dateRange.value)).then(
return delType(dictIds); (response) => {
}).then(() => { typeList.value = response.rows;
getList(); total.value = response.total;
proxy.$modal.msgSuccess("删除成功"); loading.value = false;
}).catch(() => {}); }
} );
}
/** 导出按钮操作 */
function handleExport() { /** 取消按钮 */
proxy.download("system/dict/type/export", { function cancel() {
...queryParams.value open.value = false;
}, `dict_${new Date().getTime()}.xlsx`); reset();
} }
/** 刷新缓存按钮操作 */ /** 表单重置 */
function handleRefreshCache() { function reset() {
refreshCache().then(() => { form.value = {
proxy.$modal.msgSuccess("刷新成功"); dictId: undefined,
useDictStore().cleanDict(); dictName: undefined,
}); dictType: undefined,
} status: "0",
remark: undefined,
getList(); };
</script> proxy.resetForm("dictRef");
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
handleQuery();
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = "添加字典类型";
}
/** 多选框选中数据 */
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.dictId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const dictId = row.dictId || ids.value;
getType(dictId).then((response) => {
form.value = response.data;
open.value = true;
title.value = "修改字典类型";
});
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["dictRef"].validate((valid) => {
if (valid) {
if (form.value.dictId != undefined) {
updateType(form.value).then((response) => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addType(form.value).then((response) => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
const dictIds = row.dictId || ids.value;
proxy.$modal
.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?')
.then(function () {
return delType(dictIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
"system/dict/type/export",
{
...queryParams.value,
},
`dict_${new Date().getTime()}.xlsx`
);
}
/** 刷新缓存按钮操作 */
function handleRefreshCache() {
refreshCache().then(() => {
proxy.$modal.msgSuccess("刷新成功");
useDictStore().cleanDict();
});
}
getList();
</script>

View File

@ -32,6 +32,13 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :lg="6" :md="12" :sm="24" :xl="6" :xs="24" :xxl="6">
<el-form-item label="场景类型" prop="sceneCode">
<el-select v-model="queryParams.sceneCode" clearable placeholder="请选择场景类型">
<el-option v-for="item in sceneList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</template> </template>
</TableSearch> </TableSearch>
@ -225,6 +232,11 @@
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="场景类型" prop="sceneCode">
<el-select v-model="form.sceneCode" placeholder="请输入检测项描述">
<el-option v-for="item in sceneList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input <el-input
v-model="form.remark" v-model="form.remark"
@ -280,6 +292,20 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const title = ref(""); const title = ref("");
const sceneList = [{
label: '普通',
value: 'NORMAL'
},{
label: '语音交互-唤醒场景',
value: 'VI_WAKEUP_CORPUS'
},{
label: '语音交互-单次对话场景',
value: 'VI_SINGLE_CORPUS'
},{
label: '语音交互-连续对话场景',
value: 'VI_CONTINUOUS_CORPUS'
}]
const data = reactive({ const data = reactive({
form: {}, form: {},
queryParams: { queryParams: {

View File

@ -140,7 +140,7 @@
</el-row> </el-row>
</div> </div>
<div :style="containerHeight"> <div :style="containerHeight">
<el-table <el-table
v-loading="loading" v-loading="loading"
height="100%" height="100%"
@ -148,7 +148,7 @@
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="项目ID" align="center" prop="projectId" /> <el-table-column label="项目ID" align="center" prop="projectId" width="150" show-overflow-tooltip />
<el-table-column label="项目名称" align="center" prop="projectName" width="150" show-overflow-tooltip /> <el-table-column label="项目名称" align="center" prop="projectName" width="150" show-overflow-tooltip />
<el-table-column label="测试人员" align="center" prop="tester" /> <el-table-column label="测试人员" align="center" prop="tester" />
<el-table-column <el-table-column
@ -179,6 +179,12 @@
@click="handleSetting(row)" @click="handleSetting(row)"
>配置 >配置
</el-button> </el-button>
<el-button
size="mini"
type="text"
@click="handleExecute(row)"
>执行
</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
@ -250,6 +256,14 @@
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="form.sort"
placeholder="排序"
/>
</el-form-item>
</el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="项目描述" prop="description"> <el-form-item label="项目描述" prop="description">
<el-input <el-input
@ -524,6 +538,10 @@ const handleSetting = (row) => {
router.push('/vi/plan/index/' + row.projectId) router.push('/vi/plan/index/' + row.projectId)
} }
const handleExecute = (row) => {
console.log('暂未开发')
}
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@ -8,14 +8,6 @@
v-show="showSearch" v-show="showSearch"
label-width="100px" label-width="100px"
> >
<el-form-item label="所属项目ID" prop="projectId">
<el-input
v-model="queryParams.projectId"
placeholder="请输入所属项目ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="方案名称" prop="schemeName"> <el-form-item label="方案名称" prop="schemeName">
<el-input <el-input
v-model="queryParams.schemeName" v-model="queryParams.schemeName"
@ -24,6 +16,14 @@
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="queryParams.remark"
placeholder="请输入备注"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" <el-button type="primary" icon="Search" @click="handleQuery"
>搜索</el-button >搜索</el-button
@ -90,9 +90,9 @@
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="方案ID" align="center" prop="schemeId" /> <el-table-column label="方案ID" align="center" prop="schemeId" min-width="150" show-overflow-tooltip />
<el-table-column label="所属项目ID" align="center" prop="projectId" /> <el-table-column label="所属项目ID" align="center" prop="projectId" min-width="150" show-overflow-tooltip />
<el-table-column label="方案名称" align="center" prop="schemeName" /> <el-table-column label="方案名称" align="center" prop="schemeName" min-width="150" show-overflow-tooltip />
<el-table-column <el-table-column
label="语料ID列表" label="语料ID列表"
align="center" align="center"
@ -100,11 +100,14 @@
width="300" width="300"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="排序" align="center" prop="sort" />
<el-table-column label="备注" align="center" prop="remark" min-width="150" show-overflow-tooltip />
<el-table-column <el-table-column
label="操作" label="操作"
align="center" align="center"
class-name="small-padding fixed-width" class-name="small-padding fixed-width"
width="200"
fixed="right"
> >
<template #default="scope"> <template #default="scope">
<el-button <el-button
@ -176,6 +179,9 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :step="1" step-strictly />
</el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input <el-input
v-model="form.remark" v-model="form.remark"
@ -248,6 +254,7 @@ const data = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
projectId: route.params.id,
schemeName: null, schemeName: null,
corpusIds: null, corpusIds: null,
}, },
@ -340,7 +347,7 @@ function handleUpdate(row) {
function submitForm() { function submitForm() {
proxy.$refs["schemeRef"].validate((valid) => { proxy.$refs["schemeRef"].validate((valid) => {
if (valid) { if (valid) {
if (form.value.sceneType === "1") { if (form.value.sceneType === 1) {
form.value.corpusIds = null; form.value.corpusIds = null;
} else { } else {
if (!form.value?.corpusIds || form.value?.corpusIds?.length === 0) { if (!form.value?.corpusIds || form.value?.corpusIds?.length === 0) {