init
This commit is contained in:
184
src/views/knowledge/aiFlow/NodeContextMenu.vue
Normal file
184
src/views/knowledge/aiFlow/NodeContextMenu.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div v-if="visible" class="context-menu" :style="menuStyle">
|
||||
<div v-if="!canAddRight && addPosition === 'right'" class="menu-disabled-item">
|
||||
<el-icon class="menu-icon"><WarningFilled /></el-icon>
|
||||
该节点已有子节点
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="menu-item" v-for="item in nodeTypes" :key="item.type" @click="handleAddNode(item.type)">
|
||||
<svg-icon :size="24" :class="['menu-icon', 'node-icon', 'node-icon--' + item.type]" :name="`local-${item.type}`" />
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nodeTypes } from './nodes/nodeTypes.ts';
|
||||
import { WarningFilled } from '@element-plus/icons-vue';
|
||||
|
||||
export default {
|
||||
name: 'NodeContextMenu',
|
||||
components: {
|
||||
WarningFilled,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
position: {
|
||||
type: Object,
|
||||
default: () => ({ x: 0, y: 0 }),
|
||||
},
|
||||
node: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
addPosition: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
portIndex: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
inject: ['parent'],
|
||||
computed: {
|
||||
menuStyle() {
|
||||
return {
|
||||
left: `${this.position.x}px`,
|
||||
top: `${this.position.y}px`,
|
||||
};
|
||||
},
|
||||
canAddNode() {
|
||||
return this.addPosition && this.node;
|
||||
},
|
||||
// 检查是否可以向右添加节点
|
||||
canAddRight() {
|
||||
if (this.addPosition !== 'right' || !this.node || !this.parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取当前节点已有的连接数量
|
||||
const existingConnections = this.parent.connections?.filter((conn) => conn.sourceId === this.node.id) || [];
|
||||
|
||||
// 如果不是分支节点且已经有连接,则不能向右添加
|
||||
if (!['switch', 'question'].includes(this.node.type) && existingConnections.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
nodeTypes() {
|
||||
// 如果不能向右添加,返回空数组
|
||||
if (!this.canAddRight) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let types = nodeTypes.map((config) => ({
|
||||
type: config.type,
|
||||
name: config.name,
|
||||
}));
|
||||
|
||||
if (this.addPosition === 'replace') {
|
||||
types = types.filter((node) => node.type !== 'start' && node.type !== this.node?.type);
|
||||
} else {
|
||||
types = types.filter((node) => node.type !== 'start');
|
||||
}
|
||||
|
||||
return types;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleAddNode(type) {
|
||||
this.$emit('add', type);
|
||||
this.$emit('update:visible', false);
|
||||
},
|
||||
handleClickOutside(e) {
|
||||
if (!this.$el.contains(e.target)) {
|
||||
this.$emit('update:visible', false);
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (!val) {
|
||||
this.$emit('update:visible', false);
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 点击外部关闭菜单
|
||||
document.addEventListener('click', this.handleClickOutside);
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('click', this.handleClickOutside);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.context-menu {
|
||||
opacity: 0.9;
|
||||
position: fixed;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
padding: 6px 0;
|
||||
min-width: 180px;
|
||||
z-index: 1000;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
animation: menuFadeIn 0.15s ease-out;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
color: rgb(71 84 103 / 1);
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: #f8fafc;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.menu-disabled-item {
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #9ca3af;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
padding: 3px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.menu-divider {
|
||||
height: 1px;
|
||||
background-color: #e5e7eb;
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
@keyframes menuFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user