import type { AnchorSpec, ConnectorSpec, ConnectParams, EndpointOptions, JsPlumbInstance, } from '@jsplumb/browser-ui' import { BezierConnector, DotEndpoint, newInstance } from '@jsplumb/browser-ui' export interface JsplumbConfig { connector?: ConnectorSpec type?: 'input' | 'output' stops?: [[number, string], [number, string]] // 连接线条是否变透明一些 transparent?: boolean } export interface ConnectArg { sourceId: string targetId: string anchor: AnchorSpec config?: JsplumbConfig } const defaultConfig: JsplumbConfig = { connector: { type: BezierConnector.type, options: { curviness: 70, stub: 10, }, }, type: 'input', } export class Jsplumb { instance!: JsPlumbInstance containerId: string config: JsplumbConfig constructor(eleId: string, config = {} as JsplumbConfig) { this.containerId = eleId this.config = { ...defaultConfig, ...config } onMounted(() => { this.init() }) } init = () => { if (this.instance) { return } this.instance = newInstance({ container: document.querySelector(`#${this.containerId}`)!, // 或指定共同的父容器 }) } getStops = (type?: 'input' | 'output'): [[number, string], [number, string]] => { if (type === 'input') { return [ [0, '#FF6161'], [1, '#D76976'], ] } return [ [0, '#0093EB'], [1, '#00D2D1'], ] } _connect = ( sourceId: string, targetId: string, anchor: AnchorSpec, _config = {} as JsplumbConfig, ) => { const config = { ...defaultConfig, ...this.config, ..._config, } this.init() // 连接两个元素 const sourceElement = document.querySelector(`#${sourceId}`) const targetElement = document.querySelector(`#${targetId}`) const stops = _config.stops ?? this.getStops(config.type) // 如果config.transparent为true,则将stops都加一些透明度 if (config.transparent) { stops[0][1] = stops[0][1] + '30' stops[1][1] = stops[1][1] + '30' } if (targetElement && sourceElement) { this.instance.connect({ source: sourceElement, target: targetElement, connector: config.connector, anchor: anchor, paintStyle: { stroke: stops[0][1], strokeWidth: 2.5, dashstyle: '0', zIndex: 100, opacity: 0.9, gradient: { stops: stops, type: 'linear', }, }, sourceEndpointStyle: { fill: stops[0][1] }, endpoint: { type: DotEndpoint.type, options: { radius: 5 }, }, cssClass: `jtk-connector-${config.type}` } as unknown as ConnectParams) // 为源元素添加端点 this.instance.addEndpoint(sourceElement, { anchor: (anchor as [AnchorSpec, AnchorSpec])[0], paintStyle: { fill: stops[0][1], zIndex: 100 }, // source端点颜色 } as unknown as EndpointOptions) // 为目标元素添加端点 this.instance.addEndpoint(targetElement, { anchor: (anchor as [AnchorSpec, AnchorSpec])[1], paintStyle: { fill: stops[1][1], zIndex: 100 }, // target端点颜色 } as unknown as EndpointOptions) } } connect = ( sourceId: string, targetId: string, anchor: AnchorSpec, config = {} as JsplumbConfig, ) => { this._connect(sourceId, targetId, anchor, config) } connects = (args: ConnectArg[]) => { this.instance.batch(() => { args.forEach(({ sourceId, targetId, anchor, config }) => { this._connect(sourceId, targetId, anchor, config) }) }) } repaintEverything = () => { // 重新验证元素位置 const container = document.querySelector(`#${this.containerId}`) if (container) { const elements = container.querySelectorAll('[id^="task-results-"]') elements.forEach((element) => { this.instance.revalidate(element) }) } this.instance.repaintEverything() } reset = () => { this.instance.deleteEveryConnection() const allEndpoints = this.instance.selectEndpoints() allEndpoints.each((endpoint) => { this.instance.deleteEndpoint(endpoint) }) } }