Initial commit

This commit is contained in:
libertyspy
2026-02-08 16:01:21 +08:00
parent 9ded6b757c
commit c9d5c38b52
11 changed files with 137 additions and 101 deletions

View File

@@ -29,9 +29,9 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import {defineComponent} from 'vue';
import { CheckOutlined, RollbackOutlined } from '@ant-design/icons-vue';
import { elementProps } from './builder/props';
import {elementProps} from './builder/props'
export default defineComponent({
components: {
@@ -42,20 +42,20 @@ export default defineComponent({
emits: ['save'],
setup(props, ctx) {
const handleSave = () => {
ctx.emit('save');
};
const handleSave = ()=> {
ctx.emit('save')
}
const goback = () => {
const goback = ()=> {
};
}
return {
graph: props.graph,
node: props.node,
handleSave,
goback,
};
}
},
});
})
</script>

View File

@@ -12,7 +12,7 @@ import type { NodeTemplateQuery, NodeTemplatesResponse, TreeModelDetailsResponse
import type { ApiPaginationQuery, BasicResponse } from '@/types';
const req = HttpRequestClient.create<BasicResponse>({
baseURL: '/api',
baseURL: '/bhtree/api',
});
export const findTemplatesByQuery = (query: Partial<NodeTemplateQuery>): Promise<NodeTemplatesResponse> => {
@@ -20,7 +20,7 @@ export const findTemplatesByQuery = (query: Partial<NodeTemplateQuery>): Promise
};
export const findTreesByQuery = (query: Partial<ApiPaginationQuery>): Promise<TreeModelsResponse> => {
return req.get<TreeModelsResponse>('/system/behaviortree/list', query);
return req.postJson<TreeModelsResponse>('/behavior-trees', query);
};
export const findOneTreeById = (id: number): Promise<TreeModelDetailsResponse> => {

View File

@@ -98,12 +98,12 @@ export default defineComponent({
};
onMounted(() => {
console.info('node onMounted');
console.info('node onMounted')
_props.node?.on('change:data', handleDataChange);
});
onUnmounted(() => {
console.info('node onUnmounted');
console.info('node onUnmounted')
_props.node?.off('change:data', handleDataChange);
});
@@ -121,7 +121,6 @@ export default defineComponent({
border: 1px dashed #7a6986;
box-shadow: 2px 2px 5px #000000;
}
.ks-designer-node {
background: #1b3875;
//background: url('@/assets/icons/bg-node.png') center / 100% 100%;
@@ -134,7 +133,6 @@ export default defineComponent({
&:hover {
box-shadow: 0 1px 2px -2px rgb(0 0 0), 0 3px 6px 0 rgb(0 0 0 / 60%), 0 5px 12px 4px rgb(0 0 0 / 30%);
}
.ant-card-head {
border: 0;
height: 30px;
@@ -159,33 +157,28 @@ export default defineComponent({
}
//&.ks-designer-node-root{
//background: #645525;
//.ant-card-body{
// background: #726334;
//}
//background: #645525;
//.ant-card-body{
// background: #726334;
//}
//}
&.ks-designer-node-select {
background: #255464;
.ant-card-body {
.ant-card-body{
background: #1c4654;
}
}
&.ks-designer-node-precondition,
&.ks-designer-node-parallel,
&.ks-designer-node-sequence {
&.ks-designer-node-sequence{
background: #4c5a9d;
.ant-card-body {
.ant-card-body{
background: #3f4d8d;
}
}
&.ks-designer-node-action {
&.ks-designer-node-action{
background: #645525;
.ant-card-body {
.ant-card-body{
background: #726334;
}
}

View File

@@ -7,7 +7,7 @@
* that was distributed with this source code.
*/
import type { NodeTemplateData, NodeTemplateQuery, TreeModelsData } from './types';
import type { TreeModelsData, NodeTemplateQuery, NodeTemplateData } from './types';
import type { ApiPagination, ApiPaginationQuery } from '@/types';
export const defaultPagination = {
@@ -26,16 +26,16 @@ export const defaultPaginationRequest = {
page: 1,
limit: 10,
keyword: null,
} as ApiPaginationQuery;
} as ApiPaginationQuery
export const defaultNodeTemplateQuery = {
page: 1,
limit: 1000,
keyword: null,
include_params: true,
} as NodeTemplateQuery;
} as NodeTemplateQuery
export const defaultNodeTemplateData = {
templates: [],
total: 0,
} as NodeTemplateData;
total: 0
} as NodeTemplateData

View File

@@ -46,10 +46,10 @@
</div>
<Properties
v-if="graph"
@update-element="handleUpdateElement"
:element="selectedNodeTaskElement"
:graph="graph as any"
:node="selectedModelNode as any"
@update-element="handleUpdateElement" />
:node="selectedModelNode as any" />
</div>
</a-layout>
</a-layout>
@@ -60,7 +60,7 @@
import { defineComponent, nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
import { message } from 'ant-design-vue';
import { getTeleport } from '@antv/x6-vue-shape';
import { Graph, Node, type NodeProperties } from '@antv/x6';
import { Graph, Node,Edge, type NodeProperties } from '@antv/x6';
import { CheckCircleOutlined, CheckOutlined, RollbackOutlined, SaveOutlined } from '@ant-design/icons-vue';
import { Wrapper } from '@/components/wrapper';
import { safePreventDefault, safeStopPropagation } from '@/utils/event';
@@ -100,7 +100,7 @@ export default defineComponent({
const currentTreeModelGraph = ref<TreeModelGraph | null>(null);
const selectedModelNode = ref<Node<NodeProperties> | null>(null);
const selectedNodeTaskElement = ref<SettingTaskNodeElement | null>(null);
const changed = ref<boolean>(false);
const changed = ref<boolean>(false)
const {
handleGraphEvent,
@@ -201,32 +201,32 @@ export default defineComponent({
};
const createElements = () => {
if (graph.value && currentTreeModelGraph.value) {
if(graph.value && currentTreeModelGraph.value){
graph.value.clearCells();
setTimeout(() => {
nextTick(() => {
try {
if (currentTreeModelGraph.value && !currentTreeModelGraph.value?.graph) {
setTimeout(()=> {
nextTick(()=> {
try{
if (currentTreeModelGraph.value && !currentTreeModelGraph.value?.graph){
currentTreeModelGraph.value.graph = {
nodes: [],
edges: [],
};
}
}
const nodes = currentTreeModelGraph.value?.graph?.nodes ?? [];
const edges = currentTreeModelGraph.value?.graph?.edges ?? [];
const nodes = currentTreeModelGraph.value?.graph?.nodes ?? [];
const edges = currentTreeModelGraph.value?.graph?.edges ?? [];
nodes.forEach(n => {
nodes.forEach(n=> {
const node = createTaskNodeElement(n);
graph.value?.addNode(node as Node);
});
edges.forEach(g => {
graph.value?.addEdge(g as any);
});
} catch (e) {
console.warn('createElements', e);
})
edges.forEach(g=> {
graph.value?.addEdge( g as any);
})
} catch (e){
console.warn('createElements',e)
}
});
}, 200);
})
}, 200)
}
};
@@ -290,7 +290,7 @@ export default defineComponent({
console.info('handleUpdateElement', element);
// 更新本地引用
selectedNodeTaskElement.value = element;
changed.value = true;
changed.value = true
};
const handleSelectTree = (treeModel: TreeModel) => {

View File

@@ -7,7 +7,7 @@
</template>
<div class="w-full h-full">
<a-row>
<a-col v-for="nm in controlTemplates" :span="12">
<a-col :span="12" v-for="nm in controlTemplates">
<div
:key="nm.id"
:data-type="nm.type"
@@ -15,7 +15,7 @@
@dragend="handleDragEnd"
@dragstart="handleDragStart($event, nm)"
>
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
<img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''" />
<span class="desc">{{ nm.name }}</span>
</div>
</a-col>
@@ -28,7 +28,7 @@
</template>
<div class="w-full h-full">
<a-row>
<a-col v-for="nm in conditionTemplates" :span="12">
<a-col :span="12" v-for="nm in conditionTemplates">
<div
:key="nm.id"
:data-type="nm.type"
@@ -36,7 +36,7 @@
@dragend="handleDragEnd"
@dragstart="handleDragStart($event, nm)"
>
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
<img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''" />
<span class="desc">{{ nm.name }}</span>
</div>
</a-col>
@@ -49,7 +49,7 @@
</template>
<div class="w-full h-full">
<a-row>
<a-col v-for="nm in actionsTemplates" :span="12">
<a-col :span="12" v-for="nm in actionsTemplates">
<div
:key="nm.id"
:data-type="nm.type"
@@ -57,7 +57,7 @@
@dragend="handleDragEnd"
@dragstart="handleDragStart($event, nm)"
>
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
<img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''" />
<span class="desc">{{ nm.name }}</span>
</div>
</a-col>
@@ -149,7 +149,7 @@ export default defineComponent({
if (r.data.templates) {
r.data.templates.forEach(tpl => {
if (tpl.type === 'action') {
if (tpl.parameter_defs && tpl.parameter_defs.length > 0) {
if(tpl.parameter_defs && tpl.parameter_defs.length>0){
actionsTemplates.value.push(tpl);
}
} else if (tpl.type === 'parallel' || tpl.type === 'sequence' || tpl.type === 'precondition') {
@@ -212,6 +212,6 @@ export default defineComponent({
handleDragEnd,
};
},
});
})
</script>

View File

@@ -14,38 +14,38 @@
style="padding-bottom:15px;"
>
<a-form-item label="节点名称">
<a-input v-model:value="currentElement.name" :placeholder="currentElement.name" size="small" />
<a-input size="small" v-model:value="currentElement.name" :placeholder="currentElement.name" />
</a-form-item>
<a-form-item label="节点介绍">
<a-textarea v-model:value="currentElement.description" :placeholder="currentElement.description" size="small" />
<a-textarea size="small" v-model:value="currentElement.description" :placeholder="currentElement.description" />
</a-form-item>
<a-divider />
<a-form-item label="输入">
<a-textarea v-model:value="currentElement.inputs" size="small" />
<a-textarea size="small" v-model:value="currentElement.inputs" />
</a-form-item>
<a-form-item label="输出">
<a-textarea v-model:value="currentElement.outputs" size="small" />
<a-textarea size="small" v-model:value="currentElement.outputs" />
</a-form-item>
<a-divider v-if="currentElement.settings && currentElement.settings.length > 0" />
<a-divider v-if="currentElement.settings && currentElement.settings.length > 0"/>
<a-form-item v-for="setting in currentElement.settings" :label="setting.description">
<a-input-number v-if="setting.data_type === 'double'" v-model:value="setting.default_value" :placeholder="setting.description" size="small" style="width:100%;" />
<a-input v-else v-model:value="setting.default_value" :placeholder="setting.description" size="small" />
<a-form-item :label="setting.description" v-for="setting in currentElement.settings">
<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 v-else size="small" v-model:value="setting.default_value" :placeholder="setting.description" />
</a-form-item>
</a-form>
</a-tab-pane>
<!-- <a-tab-pane key="2" tab="外观">-->
<!-- <a-tab-pane key="2" tab="外观">-->
<!-- </a-tab-pane>-->
<!-- <a-tab-pane key="3" tab="系统">-->
<!-- </a-tab-pane>-->
<!-- <a-tab-pane key="3" tab="系统">-->
<!-- </a-tab-pane>-->
<!-- </a-tab-pane>-->
</a-tabs>
<a-tabs v-model:activeKey="activeBottomTabsKey" class="ks-model-builder-tabs parameters-tabs">
@@ -83,7 +83,7 @@
</a-button>
</template>
<template v-else>
<a-input v-model:value="record[column.dataIndex]" size="small" />
<a-input v-model:value="record[column.dataIndex]" size="small"/>
</template>
</template>
</a-table>
@@ -113,7 +113,7 @@ import { defineComponent, onMounted, type PropType, ref, watch } from 'vue';
import { CheckOutlined } from '@ant-design/icons-vue';
import type { ElementVariable, SettingTaskNodeElement } from './types';
import type { Graph, Node, NodeProperties } from '@antv/x6';
import { generateKey } from '@/utils/strings';
import {generateKey} from '@/utils/strings'
const actionSpaceColumns = [
{ title: '序号', dataIndex: 'index', key: 'index', width: 40 },
@@ -127,7 +127,7 @@ export default defineComponent({
components: { CheckOutlined },
props: {
node: { type: [Object, null] as PropType<Node<NodeProperties> | null | undefined>, required: false },
graph: { type: [Object, null] as PropType<Graph | null | undefined>, required: true },
graph: { type: [Object, null] as PropType<Graph | null | undefined>, required: true }
},
emits: ['update-element'],
setup(props, { emit }) {
@@ -151,7 +151,7 @@ export default defineComponent({
}
};
const addVariable = () => {
const addVariable = ()=> {
if (!currentElement.value) {
return;
}
@@ -165,8 +165,8 @@ export default defineComponent({
value: null,
defaults: null,
unit: null,
});
};
})
}
const removeVariable = (row: ElementVariable) => {
if (currentElement.value && currentElement.value.variables) {

View File

@@ -1,10 +1,11 @@
<template>
<a-collapse v-model:activeKey="activeKey" :accordion="false">
<a-collapse-panel key="1">
<template #header>
<span class="ks-model-builder-title-icon icon-model"></span>我的行为树
</template>
<a-list :data-source="treeModelsData.trees || []" size="small" style="min-height: 25vh">
<a-list size="small" :data-source="treeModelsData.trees || []" style="min-height: 25vh">
<template #renderItem="{ item }">
<a-tooltip placement="right">
<template #title>
@@ -15,15 +16,58 @@
</a-list-item>
</a-tooltip>
</template>
<!-- <template #footer>-->
<!-- <div>Footer</div>-->
<!-- </template>-->
</a-list>
</a-collapse-panel>
</a-collapse>
<!-- <a-card class="ks-model-builder-card tress-list-card">-->
<!-- <template #title>-->
<!-- <span class="ks-model-builder-title-icon icon-model"></span>我的行为树-->
<!-- </template>-->
<!--&lt;!&ndash; <template #extra>&ndash;&gt;-->
<!--&lt;!&ndash; <a-tooltip placement="right">&ndash;&gt;-->
<!--&lt;!&ndash; <template #title>&ndash;&gt;-->
<!--&lt;!&ndash; 创建行为树&ndash;&gt;-->
<!--&lt;!&ndash; </template>&ndash;&gt;-->
<!--&lt;!&ndash; <PlusOutlined class="create-tree-icon"></PlusOutlined>&ndash;&gt;-->
<!--&lt;!&ndash; </a-tooltip>&ndash;&gt;-->
<!--&lt;!&ndash; </template>&ndash;&gt;-->
<!-- <a-list size="small" :data-source="treeModelsData.trees || []">-->
<!-- <template #renderItem="{ item }">-->
<!-- <a-tooltip placement="right">-->
<!-- <template #title>-->
<!-- {{ item.description }}-->
<!-- </template>-->
<!-- <a-list-item @click="()=> handleSelect(item)">-->
<!-- {{ item.name }}-->
<!-- </a-list-item>-->
<!-- </a-tooltip>-->
<!-- </template>-->
<!--&lt;!&ndash; <template #footer>&ndash;&gt;-->
<!--&lt;!&ndash; <div>Footer</div>&ndash;&gt;-->
<!--&lt;!&ndash; </template>&ndash;&gt;-->
<!-- </a-list>-->
<!-- <a-table-->
<!-- size="small"-->
<!-- :data-source="treeModelsData.trees || []"-->
<!-- :columns="columns"-->
<!-- :customRow="customRow"-->
<!-- :row-key="(record: any) => record.id">-->
<!-- <template #bodyCell="{ text }">-->
<!-- {{ text }}-->
<!-- </template>-->
<!-- </a-table>-->
<!-- </a-card>-->
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import { defaultPaginationRequest, defaultTreeModelsData } from './constants';
import { PlusOutlined } from '@ant-design/icons-vue';
import { CheckOutlined, PlusOutlined } from '@ant-design/icons-vue';
import type { ApiPaginationQuery } from '@/types';
import type { TreeModel, TreeModelsData } from './types';
import { findTreesByQuery } from './api';
@@ -31,12 +75,12 @@ import { findTreesByQuery } from './api';
export default defineComponent({
emits: ['select-tree'],
components: {
PlusOutlined,
PlusOutlined
},
setup(_props, { emit }) {
const treeModelsData = ref<TreeModelsData>({ ...defaultTreeModelsData });
const treeModelsQuery = ref<ApiPaginationQuery>({ ...defaultPaginationRequest });
const activeKey = ref<number>(1);
const activeKey = ref<number>(1)
const loadTress = () => {
findTreesByQuery(treeModelsQuery.value).then(r => {
treeModelsData.value = r.data;
@@ -52,7 +96,7 @@ export default defineComponent({
const handleSelect = (record: TreeModel) => {
emit('select-tree', record);
};
}
const customRow = (record: TreeModel) => {
return {
@@ -76,16 +120,15 @@ export default defineComponent({
handleSelect,
};
},
});
})
</script>
<style lang="less" scoped>
.create-tree-icon {
.create-tree-icon{
cursor: pointer;
}
.ant-list-item {
padding: 5px 5px;
cursor: pointer;

View File

@@ -7,7 +7,7 @@
* that was distributed with this source code.
*/
export * from './tree';
export * from './template';
export * from './parameter';
export * from './node';
export * from './tree'
export * from './template'
export * from './parameter'
export * from './node'

View File

@@ -7,7 +7,7 @@
* that was distributed with this source code.
*/
import type { ApiPaginationQuery, ApiResponse, NullableString } from '@/types';
import type { ApiResponse, NullableString ,ApiPaginationQuery} from '@/types';
import type { NodeSetting } from './parameter';
export interface NodeTemplate {

View File

@@ -15,8 +15,8 @@ export const createTaskNodeElementFromTemplate = (
template: NodeTemplate,
rect?: TaskNodeRect,
): SettingTaskNodeElement => {
let realRect = { width: 200, height: 100, x: 0, y: 0, ...rect || {} };
console.info('rect', rect);
let realRect = { width: 200, height: 100, x: 0, y: 0, ...rect || {} }
console.info('rect',rect)
return {
id: 0,
key: generateKey(template.type),
@@ -41,15 +41,15 @@ export const createTaskNodeElementFromTemplate = (
name: '范围',
value: '1000',
defaults: '1000',
unit: 'KM',
unit: 'KM'
},
{
key: generateKey('var_'),
name: '武器名称',
value: '地对空导弹',
defaults: '地对空导弹',
unit: '个',
},
unit: '个'
}
],
} as SettingTaskNodeElement;
};
@@ -135,7 +135,7 @@ export const hasElements = (graph: Graph): boolean => {
return taskElements.length > 0;
}
return false;
};
}
export const hasRootElementNode = (graph: Graph): boolean => {
if (graph) {