Compare commits

...

5 Commits

Author SHA1 Message Date
libertyspy
af697e9304 UPDATE: fk 2026-02-08 21:50:33 +08:00
libertyspy
e7abfca9f7 Initial commit 2026-02-08 21:36:39 +08:00
libertyspy
1058d666a0 Initial commit 2026-02-08 20:57:07 +08:00
libertyspy
58d36a3d6d Initial commit 2026-02-08 20:41:49 +08:00
libertyspy
b544391b5c Initial commit 2026-02-08 20:27:40 +08:00
16 changed files with 209 additions and 230 deletions

View File

@@ -95,26 +95,26 @@ public class NodetemplateController extends BaseController {
if (CollectionUtils.isEmpty(list)) { if (CollectionUtils.isEmpty(list)) {
return R.ok(null); return R.ok(null);
} }
// Map<String, List<NodetemplateDTO>> groupedByTemplateType = list.stream() Map<String, List<NodetemplateDTO>> groupedByTemplateType = list.stream()
// .map(template -> { .map(template -> {
// NodetemplateDTO dto = new NodetemplateDTO(); NodetemplateDTO dto = new NodetemplateDTO();
// dto.setId(template.getId()); dto.setId(template.getId());
// dto.setName(template.getName()); dto.setName(template.getName());
// dto.setDescription(template.getDescription()); dto.setDescription(template.getDescription());
// dto.setEnglishName(template.getEnglishName()); dto.setEnglishName(template.getEnglishName());
// dto.setLogicHandler(template.getLogicHandler()); dto.setLogicHandler(template.getLogicHandler());
// dto.setTempleteType(template.getTempleteType()); dto.setTempleteType(template.getTempleteType());
// return dto; return dto;
// }) })
// .collect(Collectors.groupingBy(NodetemplateDTO::getTempleteType)); .collect(Collectors.groupingBy(NodetemplateDTO::getTempleteType));
// List<NodetemplateVO> vos = new ArrayList<>(); List<NodetemplateVO> vos = new ArrayList<>();
// groupedByTemplateType.forEach((key, value) -> { groupedByTemplateType.forEach((key, value) -> {
// // 处理逻辑 // 处理逻辑
// NodetemplateVO vo = new NodetemplateVO(); NodetemplateVO vo = new NodetemplateVO();
// vo.setTempleteType(key); vo.setTempleteType(key);
// vo.setDtoList(value); vo.setDtoList(value);
// vos.add(vo); vos.add(vo);
// }); });
return R.ok(list); return R.ok(list);
} }

View File

