UPDATE: VERSION-20260326
This commit is contained in:
@@ -1829,4 +1829,45 @@
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ks-add-parameter-action{
|
||||
color: #eee;
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ks-parameter-setting-tabs{
|
||||
.ant-tabs-nav{
|
||||
background: none;
|
||||
}
|
||||
.ant-tabs-nav-list{
|
||||
margin-left: 0;
|
||||
}
|
||||
&.ant-tabs-left >.ant-tabs-content-holder,
|
||||
&.ant-tabs-left >div>.ant-tabs-content-holder{
|
||||
border-left-color: #09264b;
|
||||
}
|
||||
.ant-tabs-tab-remove{
|
||||
//position: absolute;
|
||||
//right: 10px;
|
||||
//top: 7px;
|
||||
.anticon{
|
||||
color: rgb(173 206 224);
|
||||
}
|
||||
}
|
||||
&.ant-tabs-left >.ant-tabs-nav .ant-tabs-tab{
|
||||
border-radius: 0!important;
|
||||
}
|
||||
&.ant-tabs-card >.ant-tabs-nav .ant-tabs-tab-active,
|
||||
&.ant-tabs-card >div>.ant-tabs-nav .ant-tabs-tab-active {
|
||||
background: #09264c;
|
||||
}
|
||||
|
||||
&.ant-tabs-left >.ant-tabs-content-holder >.ant-tabs-content>.ant-tabs-tabpane,
|
||||
&.ant-tabs-left >div>.ant-tabs-content-holder >.ant-tabs-content>.ant-tabs-tabpane{
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
@@ -73,58 +73,60 @@
|
||||
<template #leftExtra>
|
||||
<span class="ks-model-builder-title-icon icon-input"></span>
|
||||
</template>
|
||||
|
||||
<template #rightExtra v-if="multiableParameters">
|
||||
<a-tooltip title="添加平台" placement="left">
|
||||
<div class="ks-add-parameter-action" @click="()=> addParameterTab()">
|
||||
<a-space>
|
||||
<PlusCircleOutlined/>
|
||||
<span>添加</span>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tab-pane key="1" tab="节点变量">
|
||||
<template v-if="currentElement.parameters && currentElement.parameters.length > 0">
|
||||
<a-form
|
||||
autocomplete="off"
|
||||
layout="vertical"
|
||||
name="basic"
|
||||
style="padding-bottom:15px;"
|
||||
>
|
||||
|
||||
<a-form
|
||||
v-if="currentElement.parameters && currentElement.parameters.length > 0"
|
||||
autocomplete="off"
|
||||
layout="vertical"
|
||||
name="basic"
|
||||
style="padding-bottom:15px;"
|
||||
>
|
||||
<a-form-item v-for="setting in currentElement.parameters" :label="setting.description">
|
||||
<a-input-number v-if="setting.dataType === 'double'" v-model:value="setting.defaultValue" :placeholder="setting.description" size="small" style="width:100%;" />
|
||||
<a-input v-else v-model:value="setting.defaultValue" :placeholder="setting.description" size="small" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template v-if="multiableParameters">
|
||||
<a-tabs class="ks-parameter-setting-tabs"
|
||||
v-model:activeKey="groupedParametersActiveTab"
|
||||
tab-position="left"
|
||||
hide-add
|
||||
size="small"
|
||||
type="editable-card"
|
||||
@edit="onEditParameterTab">
|
||||
<a-tab-pane v-for="(grouped,index) in groupedParameters" :key="index" :tab="`平台 ${index + 1}`" :closable="true">
|
||||
<a-form-item v-for="setting in grouped" :label="setting.description">
|
||||
<a-input-number v-if="setting.dataType === 'double'" v-model:value="setting.defaultValue"
|
||||
:placeholder="setting.description" size="small" style="width:100%;" />
|
||||
<a-input v-else v-model:value="setting.defaultValue" :placeholder="setting.description" size="small" />
|
||||
</a-form-item>
|
||||
</a-tab-pane>
|
||||
<!-- <template #leftExtra>-->
|
||||
<!-- <a-button class="ks-btn" size="small" style="margin-bottom: 15px; width: 100%" block @click="()=> addParameterTab()">-->
|
||||
<!-- <PlusOutlined />-->
|
||||
<!--<!– <span>添加</span>–>-->
|
||||
<!-- </a-button>-->
|
||||
<!-- </template>-->
|
||||
</a-tabs>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item v-for="setting in currentElement.parameters" :label="setting.description">
|
||||
<a-input-number v-if="setting.dataType === 'double'" v-model:value="setting.defaultValue"
|
||||
:placeholder="setting.description" size="small" style="width:100%;" />
|
||||
<a-input v-else v-model:value="setting.defaultValue" :placeholder="setting.description" size="small" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
</a-form>
|
||||
</template>
|
||||
<a-empty v-else>
|
||||
|
||||
</a-empty>
|
||||
<!-- <div class="w-full">-->
|
||||
<!-- <a-space>-->
|
||||
<!-- <a-button size="small" type="primary" @click="addVariable">添加</a-button>-->
|
||||
<!-- </a-space>-->
|
||||
<!-- <a-table-->
|
||||
<!-- :columns="actionSpaceColumns"-->
|
||||
<!-- :dataSource="currentElement.variables"-->
|
||||
<!-- :pagination="false"-->
|
||||
<!-- :scroll="{ x: 500 }"-->
|
||||
<!-- class="mt-1"-->
|
||||
<!-- row-key="key"-->
|
||||
<!-- size="small"-->
|
||||
<!-- style="overflow-y:auto;height:35vh;"-->
|
||||
<!-- >-->
|
||||
<!-- <template #bodyCell="{column, record, index}">-->
|
||||
<!-- <template v-if="column.dataIndex === 'index'">-->
|
||||
<!-- {{ index + 1 }}-->
|
||||
<!-- </template>-->
|
||||
<!-- <template v-else-if="column.dataIndex === '_actions'">-->
|
||||
<!-- <a-button-->
|
||||
<!-- class="btn-link-delete"-->
|
||||
<!-- danger-->
|
||||
<!-- size="small"-->
|
||||
<!-- type="text"-->
|
||||
<!-- @click="()=> removeVariable(record)"-->
|
||||
<!-- >-->
|
||||
<!-- 删除-->
|
||||
<!-- </a-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- <template v-else>-->
|
||||
<!-- <a-input v-model:value="record[column.dataIndex]" size="small" />-->
|
||||
<!-- </template>-->
|
||||
<!-- </template>-->
|
||||
<!-- </a-table>-->
|
||||
<!-- </div>-->
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
@@ -147,8 +149,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, type PropType, ref, watch } from 'vue';
|
||||
import { CheckOutlined } from '@ant-design/icons-vue';
|
||||
import type { ElementVariable, GraphTaskElement } from '../graph';
|
||||
import { CheckOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import type { ElementParameter, ElementVariable, GraphTaskElement } from '../graph';
|
||||
import type { BehaviorTree } from './tree';
|
||||
import type { Graph, Node, NodeProperties } from '@antv/x6';
|
||||
import { generateKey } from '@/utils/strings';
|
||||
@@ -162,7 +164,7 @@ const actionSpaceColumns = [
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
components: { CheckOutlined },
|
||||
components: { CheckOutlined, PlusOutlined, PlusCircleOutlined },
|
||||
props: {
|
||||
tree: { type: [Object, null] as PropType<BehaviorTree | null | undefined>, required: false },
|
||||
treeEditing: { type: Boolean as PropType<boolean>, required: true, default: false },
|
||||
@@ -180,10 +182,98 @@ export default defineComponent({
|
||||
const currentNode = ref<Node | null>(props.node ?? null);
|
||||
const currentElement = ref<GraphTaskElement | null>(null);
|
||||
|
||||
const load = () => {
|
||||
const emptyParameters = ref<ElementParameter[]>([]);
|
||||
const groupedParameters = ref<Array<ElementParameter[]>>([]);
|
||||
const multiableParameters = ref<boolean>(false);
|
||||
const groupedParametersActiveTab = ref<number>(0);
|
||||
|
||||
const createEmptyParameters = (): ElementParameter[] => {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(currentElement.value?.parameters ?? [])) as ElementParameter[];
|
||||
} catch (e: any) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const dumpParameters = (): ElementParameter[] => {
|
||||
return JSON.parse(JSON.stringify(emptyParameters.value)) as ElementParameter[];
|
||||
};
|
||||
|
||||
const addParameterTab = () => {
|
||||
let newParameters = dumpParameters();
|
||||
// 新增一个空的参数分组
|
||||
groupedParameters.value.push(newParameters);
|
||||
// 自动切换到新增的分组
|
||||
groupedParametersActiveTab.value = groupedParameters.value.length - 1;
|
||||
};
|
||||
|
||||
|
||||
const removeParameterTab = (index: number) => {
|
||||
// 边界判断:防止删除不存在的分组 / 只剩一个分组时禁止删除
|
||||
if (index < 0 || index >= groupedParameters.value.length) return;
|
||||
if (groupedParameters.value.length <= 1) return;
|
||||
|
||||
// 从数组中删除对应索引的分组
|
||||
groupedParameters.value.splice(index, 1);
|
||||
|
||||
if (groupedParameters.value.length === 0) {
|
||||
groupedParameters.value.push(dumpParameters());
|
||||
}
|
||||
|
||||
// 删除后处理激活状态:
|
||||
// 如果删除的是最后一个分组,激活前一个
|
||||
if (groupedParametersActiveTab.value >= groupedParameters.value.length) {
|
||||
groupedParametersActiveTab.value = groupedParameters.value.length - 1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const onEditParameterTab = (targetKey: number | MouseEvent, action: string) => {
|
||||
if (action === 'add') {
|
||||
addParameterTab();
|
||||
} else {
|
||||
removeParameterTab(targetKey as number);
|
||||
}
|
||||
};
|
||||
|
||||
const resolveGroupedParameters = () => {
|
||||
emptyParameters.value = createEmptyParameters();
|
||||
// 解构获取当前元素的关键属性,简化代码
|
||||
const { multiable, parameters } = currentElement.value || {};
|
||||
|
||||
// 1. 不满足多分组条件:直接清空分组
|
||||
if (multiable !== true || !parameters || parameters.length === 0) {
|
||||
groupedParameters.value = [];
|
||||
multiableParameters.value = multiable === true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 满足条件:根据 groupIndex 对参数进行分组
|
||||
// 第一步:用 Map 做临时分组(key=groupIndex,value=当前分组的参数数组)
|
||||
const groupMap = new Map<number, ElementParameter[]>();
|
||||
parameters.forEach(param => {
|
||||
const index = param.groupIndex;
|
||||
// 如果 Map 中没有该分组,先初始化空数组
|
||||
if (!groupMap.has(index)) {
|
||||
groupMap.set(index, []);
|
||||
}
|
||||
// 将当前参数推入对应分组
|
||||
groupMap.get(index)!.push(param);
|
||||
});
|
||||
|
||||
// 第二步:将 Map 转换为二维数组(按 groupIndex 升序排序)
|
||||
groupedParameters.value = Array.from(groupMap.entries())
|
||||
// 按分组索引从小到大排序(保证分组顺序正确)
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
// 只保留分组后的参数数组,丢弃 key
|
||||
.map(item => item[1]);
|
||||
|
||||
multiableParameters.value = multiable === true && groupedParameters.value.length > 0;
|
||||
};
|
||||
|
||||
|
||||
const resolveNode = (n?: Node | null | undefined) => {
|
||||
groupedParametersActiveTab.value = 0;
|
||||
currentNode.value = n ?? null;
|
||||
if (n) {
|
||||
const data = n.getData();
|
||||
@@ -191,6 +281,7 @@ export default defineComponent({
|
||||
} else {
|
||||
currentElement.value = null;
|
||||
}
|
||||
resolveGroupedParameters();
|
||||
};
|
||||
|
||||
const addVariable = () => {
|
||||
@@ -218,10 +309,20 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const updateNode = () => {
|
||||
console.error('currentElement.value',currentElement.value)
|
||||
if (currentNode.value && currentElement.value) {
|
||||
// 深拷贝当前元素数据
|
||||
const newElement = JSON.parse(JSON.stringify(currentElement.value)) as GraphTaskElement;
|
||||
|
||||
if (multiableParameters.value) {
|
||||
newElement.parameters = groupedParameters.value.flatMap((group, groupIndex) => {
|
||||
// 遍历每个分组,给组内所有参数统一设置/保持 groupIndex
|
||||
return group.map(param => ({
|
||||
...param,
|
||||
groupIndex: groupIndex // 强制保证:当前参数的分组索引 = 所在分组的索引
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// 更新节点数据
|
||||
currentNode.value.replaceData(newElement);
|
||||
// 触发事件通知父组件
|
||||
@@ -229,6 +330,9 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.node,
|
||||
(n?: Node | null | undefined) => resolveNode(n),
|
||||
@@ -251,11 +355,18 @@ export default defineComponent({
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(() => groupedParameters.value, () => updateNode(), { deep: true });
|
||||
|
||||
watch(() => currentElement.value, () => updateNode(), { deep: true });
|
||||
|
||||
onMounted(() => load());
|
||||
|
||||
return {
|
||||
addParameterTab,
|
||||
groupedParametersActiveTab,
|
||||
multiableParameters,
|
||||
onEditParameterTab,
|
||||
groupedParameters,
|
||||
actionSpaceColumns,
|
||||
activeTopTabsKey,
|
||||
activeBottomTabsKey,
|
||||
|
||||
Reference in New Issue
Block a user