UPDATE: VERSION-20260313

This commit is contained in:
libertyspy
2026-03-13 17:05:08 +08:00
parent a2f2cbb185
commit 7b578f5d63
7 changed files with 96 additions and 54 deletions

View File

@@ -79,7 +79,7 @@ export const createGraphConnectingAttributes = (): Partial<Connecting> => {
const targetData = targetCell.getData() as ModelElement; const targetData = targetCell.getData() as ModelElement;
// 根节点不能作为子节点 // 根节点不能作为子节点
if (targetData.type === 'startEvent') { if (targetData.category === 'root') {
return false; return false;
} }

View File

@@ -3,18 +3,19 @@
<a-card <a-card
:class="[ :class="[
'ks-designer-node', 'ks-designer-node',
`ks-designer-${element?.category ?? 'model'}-node` `ks-designer-${element?.category ?? 'model'}-node`,
`ks-designer-group-${element?.group ?? 'general'}`
]" ]"
hoverable hoverable
> >
<template #title> <template #title>
<a-space> <a-space>
<span class="ks-designer-node-icon"></span> <!-- <span class="ks-designer-node-icon"></span>-->
<span class="ks-designer-node-title">{{ element?.name ?? '-' }}</span> <span class="ks-designer-node-title">{{ element?.name ?? '-' }} {{element?.category}}</span>
</a-space> </a-space>
</template> </template>
<div class="port port-in"> <div class="port port-in" magnet="active">
<div class="triangle-left" data-port="in-0" magnet="passive"></div> <div class="triangle-left" data-port="in-0" magnet="passive"></div>
</div> </div>
<div class="w-full ks-designer-node-text"> <div class="w-full ks-designer-node-text">
@@ -30,7 +31,7 @@
{{ substring(element?.description ?? (element?.name ?? '-'), 40) }} {{ substring(element?.description ?? (element?.name ?? '-'), 40) }}
</p> </p>
</div> </div>
<div class="port port-out"> <div class="port port-out" magnet="active">
<div class="triangle-right" data-port="out-0" magnet="active"></div> <div class="triangle-right" data-port="out-0" magnet="active"></div>
</div> </div>
</a-card> </a-card>
@@ -190,7 +191,7 @@ export default defineComponent({
border-radius: 0; border-radius: 0;
font-size: 12px; font-size: 12px;
padding: 10px 30px !important; padding: 10px 30px !important;
border-top: 1px solid rgba(108, 99, 255, 0.5); //border-top: 1px solid rgba(108, 99, 255, 0.5);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -234,39 +235,42 @@ export default defineComponent({
.triangle-left { .triangle-left {
width: 0; width: 0;
height: 0; height: 0;
border-top: 5px solid transparent; border-top: 4px solid transparent;
border-right: 10px solid #3c82f6; border-right: 5px solid #5da1df;
border-bottom: 6px solid transparent; border-bottom: 4px solid transparent;
position: absolute; position: absolute;
left: -8px; left: -8px;
top: 2px; top: 0.5px;
magnet: passive;
} }
/* 右三角形 */ /* 右三角形 */
.triangle-right { .triangle-right {
width: 0; width: 0;
height: 0; height: 0;
border-top: 5px solid transparent; border-top: 4px solid transparent;
border-left: 10px solid #3c82f6; border-left: 5px solid #5da1df;
border-bottom: 6px solid transparent; border-bottom: 4px solid transparent;
position: absolute; position: absolute;
right: -8px; right: -8px;
top: 2px; top: 0.5px;
magnet: passive;
} }
} }
// 左侧入桩样式 // 左侧入桩样式
.port-in { .port-in {
background-color: #6C63FF; //background-color: #3c82f6;
margin-right: 8px; margin-right: 8px;
//border: 1px solid #093866; //border: 1px solid #093866;
magnet: passive; magnet: passive;
box-shadow: none; box-shadow: none;
width: 15px; width: 13px;
height: 15px; height: 13px;
display: block; display: block;
background: url('@/assets/icons/point.svg') center / 100% 100%; //background: url('@/assets/icons/point.svg') center / 100% 100%;
border: 2px solid #5da1df;
position: absolute; position: absolute;
//top: 7px; //top: 7px;
@@ -279,10 +283,12 @@ export default defineComponent({
margin-right: 5px; margin-right: 5px;
magnet: active; magnet: active;
box-shadow: none; box-shadow: none;
width: 15px; width: 13px;
height: 15px; height: 13px;
display: block; display: block;
background: url('@/assets/icons/point.svg') center / 100% 100%; //background: url('@/assets/icons/point.svg') center / 100% 100%;
border: 2px solid #5da1df;
background:#5da1df;
position: absolute; position: absolute;
//right: 8px; //right: 8px;
@@ -302,9 +308,6 @@ export default defineComponent({
} }
&.ks-designer-root-node{ &.ks-designer-root-node{
.ant-card-head {
background: url('@/assets/icons/card-head-gray.png') center / 100% 100%;
}
.ks-designer-node-icon { .ks-designer-node-icon {
background: url('@/assets/icons/icon-root.svg') center / 100% 100%; background: url('@/assets/icons/icon-root.svg') center / 100% 100%;
} }
@@ -320,32 +323,66 @@ export default defineComponent({
} }
&.ks-designer-sequence-node{ &.ks-designer-sequence-node{
.ant-card-head {
background: url('@/assets/icons/card-head-green.png') center / 100% 100%;
}
.ks-designer-node-icon { .ks-designer-node-icon {
background: url('@/assets/icons/icon-sequence.svg') center / 100% 100%; background: url('@/assets/icons/icon-sequence.svg') center / 100% 100%;
} }
} }
&.ks-designer-parallel-node{ &.ks-designer-parallel-node{
.ant-card-head {
background: url('@/assets/icons/card-head-blue.png') center / 100% 100%;
}
.ks-designer-node-icon { .ks-designer-node-icon {
background: url('@/assets/icons/icon-parallel.svg') center / 100% 100%; background: url('@/assets/icons/icon-parallel.svg') center / 100% 100%;
} }
} }
&.ks-designer-precondition-node{ &.ks-designer-precondition-node{
.ant-card-head {
background: url('@/assets/icons/card-head-dark.png') center / 100% 100%;
}
.ks-designer-node-icon { .ks-designer-node-icon {
background: url('@/assets/icons/icon-branch.svg') center / 100% 100%; background: url('@/assets/icons/icon-branch.svg') center / 100% 100%;
} }
} }
&.ks-designer-group-control,
&.ks-designer-group-condition {
.ant-card-head{
display:none;
}
.ant-card-body {
height: calc(100%);
border-radius: 8px;
background: url('@/assets/icons/card-head-gray.png') center / 100% 100%;
}
&.ks-designer-root-node{
.ant-card-body {
background: url('@/assets/icons/card-head-dark.png') center / 100% 100%;
}
}
&.ks-designer-sequence-node{
.ant-card-body {
background: url('@/assets/icons/card-head-green.png') center / 100% 100%;
}
}
&.ks-designer-parallel-node{
.ant-card-body {
background: url('@/assets/icons/card-head-blue.png') center / 100% 100%;
}
}
&.ks-designer-precondition-node{
.ant-card-body {
background: url('@/assets/icons/card-head-dark.png') center / 100% 100%;
}
}
.port-in,
.port-out {
top: 40%;
}
.ks-designer-node-text{
line-height: 38px;
}
}
//&.ks-designer-precondition-node{ //&.ks-designer-precondition-node{
// border:0; // border:0;
// box-shadown:none; // box-shadown:none;

View File

@@ -18,10 +18,9 @@ export const createGraphTaskElement = (element: GraphTaskElement, width: number
if (!realHeight) { if (!realHeight) {
realHeight = 120; realHeight = 120;
} }
// if(element.category === 'precondition') { if(element.group === 'condition' || element.group === 'control') {
// width = 100; realHeight = 60;
// realHeight = 100; }
// }
return { return {
shape: 'task', shape: 'task',
id: element.key, id: element.key,

View File

@@ -74,7 +74,7 @@ import { Wrapper } from '@/components/wrapper';
import { safePreventDefault, safeStopPropagation } from '@/utils/event'; import { safePreventDefault, safeStopPropagation } from '@/utils/event';
import Header from './header.vue'; import Header from './header.vue';
import Properties from './properties.vue'; import Properties from './properties.vue';
import type { BehaviorTree, NodeTemplate } from './types'; import type { BehaviorTree, NodeDragTemplate, NodeTemplate } from './types';
import type { GraphTaskElement, NodeGraph } from './builder/element'; import type { GraphTaskElement, NodeGraph } from './builder/element';
import { useGraphCanvas } from './builder/hooks'; import { useGraphCanvas } from './builder/hooks';
import { registerNodeElement } from './builder/register'; import { registerNodeElement } from './builder/register';
@@ -106,7 +106,7 @@ export default defineComponent({
const canvas = ref<HTMLDivElement | null>(null); const canvas = ref<HTMLDivElement | null>(null);
const graph = ref<Graph | null>(null); const graph = ref<Graph | null>(null);
const currentZoom = ref<number>(1); const currentZoom = ref<number>(1);
const draggedNodeData = ref<NodeTemplate | null>(null); const draggedNodeData = ref<NodeDragTemplate | null>(null);
const isDraggingOver = ref(false); const isDraggingOver = ref(false);
const currentTreeEditing = ref<boolean>(false); const currentTreeEditing = ref<boolean>(false);
const currentBehaviorTree = ref<BehaviorTree | null>(null); const currentBehaviorTree = ref<BehaviorTree | null>(null);
@@ -127,7 +127,7 @@ export default defineComponent({
} = useGraphCanvas(); } = useGraphCanvas();
// 处理拖动开始 // 处理拖动开始
const handleDragStart = (nm: NodeTemplate) => { const handleDragStart = (nm: NodeDragTemplate) => {
draggedNodeData.value = nm; draggedNodeData.value = nm;
}; };
@@ -178,7 +178,7 @@ export default defineComponent({
try { try {
// 获取拖动的数据 // 获取拖动的数据
const template = draggedNodeData.value as NodeTemplate; const template = draggedNodeData.value as NodeDragTemplate;
if (!hasElements(graph.value as Graph) && template.type !== 'root') { if (!hasElements(graph.value as Graph) && template.type !== 'root') {
message.error('请先添加根节点.'); message.error('请先添加根节点.');

View File

@@ -13,7 +13,7 @@
:data-type="nm.type" :data-type="nm.type"
class="ks-model-drag-item" class="ks-model-drag-item"
@dragend="handleDragEnd" @dragend="handleDragEnd"
@dragstart="handleDragStart($event, nm)" @dragstart="handleDragStart($event, nm, 'control')"
> >
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" /> <img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
<span class="desc">{{ nm.name }}</span> <span class="desc">{{ nm.name }}</span>
@@ -34,7 +34,7 @@
:data-type="nm.type" :data-type="nm.type"
class="ks-model-drag-item" class="ks-model-drag-item"
@dragend="handleDragEnd" @dragend="handleDragEnd"
@dragstart="handleDragStart($event, nm)" @dragstart="handleDragStart($event, nm, 'condition')"
> >
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" /> <img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
<span class="desc">{{ nm.name }}</span> <span class="desc">{{ nm.name }}</span>
@@ -55,7 +55,7 @@
:data-type="nm.type" :data-type="nm.type"
class="ks-model-drag-item" class="ks-model-drag-item"
@dragend="handleDragEnd" @dragend="handleDragEnd"
@dragstart="handleDragStart($event, nm)" @dragstart="handleDragStart($event, nm, 'action')"
> >
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" /> <img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
<span class="desc">{{ nm.name }}</span> <span class="desc">{{ nm.name }}</span>
@@ -71,7 +71,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, ref } from 'vue'; import { defineComponent, onMounted, ref } from 'vue';
import type { NodeTemplate } from './types'; import type { NodeDragTemplate, NodeTemplate } from './types';
import { findNodeTemplates } from './api'; import { findNodeTemplates } from './api';
import { safePreventDefault, safeStopPropagation } from '@/utils/event'; import { safePreventDefault, safeStopPropagation } from '@/utils/event';
@@ -82,7 +82,7 @@ export default defineComponent({
const activeKey = ref<number>(1); const activeKey = ref<number>(1);
const templateData = ref<NodeTemplate[]>([]); const templateData = ref<NodeTemplate[]>([]);
const isDraggingOver = ref<boolean>(false); const isDraggingOver = ref<boolean>(false);
const draggedNodeData = ref<NodeTemplate | null>(null); const draggedNodeData = ref<NodeDragTemplate | null>(null);
// 控制节点 // 控制节点
const controlTemplates = ref<NodeTemplate[]>([]); const controlTemplates = ref<NodeTemplate[]>([]);
// 条件节点 // 条件节点
@@ -111,15 +111,16 @@ export default defineComponent({
}); });
}; };
const handleDragStart = (e: DragEvent, nm: NodeTemplate) => { const handleDragStart = (e: DragEvent, nm: NodeTemplate, group: String) => {
draggedNodeData.value = nm; let dragNode: NodeDragTemplate = { ...nm, group: group };
draggedNodeData.value = dragNode as NodeDragTemplate;
if (e.dataTransfer) { if (e.dataTransfer) {
e.dataTransfer.setData('text/plain', JSON.stringify(draggedNodeData.value)); e.dataTransfer.setData('text/plain', JSON.stringify(draggedNodeData.value));
e.dataTransfer.effectAllowed = 'copyMove'; e.dataTransfer.effectAllowed = 'copyMove';
const dragPreview = document.createElement('div'); const dragPreview = document.createElement('div');
dragPreview.textContent = draggedNodeData.value.name || ''; dragPreview.textContent = dragNode.name || '';
dragPreview.style.cssText = ` dragPreview.style.cssText = `
position: absolute; position: absolute;
top: -1000px; top: -1000px;
@@ -132,8 +133,8 @@ export default defineComponent({
`; `;
document.body.appendChild(dragPreview); document.body.appendChild(dragPreview);
e.dataTransfer.setDragImage(dragPreview, dragPreview.offsetWidth / 2, dragPreview.offsetHeight / 2); e.dataTransfer.setDragImage(dragPreview, dragPreview.offsetWidth / 2, dragPreview.offsetHeight / 2);
emit('drag-item-start', nm, isDraggingOver.value, e); emit('drag-item-start', dragNode, group, isDraggingOver.value, e);
console.log('开始拖动:', nm); console.log('开始拖动:', dragNode);
setTimeout(() => document.body.removeChild(dragPreview), 0); setTimeout(() => document.body.removeChild(dragPreview), 0);
} }
}; };

View File

@@ -21,6 +21,10 @@ export interface NodeTemplate {
parameters: ElementParameter[], parameters: ElementParameter[],
} }
export interface NodeDragTemplate extends NodeTemplate {
group: String
}
export interface NodeTemplatesResponse extends ApiDataResponse<NodeTemplate[]> { export interface NodeTemplatesResponse extends ApiDataResponse<NodeTemplate[]> {
} }

View File

@@ -7,12 +7,12 @@
* that was distributed with this source code. * that was distributed with this source code.
*/ */
import type { NodeTemplate } from '../types'; import type { NodeDragTemplate } from '../types';
import type { GraphTaskElement, GraphTaskRect } from '../builder/element'; import type { GraphTaskElement, GraphTaskRect } from '../builder/element';
import { generateKey } from '@/utils/strings'; import { generateKey } from '@/utils/strings';
export const createGraphTaskElementFromTemplate = ( export const createGraphTaskElementFromTemplate = (
template: NodeTemplate, template: NodeDragTemplate,
rect?: GraphTaskRect, rect?: GraphTaskRect,
): GraphTaskElement => { ): GraphTaskElement => {
let realRect = { width: 120, height: 80, x: 0, y: 0, ...rect || {} }; let realRect = { width: 120, height: 80, x: 0, y: 0, ...rect || {} };
@@ -25,6 +25,7 @@ export const createGraphTaskElementFromTemplate = (
templateType: template.templateType, templateType: template.templateType,
name: template.name, name: template.name,
category: template.type, category: template.type,
group: template.group,
description: template.description, description: template.description,
order: 0, order: 0,
position: { position: {