import { Dom, NodeView } from '@antv/x6' // 导入 X6 的视图基类 import Vue from 'vue' // 直接使用 Vue2 import { shapeMaps } from './registry' import { VueShape } from './node' import { isActive, connect, disconnect, teleportComponents, getTeleportContainer, } from './teleport' export class VueShapeView extends NodeView { private vm: any getComponentContainer() { return this.selectors && (this.selectors.foContent as HTMLDivElement) } confirmUpdate(flag: number) { const ret = super.confirmUpdate(flag) return this.handleAction(ret, VueShapeView.action, () => { this.renderVueComponent() }) } protected targetId() { return `${this.graph.view.cid}:${this.cell.id}` } protected renderVueComponent() { this.unmountVueComponent() const root = this.getComponentContainer() const targetId = this.targetId() const node = this.cell const graph = this.graph // 1. 从 shapeMaps 获取用户注册的原始组件 const { component: rawComponent } = shapeMaps[node.shape] || {} if (!rawComponent) return if (isActive()) { if (root) { // 2. 调用 connect 生成包含 Teleport 的动态组件(存储到 teleportComponents) connect(targetId, rawComponent, root, node, graph) // 3. 从 teleportComponents 获取动态组件并渲染(而非直接使用原始组件) const component = teleportComponents.get(targetId)! // 获取动态组件 // 4. 使用 Vue2 渲染 Teleport 组件 const teleportTarget = getTeleportContainer() as HTMLElement this.vm = new Vue({ el: teleportTarget, ...component, }) } } else { // 没有启用 teleport 的时候,直接挂载到图的节点上 this.vm = new Vue({ el: root, render(h: any) { return h(rawComponent, { props: { node, graph } }) }, provide() { return { getNode: () => node, getGraph: () => graph, } }, }) } } protected unmountVueComponent() { const root = this.getComponentContainer() if (this.vm) { this.vm.$destroy() this.vm = null } if (root) { root.innerHTML = '' } return root } onMouseDown(e: Dom.MouseDownEvent, x: number, y: number) { const target = e.target as Element const tagName = target.tagName.toLowerCase() if (tagName === 'input') { const type = target.getAttribute('type') if ( type == null || [ 'text', 'password', 'number', 'email', 'search', 'tel', 'url', ].includes(type) ) { return } } super.onMouseDown(e, x, y) } unmount() { if (isActive()) { disconnect(this.targetId()) } this.unmountVueComponent() super.unmount() return this } } export namespace VueShapeView { export const action = 'vue' as any VueShapeView.config({ bootstrap: [action], actions: { component: action, }, }) NodeView.registry.register('vue-shape-view', VueShapeView, true) }