Files
auto-solution/modeler/src/views/decision/communication/node.vue
2026-03-15 20:20:56 +08:00

319 lines
7.6 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-scenario-node',
`ks-scenario-${element?.category ?? 'model'}-node`
]"
hoverable
>
<template #title>
<a-space>
<span class="ks-scenario-node-title">{{ element?.description ?? element?.name ?? '-' }}</span>
</a-space>
</template>
<!-- 节点内容区域 -->
<div class="w-full">
<div class="ks-scenario-node-content">
<div
v-for="(item, index) in element?.components || []"
:key="item.id || index"
class="ks-scenario-node-row"
>
<div
:data-port="`in-${item.id || index}`"
:title="`入桩: ${item.name}`"
class="port port-in"
magnet="passive"
>
<div class="triangle-left"></div>
</div>
<!-- child名称 -->
<div class="ks-scenario-node-name">
{{ item.description ?? item.name }}
</div>
<!-- 右侧出桩只能作为连线源 -->
<div
:data-port="`out-${item.id || index}`"
:title="`出桩: ${item.name}`"
class="port port-out"
magnet="active"
>
<div class="triangle-right" ></div>
</div>
</div>
</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);
console.error('element',element.value)
});
onUnmounted(() => {
_props.node?.off('change:data', handleDataChange);
});
return {
element,
substring,
handleMenuClick,
handleVisibleChange,
};
},
});
</script>
<style lang="less">
.ks-scenario-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%));
background: url('@/assets/icons/card-head-red.png') center / 100% 100%;
}
.ks-scenario-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-scenario-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-scenario-node-content {
width: 100%;
display: flex;
flex-direction: column;
gap: 6px;
}
.ks-scenario-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: -17px;
}
.port-out {
margin-left: 8px;
margin-right: 5px;
magnet: active;
box-shadow: none;
width: 13px;
height: 13px;
display: block;
border: 2px solid #5da1df;
background:#5da1df;
position: absolute;
right: -17px;
}
// 节点文本样式
.ks-scenario-node-name {
flex: 1;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
}
}
</style>