Files
auto-solution/modeler/src/views/decision/designer/node.vue
2026-03-15 16:36:07 +08:00

428 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<a-dropdown :trigger="['contextmenu']" @openChange="handleVisibleChange">
<a-card
:class="[
'ks-designer-node',
`ks-designer-${element?.category ?? 'model'}-node`,
`ks-designer-group-${element?.group ?? 'general'}`
]"
hoverable
>
<template #title>
<a-space>
<!-- <span class="ks-designer-node-icon"></span>-->
<span class="ks-designer-node-title">{{ element?.name ?? '-' }}</span>
</a-space>
</template>
<div class="port port-in" data-port="in-0" magnet="passive">
<div class="triangle-left"></div>
</div>
<div class="w-full ks-designer-node-text">
<a-tooltip >
<template #title>
{{ element?.description ?? element?.name }}
</template>
<p class="ks-designer-node-label">
{{ substring(element?.name ?? (element?.name ?? '-'), 40) }}
</p>
</a-tooltip>
</div>
<div class="port port-out" data-port="out-0" magnet="active">
<div class="triangle-right" ></div>
</div>
</a-card>
<template #overlay>
<a-menu @click="handleMenuClick">
<a-menu-item key="delete">
<template #icon>
<DeleteOutlined />
</template>
删除
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
import { elementProps, type ModelElement } from '../graph';
import { DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue';
import type { Graph } from '@antv/x6';
import { substring } from '@/utils/strings';
export default defineComponent({
name: 'ModelElement',
components: {
SettingOutlined,
DeleteOutlined,
},
props: elementProps,
setup(_props) {
const element = ref<ModelElement | null>(
_props.node ? (_props.node.getData() as ModelElement) : null,
);
const updateKey = ref(0);
const isMenuVisible = ref(false);
// 获取画布实例
const getGraph = (): Graph | null => {
return _props.graph as Graph || null;
};
// 监听节点数据变化
const handleDataChange = () => {
if (_props.node) {
element.value = _props.node.getData() as ModelElement;
} else {
element.value = null;
}
updateKey.value++;
};
const handleVisibleChange = (visible: boolean) => {
isMenuVisible.value = visible;
};
const handleMenuClick = ({ key }: { key: string }) => {
if (key === 'delete') {
handleDelete();
}
};
const handleDelete = () => {
if (!_props.node) return;
const graph = getGraph();
if (graph) {
try {
// 先删除关联边
const connectedEdges = graph.getConnectedEdges(_props.node);
connectedEdges.forEach(edge => graph.removeEdge(edge));
// 再删除节点
graph.removeNode(_props.node);
console.info(`节点 ${_props.node.id} 已删除`);
} catch (error) {
console.error('删除节点失败:', error);
}
}
isMenuVisible.value = false;
};
onMounted(() => {
_props.node?.on('change:data', handleDataChange);
});
onUnmounted(() => {
_props.node?.off('change:data', handleDataChange);
});
return {
element,
substring,
handleMenuClick,
handleVisibleChange,
};
},
});
</script>
<style lang="less">
.ks-designer-node {
background: linear-gradient(150deg, rgba(108, 99, 255) 1%, rgba(108, 99, 255) 100%);
border-radius: 8px;
width: 100%;
height: 100%;
cursor: pointer;
position: relative;
background: #1e2533;
border: 1px solid #4a7aff;
border: 2px solid #000000;
&:hover {
border: 2px solid #4a7aff;
box-shadow: 0 0 10px rgba(74, 122, 255, 0.3);
}
.ant-card-head {
border: 0;
height: 28px;
min-height: 25px;
border-radius: 0;
color: #fff;
font-size: 12px;
font-weight: normal;
padding: 0 20px;
//background: linear-gradient(to bottom, #3a4c70, #2d3a56);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
background: linear-gradient(to bottom, rgba(108, 99, 255, 0.15), rgba(108, 99, 255, 0.05));
//background: url('@/assets/icons/bg-node-head.png') center / 100% 100%;
//background: linear-gradient(to bottom, rgb(234 234 234 / 20%), rgb(191 191 191 / 58%));
}
.ks-designer-node-icon {
width: 15px;
height: 15px;
display: block;
position: absolute;
left: 8px;
top: 6px;
background: url('@/assets/icons/icon-node.svg') center / 100% 100%;
}
.ks-designer-node-title {
font-size: 12px;
color: #fff;
margin-top: -7px;
display: block;
}
.ant-card-body {
color: #f5f5f5;
height: calc(100% - 25px);
border-radius: 0;
font-size: 12px;
padding: 10px 30px !important;
//border-top: 1px solid rgba(108, 99, 255, 0.5);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
box-shadow: 0 0 10px rgba(74, 122, 255, 0.3);
white-space: normal; // 恢复默认的换行行为
word-wrap: break-word; // 允许长单词换行
word-break: break-all; // 允许在任意字符处换行
line-height: 1.4; // 增加行高提升可读性
box-shadow: 0 0 10px rgba(74, 122, 255, 0.3);
}
// 连接桩容器样式
.ks-designer-node-content {
width: 100%;
display: flex;
flex-direction: column;
gap: 4px;
}
.ks-designer-node-row {
width: 100%;
display: flex;
align-items: center;
position: relative;
min-height: 24px;
}
.port {
width: 12px;
height: 12px;
border-radius: 50%;
cursor: crosshair;
flex-shrink: 0;
box-shadow: 0 0 0 2px rgb(108, 99, 255, 0.8);
z-index: 10;
magnet: true;
position: relative;
.triangle-left {
width: 0;
height: 0;
border-top: 4px solid transparent;
border-right: 5px solid #5da1df;
border-bottom: 4px solid transparent;
position: absolute;
left: -8px;
top: 0.5px;
magnet: passive;
}
/* 右三角形 */
.triangle-right {
width: 0;
height: 0;
border-top: 4px solid transparent;
border-left: 5px solid #5da1df;
border-bottom: 4px solid transparent;
position: absolute;
right: -8px;
top: 0.5px;
magnet: passive;
}
}
// 左侧入桩样式
.port-in {
//background-color: #3c82f6;
margin-right: 8px;
//border: 1px solid #093866;
magnet: passive;
box-shadow: none;
width: 13px;
height: 13px;
display: block;
//background: url('@/assets/icons/point.svg') center / 100% 100%;
border: 2px solid #5da1df;
position: absolute;
//top: 7px;
left: 10px;
top: 50%;
}
.port-out {
margin-left: 8px;
margin-right: 5px;
magnet: active;
box-shadow: none;
width: 13px;
height: 13px;
display: block;
//background: url('@/assets/icons/point.svg') center / 100% 100%;
border: 2px solid #5da1df;
background:#5da1df;
position: absolute;
//right: 8px;
//top: 7px;
top: 50%;
right: 6px;
}
// 节点文本样式
.ks-designer-node-name {
flex: 1;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
//white-space: nowrap;
}
&.ks-designer-root-node{
.ks-designer-node-icon {
background: url('@/assets/icons/icon-root.svg') center / 100% 100%;
}
}
&.ks-designer-action-node{
.ant-card-head {
background: url('@/assets/icons/card-head-red.png') center / 100% 100%;
}
.ks-designer-node-icon {
background: url('@/assets/icons/icon-action.svg') center / 100% 100%;
}
}
&.ks-designer-sequence-node{
.ks-designer-node-icon {
background: url('@/assets/icons/icon-sequence.svg') center / 100% 100%;
}
}
&.ks-designer-parallel-node{
.ks-designer-node-icon {
background: url('@/assets/icons/icon-parallel.svg') center / 100% 100%;
}
}
&.ks-designer-precondition-node{
.ks-designer-node-icon {
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{
// border:0;
// box-shadown:none;
// &:hover{
// border:0;
// box-shadown:none;
// }
// background: url('@/assets/icons/lx.svg') center / 100% 100%;
// //transform: rotate(45deg);
// .ant-card-body {
// border: 0;
// box-shadow: none;
// height: 95px;
// line-height: 80px;
// font-size: 10px;
// padding: 0 !important;
// }
// .ant-card-head {
// display: none;
// }
//
// .ks-designer-node-label {
// width: 40px; /* 保留原有宽度 */
// text-align: center; /* 保留文字居中 */
// /* 核心修改:取消固定行高,重置换行相关属性 */
// word-wrap: break-word;/* 强制换行(兼容老旧浏览器) */
// word-break: break-all;/* 截断长单词/字符确保在40px内换行 */
// white-space: normal; /* 恢复默认换行规则(避免文字不换行) */
// /* 可选:添加行间距,提升多行可读性 */
// line-height: 1.4; /* 多行时的行间距,可根据需求调整 */
// padding: 10px 0; /* 上下内边距替代原有line-height:98px的垂直居中效果 */
// margin: 31% auto;
// }
//
// .port-in {
// left: 12px;
// top: 42px;
// }
//
// .port-out {
// right: 6px;
// top: 42px;
// }
//}
}
</style>