@@ -8,7 +8,7 @@
*/ */
import { HttpRequestClient } from '@/utils/request'; import { HttpRequestClient } from '@/utils/request';
import type { BehaviorTree, BehaviorTreeDetailsResponse, BehaviorTreePageResponse, NodeTemplatesResponse } from './types'; import type { BehaviorTree, BehaviorTreeDetailsResponse, BehaviorTreePageResponse, BehaviorTreeRequest, NodeTemplatesResponse } from './types';
import type { BasicResponse } from '@/types'; import type { BasicResponse } from '@/types';
const req = HttpRequestClient.create<BasicResponse>({ const req = HttpRequestClient.create<BasicResponse>({
@@ -19,7 +19,7 @@ export const findNodeTemplates = (): Promise<NodeTemplatesResponse> => {
return req.get('/system/nodetemplate/all'); return req.get('/system/nodetemplate/all');
}; };
export const findTreesByQuery = (query: Partial<BehaviorTree> = {}): Promise<BehaviorTreePageResponse> => { export const findTreesByQuery = (query: Partial<BehaviorTreeRequest> = {}): Promise<BehaviorTreePageResponse> => {
return req.get<BehaviorTreePageResponse>('/system/behaviortree/list', query); return req.get<BehaviorTreePageResponse>('/system/behaviortree/list', query);
}; };

View File

@@ -8,7 +8,6 @@
*/ */
import type { NullableString } from '@/types'; import type { NullableString } from '@/types';
import type { NodeSetting } from '@/views/decision/types';
export interface DraggableElement { export interface DraggableElement {
id: number | null, id: number | null,
@@ -24,7 +23,17 @@ export interface DraggableElement {
export type ElementStatus = 'default' | 'success' | 'failed' | 'running' | string | null export type ElementStatus = 'default' | 'success' | 'failed' | 'running' | string | null
export interface ElementPosition { export interface ElementParameter {
id: number,
templateId: number,
paramKey: NullableString,
dataType: NullableString,
defaultValue: NullableString,
description: NullableString,
templateType: NullableString,
}
export interface GraphPosition {
x: number; x: number;
y: number; y: number;
} }
@@ -37,57 +46,56 @@ export interface ElementVariable {
unit: NullableString; unit: NullableString;
} }
export interface BaseElement { export interface GraphTaskRect {
key: string;
name: string;
type: string;
width: number;
height: number;
position: ElementPosition;
category: NullableString;
element?: DraggableElement;
[key: string]: unknown;
}
export interface TaskNodeRect {
width?: number; width?: number;
height?: number; height?: number;
x?: number; x?: number;
y?: number; y?: number;
} }
export interface TaskNodeElement extends BaseElement { export interface BaseElement {
template: number; id: number;
inputs: any; key: NullableString;
outputs: any; name: NullableString;
variables: ElementVariable[]; description: NullableString;
parameters: Record<any, any>; type: NullableString;
width: number;
children?: TaskNodeElement[], height: number;
position: GraphPosition;
category: NullableString;
element?: DraggableElement;
[key: string]: unknown; [key: string]: unknown;
} }
export interface SettingTaskNodeElement extends TaskNodeElement { export interface GraphTaskElement extends BaseElement {
settings: NodeSetting[]; template: number;
inputs: any;
outputs: any;
variables: ElementVariable[];
parameters: ElementParameter[];
children?: GraphTaskElement[],
[key: string]: unknown;
} }
export interface ModelElement extends BaseElement { export interface ModelElement extends BaseElement {
edges: EdgeNodeElement[]; edges: GraphEdgeElement[];
} }
export interface EdgeNodeElement { export interface GraphEdgeElement {
id: number;
key: NullableString; key: NullableString;
source: NullableString; source: NullableString;
sourceName: NullableString;
target: NullableString; target: NullableString;
targetName: NullableString;
attrs: Record<any, any>; attrs: Record<any, any>;
router: Record<any, any>; router: Record<any, any>;
connector: any; connector: any;
[key: string]: unknown;
} }
export interface NodeGraph { export interface NodeGraph {
edges: EdgeNodeElement[]; edges: GraphEdgeElement[];
nodes: TaskNodeElement[]; nodes: GraphTaskElement[];
} }

View File

@@ -155,6 +155,7 @@ export const useGraphCanvas = (readonly: boolean = false): UseGraphCanvas => {
if (!existingEdge) { if (!existingEdge) {
sourceEdges.push({ sourceEdges.push({
id: sourceData.id,
key: edge.id, key: edge.id,
source: sourceNode.id, source: sourceNode.id,
sourceName: sourceData.name, sourceName: sourceData.name,

View File

@@ -9,64 +9,23 @@
> >
<template #title> <template #title>
<a-space> <a-space>
<span class="ks-designer-node-icon"></span> <div class="port port-in" data-port="in-0" magnet="passive"></div>
<span class="ks-designer-node-title">{{ element?.name ?? '-' }}</span> <span class="ks-designer-node-title">{{ element?.name ?? '-' }}</span>
<div class="port port-out" data-port="out-0" magnet="active"></div>
</a-space> </a-space>
</template> </template>
<!-- 节点内容区域 --> <!-- 节点内容区域 -->
<div class="w-full"> <div class="w-full">
<div class="ks-designer-node-content"> <a-tooltip>
<div <template #title>
v-for="(item, index) in element?.children || []" {{element?.description ?? element?.name}}
:key="item.id || index" </template>
class="ks-designer-node-row" <p>
> {{ substring(element?.description ?? (element?.name ?? '-') ,40) }}
<div </p>
:data-port="`in-${item.id || index}`" </a-tooltip>
:title="`入桩: ${item.name}`"
class="port port-in"
magnet="passive"
></div>
<!-- child名称 -->
<div class="ks-designer-node-name">
{{ item.name }}
</div>
<!-- 右侧出桩只能作为连线源 -->
<div
:data-port="`out-${item.id || index}`"
:title="`出桩: ${item.name}`"
class="port port-out"
magnet="active"
></div>
</div>
<div v-if="!(element?.children && element?.children?.length > 0)" class="ks-designer-node-row">
<div class="port port-in" data-port="in-0" magnet="passive"></div>
<div class="ks-designer-node-name" v-if="element?.category !== 'component'">
{{ element?.name ?? '-' }}
</div>
<div class="ks-designer-node-name" v-else>
<p>隐藏纬度: {{ element?.parameters?.hiddenLatitude ?? '-' }}</p>
<p>激活函数: {{ element?.parameters?.activationFunction ?? '-' }}</p>
</div>
<div class="port port-out" data-port="out-0" magnet="active"></div>
</div>
</div>
</div> </div>
<!-- <div class="w-full" v-else>-->
<!-- <div class="ks-designer-node-content">-->
<!-- <div class="port port-in" data-port="in-0" magnet="passive"></div>-->
<!-- <div class="ks-designer-node-name">-->
<!-- <p>隐藏纬度: {{ element?.parameters?.hiddenLatitude ?? '-' }}</p>-->
<!-- <p>激活函数: {{ element?.parameters?.activationFunction ?? '-' }}</p>-->
<!-- </div>-->
<!-- <div class="port port-out" data-port="out-0" magnet="active"></div>-->
<!-- </div>-->
<!-- </div>-->
</a-card> </a-card>
<template #overlay> <template #overlay>
@@ -88,6 +47,7 @@ import { elementProps } from './props';
import type { ModelElement } from './element'; import type { ModelElement } from './element';
import { DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue'; import { DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue';
import type { Graph } from '@antv/x6'; import type { Graph } from '@antv/x6';
import {substring} from '@/utils/strings'
export default defineComponent({ export default defineComponent({
name: 'ModelElement', name: 'ModelElement',
@@ -157,6 +117,7 @@ export default defineComponent({
return { return {
element, element,
substring,
handleMenuClick, handleMenuClick,
handleVisibleChange, handleVisibleChange,
}; };
@@ -205,10 +166,13 @@ export default defineComponent({
border-radius: 0; border-radius: 0;
font-size: 12px; font-size: 12px;
padding: 8px 15px; padding: 8px 15px;
overflow-y: auto;
border-top: 1px solid #195693; border-top: 1px solid #195693;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
&.ks-designer-model-node,
&.ks-designer-task-node { &.ks-designer-task-node {
background: linear-gradient(150deg, #20421b 1%, #4a6646 55%); background: linear-gradient(150deg, #20421b 1%, #4a6646 55%);
@@ -243,6 +207,8 @@ export default defineComponent({
background: url('@/assets/icons/bg-fk-point.png') center / 100% 100%; background: url('@/assets/icons/bg-fk-point.png') center / 100% 100%;
} }
} }
&.ks-designer-precondition-node,
&.ks-designer-component-node { &.ks-designer-component-node {
background: linear-gradient(150deg, #06226b 1%, #1a43a7 55%); background: linear-gradient(150deg, #06226b 1%, #1a43a7 55%);
@@ -251,6 +217,7 @@ export default defineComponent({
} }
} }
&.ks-designer-select-node,
&.ks-designer-control-node { &.ks-designer-control-node {
background: linear-gradient(150deg, #1d4f32 1%, #326a5d 55%); background: linear-gradient(150deg, #1d4f32 1%, #326a5d 55%);
@@ -305,6 +272,10 @@ export default defineComponent({
height: 20px; height: 20px;
display: block; display: block;
background: url('@/assets/icons/point.svg') center / 100% 100%; background: url('@/assets/icons/point.svg') center / 100% 100%;
position: absolute;
left: 5px;
top: 10px;
} }
// 右侧出桩样式 // 右侧出桩样式
@@ -318,6 +289,10 @@ export default defineComponent({
height: 20px; height: 20px;
display: block; display: block;
background: url('@/assets/icons/arrow-right.svg') center / 100% 100%; background: url('@/assets/icons/arrow-right.svg') center / 100% 100%;
position: absolute;
right: 5px;
top: 10px;
} }
// 节点文本样式 // 节点文本样式
@@ -326,7 +301,7 @@ export default defineComponent({
line-height: 24px; line-height: 24px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; //white-space: nowrap;
} }
} }
</style> </style>

View File

@@ -21,7 +21,7 @@ import ModelElement from './node.vue';
export const registerNodeElement = () => { export const registerNodeElement = () => {
console.info('registerNodeElement'); console.info('registerNodeElement');
register({ register({
shape: 'node', shape: 'task',
component: ModelElement, component: ModelElement,
width: 120, width: 120,
attrs: { attrs: {

View File

@@ -6,20 +6,20 @@
* For the full copyright and license information, please view the LICENSE file * For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code. * that was distributed with this source code.
*/ */
import type { EdgeNodeElement, NodeGraph, TaskNodeElement } from './element'; import type { GraphEdgeElement, GraphTaskElement, NodeGraph } from './element';
import { Edge, Graph, Node } from '@antv/x6'; import { Edge, Graph, Node } from '@antv/x6';
export const defaultHeight: Record<string, number> = { export const defaultHeight: Record<string, number> = {
component: 110, component: 110,
}; };
export const createModelNode = (element: TaskNodeElement, width: number = 250, height: number = 120): any => { export const createGraphTaskElement = (element: GraphTaskElement, width: number = 250, height: number = 120): any => {
let realHeight = defaultHeight[element.category as string]; let realHeight = defaultHeight[element.category as string];
if (!realHeight) { if (!realHeight) {
realHeight = 120; realHeight = 120;
} }
return { return {
shape: 'node', shape: 'task',
id: element.key, id: element.key,
position: { position: {
x: element.position?.x || 0, x: element.position?.x || 0,
@@ -38,13 +38,13 @@ export const createModelNode = (element: TaskNodeElement, width: number = 250, h
}; };
}; };
export const resolveNodeTaskElements = (graph: Graph): TaskNodeElement[] => { export const resolveGraphTaskElements = (graph: Graph): GraphTaskElement[] => {
const taskElements: TaskNodeElement[] = []; const taskElements: GraphTaskElement[] = [];
if (graph) { if (graph) {
const nodes = graph?.getNodes() as Node[]; const nodes = graph?.getNodes() as Node[];
if (nodes) { if (nodes) {
nodes.forEach(node => { nodes.forEach(node => {
const nodeData = node.getData() as TaskNodeElement; const nodeData = node.getData() as GraphTaskElement;
const newElement = { const newElement = {
...nodeData, ...nodeData,
key: node.id, key: node.id,
@@ -59,13 +59,13 @@ export const resolveNodeTaskElements = (graph: Graph): TaskNodeElement[] => {
return taskElements; return taskElements;
}; };
export const resolveNodeEdgeElements = (graph: Graph): EdgeNodeElement[] => { export const resolveGraphEdgeElements = (graph: Graph): GraphEdgeElement[] => {
const edgeElements: EdgeNodeElement[] = []; const edgeElements: GraphEdgeElement[] = [];
if (graph) { if (graph) {
const graphEdges = graph?.getEdges() ?? [] as Edge[]; const graphEdges = graph?.getEdges() ?? [] as Edge[];
if (graphEdges) { if (graphEdges) {
graphEdges.forEach(edge => { graphEdges.forEach(edge => {
const nodeData = edge.getData() as TaskNodeElement; const nodeData = edge.getData() as GraphTaskElement;
edgeElements.push({ edgeElements.push({
id: nodeData?.id ?? 0, id: nodeData?.id ?? 0,
key: edge.id, key: edge.id,
@@ -84,8 +84,8 @@ export const resolveNodeEdgeElements = (graph: Graph): EdgeNodeElement[] => {
}; };
export const resolveNodeGraph = (graph: Graph): NodeGraph => { export const resolveNodeGraph = (graph: Graph): NodeGraph => {
const nodes: TaskNodeElement[] = resolveNodeTaskElements(graph); const nodes: GraphTaskElement[] = resolveGraphTaskElements(graph);
const edges: EdgeNodeElement[] = resolveNodeEdgeElements(graph); const edges: GraphEdgeElement[] = resolveGraphEdgeElements(graph);
return { return {
nodes, nodes,
edges, edges,
@@ -94,7 +94,7 @@ export const resolveNodeGraph = (graph: Graph): NodeGraph => {
export const hasElements = (graph: Graph): boolean => { export const hasElements = (graph: Graph): boolean => {
if (graph) { if (graph) {
const taskElements: TaskNodeElement[] = resolveNodeTaskElements(graph); const taskElements: GraphTaskElement[] = resolveGraphTaskElements(graph);
return taskElements.length > 0; return taskElements.length > 0;
} }
return false; return false;
@@ -102,7 +102,7 @@ export const hasElements = (graph: Graph): boolean => {
export const hasRootElementNode = (graph: Graph): boolean => { export const hasRootElementNode = (graph: Graph): boolean => {
if (graph) { if (graph) {
const taskElements: TaskNodeElement[] = resolveNodeTaskElements(graph); const taskElements: GraphTaskElement[] = resolveGraphTaskElements(graph);
return taskElements.filter(e => e.type === 'root').length === 1; return taskElements.filter(e => e.type === 'root').length === 1;
} }
return false; return false;

View File

@@ -69,10 +69,10 @@ import Properties from './properties.vue';
import { useGraphCanvas } from './builder/hooks'; import { useGraphCanvas } from './builder/hooks';
import { registerNodeElement } from './builder/register'; import { registerNodeElement } from './builder/register';
import type { BehaviorTree, NodeTemplate } from './types'; import type { BehaviorTree, NodeTemplate } from './types';
import type { NodeGraph, SettingTaskNodeElement, TaskNodeElement } from './builder/element'; import type { GraphTaskElement, NodeGraph } from './builder/element';
import { createTree, findOneTreeById, updateTree } from './api'; import { createTree, findOneTreeById, updateTree } from './api';
import { createModelNode, hasElements, hasRootElementNode, resolveNodeGraph } from './builder/utils'; import { createGraphTaskElement, hasElements, hasRootElementNode, resolveNodeGraph } from './builder/utils';
import { createTaskNodeElementFromTemplate } from './utils/node'; import { createGraphTaskElementFromTemplate } from './utils/node';
import TressCard from './trees-card.vue'; import TressCard from './trees-card.vue';
import NodesCard from './nodes-card.vue'; import NodesCard from './nodes-card.vue';
import { createLineOptions } from '@/views/decision/builder/line.ts'; import { createLineOptions } from '@/views/decision/builder/line.ts';
@@ -103,7 +103,7 @@ export default defineComponent({
const currentBehaviorTree = ref<BehaviorTree | null>(null); const currentBehaviorTree = ref<BehaviorTree | null>(null);
const currentNodeGraph = ref<NodeGraph | null>(null); const currentNodeGraph = ref<NodeGraph | null>(null);
const selectedModelNode = ref<Node<NodeProperties> | null>(null); const selectedModelNode = ref<Node<NodeProperties> | null>(null);
const selectedNodeTaskElement = ref<SettingTaskNodeElement | null>(null); const selectedNodeTaskElement = ref<GraphTaskElement | null>(null);
const changed = ref<boolean>(false) const changed = ref<boolean>(false)
const { const {
@@ -188,9 +188,9 @@ export default defineComponent({
console.log('放置节点:', { ...template, x, y }); console.log('放置节点:', { ...template, x, y });
// 创建节点数据 // 创建节点数据
const settingTaskElement: SettingTaskNodeElement = createTaskNodeElementFromTemplate(template, { x, y }); const settingTaskElement: GraphTaskElement = createGraphTaskElementFromTemplate(template, { x, y });
// 创建节点 // 创建节点
const settingTaskNode = createModelNode(settingTaskElement); const settingTaskNode = createGraphTaskElement(settingTaskElement);
console.info('create settingTaskNode: ', settingTaskElement, settingTaskNode); console.info('create settingTaskNode: ', settingTaskElement, settingTaskNode);
// 将节点添加到画布 // 将节点添加到画布
@@ -237,7 +237,7 @@ export default defineComponent({
if (currentBehaviorTree.value?.graph && graph.value) { if (currentBehaviorTree.value?.graph && graph.value) {
if (currentBehaviorTree.value?.graph.nodes) { if (currentBehaviorTree.value?.graph.nodes) {
currentBehaviorTree.value?.graph.nodes.forEach(ele => { currentBehaviorTree.value?.graph.nodes.forEach(ele => {
const node = createModelNode(ele as TaskNodeElement); const node = createGraphTaskElement(ele as GraphTaskElement);
console.info('create node: ', ele); console.info('create node: ', ele);
// 将节点添加到画布 // 将节点添加到画布
graph.value?.addNode(node as Node); graph.value?.addNode(node as Node);
@@ -277,10 +277,10 @@ export default defineComponent({
handleGraphEvent('node:click', (args: any) => { handleGraphEvent('node:click', (args: any) => {
const node = args.node as Node<NodeProperties>; const node = args.node as Node<NodeProperties>;
const newElement = node.getData() as SettingTaskNodeElement; const newElement = node.getData() as GraphTaskElement;
selectedModelNode.value = node; selectedModelNode.value = node;
selectedNodeTaskElement.value = JSON.parse(JSON.stringify(newElement || {})) as SettingTaskNodeElement; selectedNodeTaskElement.value = JSON.parse(JSON.stringify(newElement || {})) as GraphTaskElement;
}); });
// 监听节点鼠标事件,显示/隐藏连接点 // 监听节点鼠标事件,显示/隐藏连接点
@@ -310,7 +310,7 @@ export default defineComponent({
}); });
}; };
const handleUpdateElement = (element: SettingTaskNodeElement) => { const handleUpdateElement = (element: GraphTaskElement) => {
// 更新选中的节点数据 // 更新选中的节点数据
if (selectedModelNode.value) { if (selectedModelNode.value) {
selectedModelNode.value.replaceData(element); selectedModelNode.value.replaceData(element);

View File

@@ -31,21 +31,15 @@
<a-textarea size="small" v-model:value="currentElement.outputs" /> <a-textarea size="small" v-model:value="currentElement.outputs" />
</a-form-item> </a-form-item>
<a-divider v-if="currentElement.settings && currentElement.settings.length > 0"/> <a-divider v-if="currentElement.settings && currentElement.parameters.length > 0"/>
<a-form-item :label="setting.description" v-for="setting in currentElement.settings"> <a-form-item :label="setting.description" v-for="setting in currentElement.parameters">
<a-input-number size="small" style="width:100%;" v-if="setting.data_type === 'double'" v-model:value="setting.default_value" :placeholder="setting.description" /> <a-input-number size="small" style="width:100%;" v-if="setting.dataType === 'double'" v-model:value="setting.defaultValue" :placeholder="setting.description" />
<a-input v-else size="small" v-model:value="setting.default_value" :placeholder="setting.description" /> <a-input v-else size="small" v-model:value="setting.defaultValue" :placeholder="setting.description" />
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
<!-- <a-tab-pane key="2" tab="外观">-->
<!-- </a-tab-pane>-->
<!-- <a-tab-pane key="3" tab="系统">-->
<!-- </a-tab-pane>-->
</a-tabs> </a-tabs>
<a-tabs v-model:activeKey="activeBottomTabsKey" class="ks-model-builder-tabs parameters-tabs"> <a-tabs v-model:activeKey="activeBottomTabsKey" class="ks-model-builder-tabs parameters-tabs">
@@ -111,9 +105,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, type PropType, ref, watch } from 'vue'; import { defineComponent, onMounted, type PropType, ref, watch } from 'vue';
import { CheckOutlined } from '@ant-design/icons-vue'; import { CheckOutlined } from '@ant-design/icons-vue';
import type { ElementVariable, SettingTaskNodeElement } from './builder/element'; import type { ElementVariable, GraphTaskElement } from './builder/element';
import type { Graph, Node, NodeProperties } from '@antv/x6'; import type { Graph, Node, NodeProperties } from '@antv/x6';
import {generateKey} from '@/utils/strings' import { generateKey } from '@/utils/strings';
const actionSpaceColumns = [ const actionSpaceColumns = [
{ title: '序号', dataIndex: 'index', key: 'index', width: 40 }, { title: '序号', dataIndex: 'index', key: 'index', width: 40 },
@@ -136,7 +130,7 @@ export default defineComponent({
const activeBottomTabs2Key = ref<string>('1'); const activeBottomTabs2Key = ref<string>('1');
const currentNode = ref<Node | null>(props.node ?? null); const currentNode = ref<Node | null>(props.node ?? null);
const currentElement = ref<SettingTaskNodeElement | null>(null); const currentElement = ref<GraphTaskElement | null>(null);
const load = () => { const load = () => {
}; };
@@ -145,7 +139,7 @@ export default defineComponent({
currentNode.value = n ?? null; currentNode.value = n ?? null;
if (n) { if (n) {
const data = n.getData(); const data = n.getData();
currentElement.value = JSON.parse(JSON.stringify(data || {})) as SettingTaskNodeElement; currentElement.value = JSON.parse(JSON.stringify(data || {})) as GraphTaskElement;
} else { } else {
currentElement.value = null; currentElement.value = null;
} }
@@ -178,7 +172,7 @@ export default defineComponent({
const updateNode = () => { const updateNode = () => {
if (currentNode.value && currentElement.value) { if (currentNode.value && currentElement.value) {
// 深拷贝当前元素数据 // 深拷贝当前元素数据
const newElement = JSON.parse(JSON.stringify(currentElement.value)) as SettingTaskNodeElement; const newElement = JSON.parse(JSON.stringify(currentElement.value)) as GraphTaskElement;
// 更新节点数据 // 更新节点数据
currentNode.value.replaceData(newElement); currentNode.value.replaceData(newElement);
// 触发事件通知父组件 // 触发事件通知父组件

View File

@@ -1,9 +1,12 @@
<template> <template>
<a-collapse v-model:activeKey="activeKey" :accordion="false"> <a-collapse v-model:activeKey="activeKey" :accordion="false" class="ks-trees-collapse">
<a-collapse-panel key="1"> <a-collapse-panel key="1">
<template #header> <template #header>
<span class="ks-model-builder-title-icon icon-model"></span>我的行为树 <span class="ks-model-builder-title-icon icon-model"></span>我的行为树
</template> </template>
<div class="w-full" style="padding: 5px;">
<a-input-search size="small" allowClear v-model:value="behaviorTreeQuery.name" placeholder="行为树名称" @search="loadTress" />
</div>
<a-list size="small" :data-source="behaviorTrees || []" style="min-height: 25vh"> <a-list size="small" :data-source="behaviorTrees || []" style="min-height: 25vh">
<template #renderItem="{ item }"> <template #renderItem="{ item }">
<a-tooltip placement="right"> <a-tooltip placement="right">
@@ -17,6 +20,11 @@
</a-tooltip> </a-tooltip>
</template> </template>
</a-list> </a-list>
<a-pagination
size="small"
v-model:current="behaviorTreeQuery.pageNum"
:page-size="behaviorTreeQuery.pageSize"
simple :total="totalTress" @change="handleChange" />
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
</template> </template>
@@ -24,9 +32,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, ref } from 'vue'; import { defineComponent, onMounted, ref } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue'; import { PlusOutlined } from '@ant-design/icons-vue';
import type { BehaviorTree } from './types'; import type { BehaviorTree, BehaviorTreeRequest } from './types';
import { findTreesByQuery } from './api'; import { findTreesByQuery } from './api';
import {substring} from '@/utils/strings' import { substring } from '@/utils/strings';
export default defineComponent({ export default defineComponent({
emits: ['select-tree'], emits: ['select-tree'],
@@ -35,14 +43,26 @@ export default defineComponent({
}, },
setup(_props, { emit }) { setup(_props, { emit }) {
const behaviorTrees = ref<BehaviorTree[]>([]); const behaviorTrees = ref<BehaviorTree[]>([]);
const behaviorTreeQuery = ref<Partial<BehaviorTree>>({}); const behaviorTreeQuery = ref<Partial<BehaviorTreeRequest>>({
name: null,
pageNum: 1,
pageSize: 8,
});
const activeKey = ref<number>(1) const activeKey = ref<number>(1)
const totalTress = ref<number>(0);
const loadTress = () => { const loadTress = () => {
findTreesByQuery(behaviorTreeQuery.value).then(r => { findTreesByQuery(behaviorTreeQuery.value).then(r => {
behaviorTrees.value = r.rows; behaviorTrees.value = r.rows;
totalTress.value = r.total ?? 0;
}); });
}; };
const handleChange = (page: number, pageSize: number) => {
behaviorTreeQuery.value.pageNum = page;
behaviorTreeQuery.value.pageSize = pageSize;
loadTress();
};
const columns = [ const columns = [
{ {
title: '名称', title: '名称',
@@ -67,6 +87,7 @@ export default defineComponent({
}); });
return { return {
totalTress,
substring, substring,
activeKey, activeKey,
behaviorTrees, behaviorTrees,
@@ -75,6 +96,7 @@ export default defineComponent({
columns, columns,
customRow, customRow,
handleSelect, handleSelect,
handleChange,
}; };
}, },
}) })
@@ -82,12 +104,52 @@ export default defineComponent({
</script> </script>
<style lang="less" scoped> <style lang="less">
.ant-collapse {
.ant-list-sm {
.ant-list-item {
padding: 4px 15px;
cursor: pointer;
color: rgb(130 196 233);
&:hover {
background: #0d2d4e;
}
}
}
&.ks-trees-collapse {
.ant-collapse-content-box {
padding: 0;
height: 40vh;
position: relative;
}
.ant-pagination{
position: absolute;
bottom: 10px;
width: 100%;
.ant-pagination-disabled .ant-pagination-item-link,
.ant-pagination-disabled:hover .ant-pagination-item-link,
.ant-pagination-prev .ant-pagination-item-link,
.ant-pagination-next .ant-pagination-item-link,
&.ant-pagination-mini .ant-pagination-total-text,
&.ant-pagination-mini .ant-pagination-simple-pager{
color: rgb(255 255 255 / 95%);
}
&.ant-pagination-simple .ant-pagination-simple-pager input {
background-color: transparent;
border: 1px solid #0f4a7c;
color:#eee;
}
}
}
}
.create-tree-icon{ .create-tree-icon{
cursor: pointer; cursor: pointer;
} }
.ant-list-item { .ant-list-item {
padding: 5px 5px; padding: 3px 5px;
cursor: pointer; cursor: pointer;
color: rgb(130 196 233); color: rgb(130 196 233);

View File

@@ -9,4 +9,3 @@
export * from './tree' export * from './tree'
export * from './template' export * from './template'
export * from './parameter'

View File

@@ -1,20 +0,0 @@
/*
* This file is part of the kernelstudio package.
*
* (c) 2014-2026 zlin <admin@kernelstudio.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
import type { NullableString } from '@/types';
export interface NodeSetting {
id: number;
name: NullableString;
type: NullableString;
default_value: NullableString;
data_type: NullableString;
required: boolean;
description: NullableString;
}

View File

@@ -8,16 +8,7 @@
*/ */
import type { ApiDataResponse, NullableString } from '@/types'; import type { ApiDataResponse, NullableString } from '@/types';
import type { ElementParameter } from '../builder/element';
export interface NodeTemplateParameter {
id: number,
templateId: number,
paramKey: NullableString,
dataType: NullableString,
defaultValue: NullableString,
description: NullableString,
templateType: NullableString,
}
export interface NodeTemplate { export interface NodeTemplate {
id: number; id: number;
@@ -27,7 +18,7 @@ export interface NodeTemplate {
description: NullableString; description: NullableString;
templeteType: NullableString; templeteType: NullableString;
englishName: NullableString; englishName: NullableString;
parameters: NodeTemplateParameter[], parameters: ElementParameter[],
} }
export interface NodeTemplatesResponse extends ApiDataResponse<NodeTemplate[]> { export interface NodeTemplatesResponse extends ApiDataResponse<NodeTemplate[]> {

View File

@@ -21,6 +21,11 @@ export interface BehaviorTree {
graph: NodeGraph graph: NodeGraph
} }
export interface BehaviorTreeRequest extends BehaviorTree {
pageNum: number,
pageSize: number,
}
export interface BehaviorTreeDetailsResponse extends ApiDataResponse<BehaviorTree> { export interface BehaviorTreeDetailsResponse extends ApiDataResponse<BehaviorTree> {

View File

@@ -8,22 +8,22 @@
*/ */
import type { NodeTemplate } from '../types'; import type { NodeTemplate } from '../types';
import type { SettingTaskNodeElement, TaskNodeRect } from '../builder/element'; import type { GraphTaskElement, GraphTaskRect } from '../builder/element';
import { generateKey } from '@/utils/strings'; import { generateKey } from '@/utils/strings';
export const createTaskNodeElementFromTemplate = ( export const createGraphTaskElementFromTemplate = (
template: NodeTemplate, template: NodeTemplate,
rect?: TaskNodeRect, rect?: GraphTaskRect,
): SettingTaskNodeElement => { ): GraphTaskElement => {
let realRect = { width: 200, height: 100, x: 0, y: 0, ...rect || {} } let realRect = { width: 200, height: 100, x: 0, y: 0, ...rect || {} }
console.info('rect',rect) console.info('rect',rect)
return { return {
id: 0, id: 0,
key: generateKey(template.type), key: generateKey(template.type),
status: null, type: 'task',
template: template.id, template: template.id,
type: template.type,
name: template.name, name: template.name,
category: template.type,
description: template.description, description: template.description,
position: { position: {
x: realRect.x ?? 0, x: realRect.x ?? 0,
@@ -31,25 +31,9 @@ export const createTaskNodeElementFromTemplate = (
}, },
width: realRect.width, width: realRect.width,
height: realRect.height, height: realRect.height,
// settings: JSON.parse(JSON.stringify(template.parameter_defs ?? [])),
inputs: null, inputs: null,
outputs: null, outputs: null,
parameters: {}, parameters: template.parameters ?? [],
variables: [ variables: [],
{ } as GraphTaskElement;
key: generateKey('var_'),
name: '范围',
value: '1000',
defaults: '1000',
unit: 'KM'
},
{
key: generateKey('var_'),
name: '武器名称',
value: '地对空导弹',
defaults: '地对空导弹',
unit: '个'
}
],
} as SettingTaskNodeElement;
}; };

View File

@@ -15,8 +15,6 @@ declare module 'vue' {
AAvatar: typeof import('ant-design-vue/es')['Avatar'] AAvatar: typeof import('ant-design-vue/es')['Avatar']
AButton: typeof import('ant-design-vue/es')['Button'] AButton: typeof import('ant-design-vue/es')['Button']
ACard: typeof import('ant-design-vue/es')['Card'] ACard: typeof import('ant-design-vue/es')['Card']
ACarousel: typeof import('ant-design-vue/es')['Carousel']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACol: typeof import('ant-design-vue/es')['Col'] ACol: typeof import('ant-design-vue/es')['Col']
ACollapse: typeof import('ant-design-vue/es')['Collapse'] ACollapse: typeof import('ant-design-vue/es')['Collapse']
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
@@ -27,9 +25,7 @@ declare module 'vue' {
AFloatButton: typeof import('ant-design-vue/es')['FloatButton'] AFloatButton: typeof import('ant-design-vue/es')['FloatButton']
AForm: typeof import('ant-design-vue/es')['Form'] AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem'] AFormItem: typeof import('ant-design-vue/es')['FormItem']
AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest']
AInput: typeof import('ant-design-vue/es')['Input'] AInput: typeof import('ant-design-vue/es')['Input']
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
AInputSearch: typeof import('ant-design-vue/es')['InputSearch'] AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
@@ -41,16 +37,10 @@ declare module 'vue' {
AListItem: typeof import('ant-design-vue/es')['ListItem'] AListItem: typeof import('ant-design-vue/es')['ListItem']
AMenu: typeof import('ant-design-vue/es')['Menu'] AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal'] APagination: typeof import('ant-design-vue/es')['Pagination']
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
APopover: typeof import('ant-design-vue/es')['Popover']
AProgress: typeof import('ant-design-vue/es')['Progress']
ARow: typeof import('ant-design-vue/es')['Row'] ARow: typeof import('ant-design-vue/es')['Row']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASpace: typeof import('ant-design-vue/es')['Space'] ASpace: typeof import('ant-design-vue/es')['Space']
ASteps: typeof import('ant-design-vue/es')['Steps']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
ATable: typeof import('ant-design-vue/es')['Table'] ATable: typeof import('ant-design-vue/es')['Table']
ATabPane: typeof import('ant-design-vue/es')['TabPane'] ATabPane: typeof import('ant-design-vue/es')['TabPane']
@@ -67,8 +57,6 @@ declare global {
const AAvatar: typeof import('ant-design-vue/es')['Avatar'] const AAvatar: typeof import('ant-design-vue/es')['Avatar']
const AButton: typeof import('ant-design-vue/es')['Button'] const AButton: typeof import('ant-design-vue/es')['Button']
const ACard: typeof import('ant-design-vue/es')['Card'] const ACard: typeof import('ant-design-vue/es')['Card']
const ACarousel: typeof import('ant-design-vue/es')['Carousel']
const ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
const ACol: typeof import('ant-design-vue/es')['Col'] const ACol: typeof import('ant-design-vue/es')['Col']
const ACollapse: typeof import('ant-design-vue/es')['Collapse'] const ACollapse: typeof import('ant-design-vue/es')['Collapse']
const ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] const ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
@@ -79,9 +67,7 @@ declare global {
const AFloatButton: typeof import('ant-design-vue/es')['FloatButton'] const AFloatButton: typeof import('ant-design-vue/es')['FloatButton']
const AForm: typeof import('ant-design-vue/es')['Form'] const AForm: typeof import('ant-design-vue/es')['Form']
const AFormItem: typeof import('ant-design-vue/es')['FormItem'] const AFormItem: typeof import('ant-design-vue/es')['FormItem']
const AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest']
const AInput: typeof import('ant-design-vue/es')['Input'] const AInput: typeof import('ant-design-vue/es')['Input']
const AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
const AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] const AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
const AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] const AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
const AInputSearch: typeof import('ant-design-vue/es')['InputSearch'] const AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
@@ -93,16 +79,10 @@ declare global {
const AListItem: typeof import('ant-design-vue/es')['ListItem'] const AListItem: typeof import('ant-design-vue/es')['ListItem']
const AMenu: typeof import('ant-design-vue/es')['Menu'] const AMenu: typeof import('ant-design-vue/es')['Menu']
const AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] const AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
const AModal: typeof import('ant-design-vue/es')['Modal'] const APagination: typeof import('ant-design-vue/es')['Pagination']
const APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] const APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
const APopover: typeof import('ant-design-vue/es')['Popover']
const AProgress: typeof import('ant-design-vue/es')['Progress']
const ARow: typeof import('ant-design-vue/es')['Row'] const ARow: typeof import('ant-design-vue/es')['Row']
const ASelect: typeof import('ant-design-vue/es')['Select']
const ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
const ASlider: typeof import('ant-design-vue/es')['Slider']
const ASpace: typeof import('ant-design-vue/es')['Space'] const ASpace: typeof import('ant-design-vue/es')['Space']
const ASteps: typeof import('ant-design-vue/es')['Steps']
const ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] const ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
const ATable: typeof import('ant-design-vue/es')['Table'] const ATable: typeof import('ant-design-vue/es')['Table']
const ATabPane: typeof import('ant-design-vue/es')['TabPane'] const ATabPane: typeof import('ant-design-vue/es')['TabPane']