2025-10-29 10:22:14 +08:00
|
|
|
|
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 [
|
2025-11-03 09:44:14 +08:00
|
|
|
|
[0, '#FF6161'],
|
|
|
|
|
|
[1, '#D76976'],
|
2025-10-29 10:22:14 +08:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
2025-11-03 09:44:14 +08:00
|
|
|
|
[0, '#0093EB'],
|
|
|
|
|
|
[1, '#00D2D1'],
|
2025-10-29 10:22:14 +08:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_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 },
|
|
|
|
|
|
},
|
2025-11-03 09:44:14 +08:00
|
|
|
|
cssClass: `jtk-connector-${config.type}`
|
2025-10-29 10:22:14 +08:00
|
|
|
|
} as unknown as ConnectParams<unknown>)
|
|
|
|
|
|
|
|
|
|
|
|
// 为源元素添加端点
|
|
|
|
|
|
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)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|