Initial commit
This commit is contained in:
@@ -8,23 +8,24 @@
|
||||
*/
|
||||
|
||||
import { HttpRequestClient } from '@/utils/request';
|
||||
import type { NodeTemplateQuery, NodeTemplatesResponse, TreeModelDetailsResponse, TreeModelGraph, TreeModelsResponse } from './types';
|
||||
import type { ApiPaginationQuery, BasicResponse } from '@/types';
|
||||
import type { BehaviorTree, BehaviorTreeDetailsResponse, BehaviorTreePageResponse, NodeTemplatesResponse, TreeModelDetailsResponse, TreeModelGraph } from './types';
|
||||
import type { BasicResponse } from '@/types';
|
||||
|
||||
const req = HttpRequestClient.create<BasicResponse>({
|
||||
baseURL: '/bhtree/api',
|
||||
baseURL: '/api',
|
||||
});
|
||||
|
||||
export const findTemplatesByQuery = (query: Partial<NodeTemplateQuery>): Promise<NodeTemplatesResponse> => {
|
||||
return req.postJson('/node-templates', query);
|
||||
export const findNodeTemplates = (): Promise<NodeTemplatesResponse> => {
|
||||
return req.get('/system/nodetemplate/listAll');
|
||||
};
|
||||
|
||||
export const findTreesByQuery = (query: Partial<ApiPaginationQuery>): Promise<TreeModelsResponse> => {
|
||||
return req.postJson<TreeModelsResponse>('/behavior-trees', query);
|
||||
export const findTreesByQuery = (query: Partial<BehaviorTree> = {}): Promise<BehaviorTreePageResponse> => {
|
||||
return req.get<BehaviorTreePageResponse>('/system/behaviortree/list', query);
|
||||
};
|
||||
|
||||
export const findOneTreeById = (id: number): Promise<TreeModelDetailsResponse> => {
|
||||
return req.get(`/behavior-trees/${id}`);
|
||||
|
||||
export const findOneTreeById = (id: number): Promise<BehaviorTreeDetailsResponse> => {
|
||||
return req.get(`/system/behaviortree/${id}`);
|
||||
};
|
||||
|
||||
export const updateTree = (rt: Partial<TreeModelGraph>): Promise<BasicResponse> => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="ks-model-builder-content">
|
||||
<div class="ks-model-builder-actions">
|
||||
<a-space>
|
||||
<a-tooltip v-if="graph && currentTreeModelGraph" placement="top">
|
||||
<a-tooltip v-if="graph && currentNodeGraph" placement="top">
|
||||
<template #title>
|
||||
保存
|
||||
</template>
|
||||
@@ -68,7 +68,7 @@ import Header from './header.vue';
|
||||
import Properties from './properties.vue';
|
||||
import { useGraphCanvas } from './builder/hooks';
|
||||
import { registerNodeElement } from './builder/register';
|
||||
import type { NodeGraph, NodeTemplate, SettingTaskNodeElement, TreeModel, TreeModelGraph } from './types';
|
||||
import type { BehaviorTree, NodeGraph, NodeTemplate, SettingTaskNodeElement, TreeModel, TreeModelGraph } from './types';
|
||||
import { createTree, findOneTreeById, updateTree } from './api';
|
||||
import { createTaskNodeElement, createTaskNodeElementFromTemplate, hasElements, hasRootElementNode, resolveNodeGraph } from './utils/node';
|
||||
import TressCard from './trees-card.vue';
|
||||
@@ -97,7 +97,7 @@ export default defineComponent({
|
||||
const currentZoom = ref<number>(1);
|
||||
const draggedNodeData = ref<NodeTemplate | null>(null);
|
||||
const isDraggingOver = ref(false);
|
||||
const currentTreeModelGraph = ref<TreeModelGraph | null>(null);
|
||||
const currentNodeGraph = ref<NodeGraph | null>(null);
|
||||
const selectedModelNode = ref<Node<NodeProperties> | null>(null);
|
||||
const selectedNodeTaskElement = ref<SettingTaskNodeElement | null>(null);
|
||||
const changed = ref<boolean>(false)
|
||||
@@ -151,7 +151,7 @@ export default defineComponent({
|
||||
safeStopPropagation(e);
|
||||
isDraggingOver.value = false;
|
||||
|
||||
if (!currentTreeModelGraph.value) {
|
||||
if (!currentNodeGraph.value) {
|
||||
message.error('请先选择或者创建行为树.');
|
||||
return;
|
||||
}
|
||||
@@ -201,19 +201,19 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const createElements = () => {
|
||||
if(graph.value && currentTreeModelGraph.value){
|
||||
if(graph.value && currentNodeGraph.value){
|
||||
graph.value.clearCells();
|
||||
setTimeout(()=> {
|
||||
nextTick(()=> {
|
||||
try{
|
||||
if (currentTreeModelGraph.value && !currentTreeModelGraph.value?.graph){
|
||||
currentTreeModelGraph.value.graph = {
|
||||
if (currentNodeGraph.value && !currentNodeGraph.value?.graph){
|
||||
currentNodeGraph.value.graph = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
}
|
||||
}
|
||||
const nodes = currentTreeModelGraph.value?.graph?.nodes ?? [];
|
||||
const edges = currentTreeModelGraph.value?.graph?.edges ?? [];
|
||||
const nodes = currentNodeGraph.value?.graph?.nodes ?? [];
|
||||
const edges = currentNodeGraph.value?.graph?.edges ?? [];
|
||||
|
||||
nodes.forEach(n=> {
|
||||
const node = createTaskNodeElement(n);
|
||||
@@ -297,7 +297,7 @@ export default defineComponent({
|
||||
console.error('handleSelectTree', treeModel);
|
||||
findOneTreeById(treeModel.id).then(r => {
|
||||
if (r.data) {
|
||||
currentTreeModelGraph.value = r.data;
|
||||
currentNodeGraph.value = r.data;
|
||||
createElements();
|
||||
} else {
|
||||
message.error(r.message ?? '行为树不存在.');
|
||||
@@ -308,16 +308,16 @@ export default defineComponent({
|
||||
const handleSave = () => {
|
||||
const graphData: NodeGraph = resolveNodeGraph(graph.value as Graph);
|
||||
console.info('handleSave', graphData);
|
||||
if (!currentTreeModelGraph.value) {
|
||||
if (!currentNodeGraph.value) {
|
||||
message.error('当前决策树不存在');
|
||||
return;
|
||||
}
|
||||
const newModel: TreeModelGraph = {
|
||||
...currentTreeModelGraph.value,
|
||||
...currentNodeGraph.value,
|
||||
graph: graphData,
|
||||
};
|
||||
let res = null;
|
||||
if (currentTreeModelGraph.value.id > 0) {
|
||||
if (currentNodeGraph.value.id > 0) {
|
||||
res = createTree(newModel);
|
||||
} else {
|
||||
res = updateTree(newModel);
|
||||
@@ -351,7 +351,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
return {
|
||||
currentTreeModelGraph,
|
||||
currentNodeGraph,
|
||||
selectedNodeTaskElement,
|
||||
selectedModelNode,
|
||||
graph,
|
||||
|
||||
@@ -66,60 +66,13 @@
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
||||
|
||||
<!-- <a-card class="ks-model-builder-card">-->
|
||||
<!-- <template #title>-->
|
||||
<!-- <span class="ks-model-builder-title-icon icon-model"></span>控制节点-->
|
||||
<!-- </template>-->
|
||||
<!-- <div-->
|
||||
<!-- v-for="nm in controlTemplates"-->
|
||||
<!-- :key="nm.id"-->
|
||||
<!-- :data-type="nm.type"-->
|
||||
<!-- class="ks-model-drag-item"-->
|
||||
<!-- >-->
|
||||
<!-- <img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''"/>-->
|
||||
<!-- <span class="desc">{{ nm.name }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-card>-->
|
||||
|
||||
<!-- <a-card class="ks-model-builder-card">-->
|
||||
<!-- <template #title>-->
|
||||
<!-- <span class="ks-model-builder-title-icon icon-model"></span>条件节点-->
|
||||
<!-- </template>-->
|
||||
<!-- <div-->
|
||||
<!-- v-for="nm in conditionTemplates"-->
|
||||
<!-- :key="nm.id"-->
|
||||
<!-- :data-type="nm.type"-->
|
||||
<!-- class="ks-model-drag-item"-->
|
||||
<!-- >-->
|
||||
<!-- <img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''"/>-->
|
||||
<!-- <span class="desc">{{ nm.name }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-card>-->
|
||||
|
||||
<!-- <a-card class="ks-model-builder-card">-->
|
||||
<!-- <template #title>-->
|
||||
<!-- <span class="ks-model-builder-title-icon icon-model"></span>行为节点-->
|
||||
<!-- </template>-->
|
||||
<!-- <div-->
|
||||
<!-- v-for="nm in actionsTemplates"-->
|
||||
<!-- :key="nm.id"-->
|
||||
<!-- :data-type="nm.type"-->
|
||||
<!-- class="ks-model-drag-item"-->
|
||||
<!-- >-->
|
||||
<!-- <img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''"/>-->
|
||||
<!-- <span class="desc">{{ nm.name }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-card>-->
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
import { defaultNodeTemplateData, defaultNodeTemplateQuery } from './constants';
|
||||
import type { NodeTemplate, NodeTemplateData, NodeTemplateQuery } from './types';
|
||||
import { findTemplatesByQuery } from './api';
|
||||
import type { NodeTemplate } from './types';
|
||||
import { findNodeTemplates } from './api';
|
||||
import { safePreventDefault, safeStopPropagation } from '@/utils/event';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -127,8 +80,7 @@ export default defineComponent({
|
||||
setup(_props, { emit }) {
|
||||
|
||||
const activeKey = ref<number>(1);
|
||||
const templateData = ref<NodeTemplateData>({ ...defaultNodeTemplateData });
|
||||
const templateQuery = ref<NodeTemplateQuery>({ ...defaultNodeTemplateQuery });
|
||||
const templateData = ref<NodeTemplate[]>([]);
|
||||
const isDraggingOver = ref(false);
|
||||
const draggedNodeData = ref<NodeTemplate | null>(null);
|
||||
|
||||
@@ -144,14 +96,12 @@ export default defineComponent({
|
||||
conditionTemplates.value = [];
|
||||
actionsTemplates.value = [];
|
||||
|
||||
findTemplatesByQuery(templateQuery.value).then(r => {
|
||||
findNodeTemplates().then(r => {
|
||||
templateData.value = r.data;
|
||||
if (r.data.templates) {
|
||||
r.data.templates.forEach(tpl => {
|
||||
if (r.data) {
|
||||
r.data.forEach(tpl => {
|
||||
if (tpl.type === 'action') {
|
||||
if(tpl.parameter_defs && tpl.parameter_defs.length>0){
|
||||
actionsTemplates.value.push(tpl);
|
||||
}
|
||||
actionsTemplates.value.push(tpl);
|
||||
} else if (tpl.type === 'parallel' || tpl.type === 'sequence' || tpl.type === 'precondition') {
|
||||
conditionTemplates.value.push(tpl);
|
||||
} else {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<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 size="small" :data-source="treeModelsData.trees || []" style="min-height: 25vh">
|
||||
<a-list size="small" :data-source="behaviorTrees || []" style="min-height: 25vh">
|
||||
<template #renderItem="{ item }">
|
||||
<a-tooltip placement="right">
|
||||
<template #title>
|
||||
@@ -16,60 +15,15 @@
|
||||
</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>-->
|
||||
<!--<!– <template #extra>–>-->
|
||||
<!--<!– <a-tooltip placement="right">–>-->
|
||||
<!--<!– <template #title>–>-->
|
||||
<!--<!– 创建行为树–>-->
|
||||
<!--<!– </template>–>-->
|
||||
<!--<!– <PlusOutlined class="create-tree-icon"></PlusOutlined>–>-->
|
||||
<!--<!– </a-tooltip>–>-->
|
||||
<!--<!– </template>–>-->
|
||||
|
||||
<!-- <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>-->
|
||||
<!--<!– <template #footer>–>-->
|
||||
<!--<!– <div>Footer</div>–>-->
|
||||
<!--<!– </template>–>-->
|
||||
<!-- </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 { CheckOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import type { ApiPaginationQuery } from '@/types';
|
||||
import type { TreeModel, TreeModelsData } from './types';
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import type { BehaviorTree } from './types';
|
||||
import { findTreesByQuery } from './api';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -78,12 +32,12 @@ export default defineComponent({
|
||||
PlusOutlined
|
||||
},
|
||||
setup(_props, { emit }) {
|
||||
const treeModelsData = ref<TreeModelsData>({ ...defaultTreeModelsData });
|
||||
const treeModelsQuery = ref<ApiPaginationQuery>({ ...defaultPaginationRequest });
|
||||
const behaviorTrees = ref<BehaviorTree[]>([]);
|
||||
const behaviorTreeQuery = ref<Partial<BehaviorTree>>({});
|
||||
const activeKey = ref<number>(1)
|
||||
const loadTress = () => {
|
||||
findTreesByQuery(treeModelsQuery.value).then(r => {
|
||||
treeModelsData.value = r.data;
|
||||
findTreesByQuery(behaviorTreeQuery.value).then(r => {
|
||||
behaviorTrees.value = r.rows;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -94,11 +48,11 @@ export default defineComponent({
|
||||
},
|
||||
];
|
||||
|
||||
const handleSelect = (record: TreeModel) => {
|
||||
const handleSelect = (record: BehaviorTree) => {
|
||||
emit('select-tree', record);
|
||||
}
|
||||
|
||||
const customRow = (record: TreeModel) => {
|
||||
const customRow = (record: BehaviorTree) => {
|
||||
return {
|
||||
onClick: (event: any) => {
|
||||
emit('select-tree', record, event);
|
||||
@@ -112,8 +66,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
activeKey,
|
||||
treeModelsData,
|
||||
treeModelsQuery,
|
||||
behaviorTrees,
|
||||
behaviorTreeQuery,
|
||||
loadTress,
|
||||
columns,
|
||||
customRow,
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import type { ApiResponse, NullableString ,ApiPaginationQuery} from '@/types';
|
||||
import type { NodeSetting } from './parameter';
|
||||
import type { ApiDataResponse, ApiPaginationQuery, NullableString } from '@/types';
|
||||
|
||||
export interface NodeTemplate {
|
||||
id: number;
|
||||
name: NullableString;
|
||||
type: NullableString;
|
||||
english_name: NullableString;
|
||||
logicHandler: NullableString;
|
||||
description: NullableString;
|
||||
parameter_defs: NodeSetting[];
|
||||
templeteType: NullableString;
|
||||
englishName: NullableString;
|
||||
}
|
||||
|
||||
export interface NodeTemplateData {
|
||||
@@ -29,7 +29,7 @@ export interface NodeTemplateQuery extends ApiPaginationQuery {
|
||||
type: NullableString;
|
||||
}
|
||||
|
||||
export interface NodeTemplatesResponse extends ApiResponse<NodeTemplateData> {
|
||||
export interface NodeTemplatesResponse extends ApiDataResponse<NodeTemplate[]> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,86 +7,24 @@
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import type { ApiErrors, ApiPagination, ApiResponse, NullableString } from '@/types';
|
||||
import type { NodeGraph } from './node';
|
||||
import type { NullableString, PageableResponse, ApiDataResponse } from '@/types';
|
||||
|
||||
export type TreeModelStatus = string | 'active'
|
||||
export interface BehaviorTree {
|
||||
|
||||
export type TreeNodeType = 'selector' | string;
|
||||
|
||||
// 获取树列表
|
||||
export interface TreeModel {
|
||||
id: number;
|
||||
name: NullableString;
|
||||
english_name: NullableString;
|
||||
description: NullableString;
|
||||
created_at: NullableString;
|
||||
updated_at: NullableString;
|
||||
node_count: number;
|
||||
status: TreeModelStatus;
|
||||
id: number,
|
||||
name: NullableString,
|
||||
description: NullableString,
|
||||
createdAt: NullableString,
|
||||
updatedAt: NullableString,
|
||||
englishName: NullableString,
|
||||
xmlContent: NullableString,
|
||||
}
|
||||
|
||||
export interface TreeModelGraph extends TreeModel {
|
||||
graph: NodeGraph;
|
||||
}
|
||||
|
||||
// 所有行为树列表
|
||||
export interface TreeModelsData {
|
||||
trees: TreeModel[];
|
||||
pagination: ApiPagination;
|
||||
}
|
||||
|
||||
export interface TreeModelsResponse extends ApiResponse<TreeModelsData> {
|
||||
export interface BehaviorTreeDetailsResponse extends ApiDataResponse<BehaviorTree> {
|
||||
|
||||
}
|
||||
|
||||
export interface TreeModelDetailsResponse extends ApiResponse<TreeModelGraph> {
|
||||
export interface BehaviorTreePageResponse extends PageableResponse<BehaviorTree> {
|
||||
|
||||
}
|
||||
|
||||
// 创建行为树
|
||||
export interface RootTreeNodeConfig {
|
||||
type: TreeNodeType;
|
||||
instance_name: NullableString;
|
||||
}
|
||||
|
||||
export interface RootTreeNode {
|
||||
id: number;
|
||||
name: NullableString;
|
||||
english_name: NullableString;
|
||||
created_at: NullableString;
|
||||
root_node_id: number;
|
||||
}
|
||||
|
||||
export interface CreateTreeModel {
|
||||
name: NullableString;
|
||||
english_name: NullableString;
|
||||
description: NullableString;
|
||||
template_id: number;
|
||||
root_node_config: RootTreeNodeConfig;
|
||||
}
|
||||
|
||||
export type CreateTreeModelData = RootTreeNode | ApiErrors
|
||||
|
||||
export interface CreateTreeModelResponse extends ApiResponse<CreateTreeModelData> {
|
||||
|
||||
}
|
||||
|
||||
// 行为树详细信息
|
||||
|
||||
export interface TreeNodeDetailRequest {
|
||||
tree_id: number;
|
||||
include_structure: boolean;
|
||||
include_parameters: boolean;
|
||||
}
|
||||
|
||||
export interface TreeNodeStructure {
|
||||
id: number;
|
||||
template_name: NullableString;
|
||||
instance_name: NullableString;
|
||||
children: TreeNodeStructure[];
|
||||
}
|
||||
|
||||
export interface TreeNodeDetail {
|
||||
tree: TreeModel;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user