部分源代码:https://gitee.com/eadela/x6-example.git
channelReferral.vue 节点组件
<template>
<!-- 多波次 -->
<div class="node-box">
<Icon type="cancel-questionnaire" class="icon" />
<p> 渠道转介 </p>
<div class="child">
<Button type="text" @click="edit">
编辑
</Button>
<Button type="text" @click="del" class="m-l-1">
删除
</Button>
</div>
</div>
</template>
<script lang="ts" setup>
/*eslint-disable */
import { Icon, Button } from '@bairong/uxd-ui'
import { defineProps } from 'vue'
const props = defineProps({
icon: {
type: String
// required: true
},
text: {
type: String
// required: true
}
})
defineExpose({
...props
})
const edit = () => {
console.log(123123)
}
const del = () => {
console.log(456456)
}
/*eslint-disable */
</script>
<style scoped lang="less">
.child {
width: 150px;
min-height: 100px;
background: #fff;
position: absolute;
left: 60px;
top: 0;
display: none;
}
.node-box {
width: 64px;
height: 64px;
border: 1px solid #136fff;
border-radius: 5px;
background: #fff;
text-align: center;
padding-top: 6px;
position: relative;
&:hover {
background: #136fff;
color: #fff;
.child {
// display: block;
color: #000;
}
.icon {
color: #fff;
}
}
.icon {
font-size: 22px;
color: #136fff;
}
}
</style>
常量
// 所有节点组
export const ShapeComs = {
HEEL_NODE: 'HeelNode', // 客群筛选
SEC_SCREENING: 'SecScreening', // 二次筛选
SEC_CREATE: 'SecCreate', // 二次筛选裂变产生 -- 特殊节点
AB_TEST: 'AbTest', // AB测试
AB_CREATE: 'AbCreate', // AB测试裂变产生 -- 特殊节点
EXTERNAL_DATA: 'ExternalData', // 外部数据
FINANCIAL_MANAGER: 'FinancialManager', // 理财经理
MANUAL_OUTBOUND_CALL: 'ManualOutboundCall', // 人工外呼
SHORT_MESSAGE: 'ShortMessage', // 短信
PHONE_APP: 'PhoneApp', // 手机APP
OFFICIAL_ACCOUNT: 'OfficialAccount', // 公众号
BRAIN_POWER: 'BrainPower', // 智能外呼
CHANNEL_REFERRAL: 'ChannelReferral', // 渠道转介
DELAY_NODE: 'DelayNode', // 延时节点
EQUITY_RESOURCES: 'EquityResources', // 权益资源
END_NODE: 'EndNode', // 结束
PARENT_NODE: 'ParentNode' // 父节点
}
注册组件,注册节点
import { Graph, Edge } from '@antv/x6'
import { VueShape } from '@antv/x6-vue-shape'
import HeelNode from '../shape/heelNode.vue'
import SecScreening from '../shape/secScreening.vue'
import AbTest from '../shape/abTest.vue'
import ExternalData from '../shape/externalData.vue'
import FinancialManager from '../shape/financialManager.vue'
import ManualOutboundCall from '../shape/manualOutboundCall.vue'
import ShortMessage from '../shape/shortMessage.vue'
import PhoneApp from '../shape/phoneApp.vue'
import OfficialAccount from '../shape/officialAccount.vue'
import BrainPower from '../shape/brainPower.vue'
import ChannelReferral from '../shape/channelReferral.vue'
import DelayNode from '../shape/delayNode.vue'
import EndNode from '../shape/endNode.vue'
// 所有vue组件名称
import { ShapeComs } from './shapeConst'
// 注册vue组件到x6中
const registerVueComponent = (comNm: string) => {
Graph.registerVueComponent(
comNm,
{
template: `<${comNm} />`,
components: {
HeelNode,
SecScreening,
AbTest,
ExternalData,
FinancialManager,
ManualOutboundCall,
ShortMessage,
PhoneApp,
OfficialAccount,
BrainPower,
ChannelReferral,
DelayNode,
EndNode,
},
},
true,
)
}
// 注册vue组件到x6中
Object.values(ShapeComs).map(comNm => registerVueComponent(comNm))
// 定义节点
// 第一步继承VueShape
export class MyShape extends VueShape {
getInPorts() {
return this.getPortsByGroup('in')
}
getOutPorts() {
return this.getPortsByGroup('out')
}
getUsedInPorts(graph: Graph) {
console.log(graph)
// const incomingEdges = graph.getIncomingEdges(this) || []
return [].map((edge: Edge) => {
const portId = edge.getTargetPortId()
return this.getPort(portId!)
})
}
// eslint-disable-next-line class-methods-use-this
getNewInPorts(length: number) {
return Array.from(
{
length,
},
() => ({
group: 'in' // , id: `${this.data.comType}-out-${Date.now()}`
}),
)
}
// eslint-disable-next-line class-methods-use-this
getNewOutPorts(length: number) {
return Array.from(
{
length,
},
() => ({
group: 'out', // , id: `${this.data.comType}-out-${Date.now()}`
}),
)
}
updateInPorts(graph: Graph) {
const minNumberOfPorts = 2
// const ports = this.getInPorts()
// const usedPorts = this.getUsedInPorts(graph)
const newInPorts = this.getNewInPorts(1)
const newOutPorts = this.getNewOutPorts(1)
// debugger
if (this.id.includes('HeelNode')) { // 起始节点
this.addPorts(newOutPorts)
} else if (this.id.includes('EndNode')) { // 结束节点
this.addPorts(newInPorts)
} else { // 其他节点
this.addPorts(newOutPorts)
this.addPorts(newInPorts)
}
// if (ports.length === minNumberOfPorts && ports.length - usedPorts.length > 0) {
// // noop
// } else if (ports.length === usedPorts.length) {
// this.addPorts(newPorts)
// } else if (ports.length + 1 > usedPorts.length) {
// this.prop(
// ['ports', 'items'],
// this.getOutPorts()
// .concat(usedPorts)
// .concat(newPorts),
// {
// rewrite: true,
// },
// )
// }
return this
}
}
// 第二步:配置
MyShape.config({
attrs: {
// root: {
// magnet: false,
// },
body: {
fill: '#f5f5f5',
stroke: '#d9d9d9',
strokeWidth: 1,
},
},
ports: {
// items: [
// {
// group: 'out',
// },
// ],
groups: {
in: {
position: {
name: 'top',
},
attrs: {
portBody: {
magnet: 'passive',
r: 6,
stroke: '#ffa940',
fill: '#fff',
strokeWidth: 2,
},
},
},
out: {
position: {
name: 'bottom',
},
attrs: {
portBody: {
magnet: true,
r: 8,
fill: '#fff',
stroke: '#3199FF',
strokeWidth: 2,
},
},
},
},
},
portMarkup: [
{
tagName: 'circle',
selector: 'portBody',
},
],
// component: {
// template: `<Abtext2 />`,
// components: {
// Abtext2,
// },
// },
})
// 第三步:注册
Graph.registerNode('my-shape', MyShape)
// Graph.registerEdge('my-edge', {
// inherit: 'edge',
// attrs: {
// line: {
// stroke: '#a0a0a0',
// strokeWidth: 1,
// targetMarker: {
// name: 'classic',
// size: 7,
// },
// },
// },
// })
画布添加vue节点
在这里插入代码片
<!--
* VUE节点
-->
<template>
<div id="containered" ref="containered" />
</template>
<script lang="ts" setup>
/*eslint-disable */
import { ref, computed, onMounted } from 'vue'
import { Graph, Shape } from '@antv/x6';
import './ts/register';
import ChannelReferral from "./shape/channelReferral.vue";
const graph = ref<any>({}) // 流程图
const containered = ref<HTMLElement>() // 流程图挂载dom节点
const init = () => {
graph.value = new Graph({
container: containered.value,
width: 800,
height: 600,
background: {
color: '#fffbe6', // 设置画布背景颜色
},
grid: {
size: 10, // 网格大小 10px
visible: true, // 渲染网格背景
},
snapline: { // 对齐线
enabled: true,
sharp: true, // 是否显示截断的对齐线-对齐线变短
},
selecting: { // 点选/框选,默认禁用。
enabled: true,
// rubberband: true, // 框选
// 是否显示节点的选择框,默认为 false,建议使用下面的样式定制方法去定制自己的选择框样式。
// showNodeSelectionBox: true // 节点的选择框
},
keyboard: { // 键盘事件可用于绑定快捷键
enabled: true
},
// 开启撤销/重做
history: {
enabled: true,
ignoreChange: true
},
// 剪切板用于复制/粘贴节点和边,并支持跨画布的复制/粘贴,创建画布时通过以下配置启用。
clipboard: {
enabled: true
// useLocalStorage: true // 保存到 localStorage
},
// panning: true, // 普通画布(未开启 scroller 模式)通过开启 panning 选项来支持拖拽平移。
// 使画布具备滚动、平移、居中、缩放等能力
scroller: {
enabled: true,
pageVisible: false, // 是否分页,默认为 false。
pageBreak: false, // 是否显示分页符,默认为 false。
pannable: true // 启用画布平移
},
mousewheel: { // 鼠标滚轮缩放
enabled: true,
// 是否为全局事件,设置为 true 时滚轮事件绑定在 document 上,否则绑定在画布容器上。默认为 false。
global: true,
modifiers: ['ctrl', 'meta']
},
highlighting: {
// 连线过程中,节点可以被链接时被使用
// nodeAvailable: {},
// 拖动节点进行嵌入操作过程中,节点可以被嵌入时被使用
embedding: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#47C769',
},
},
},
// 连线过程中,链接桩可以被链接时被使用
magnetAvailable: { // 高亮
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#47C769',
},
},
},
// 连线过程中,自动吸附到链接桩时被使用
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#31d0c6',
},
},
},
},
});
graph.value.addNode({
shape: 'my-shape',
width: 64,
height: 64,
x: 40,
y: 40,
component: {
template: `<channel-referral :text="text" />`,
data() {
return {
text:'AB测试',
}
},
components: {
ChannelReferral,
},
},
})
}
onMounted(() => {
init()
})
</script>