import Components from '@/components'
import ElementPlus from 'element-plus'
import * as ElementPlusIcons from '@element-plus/icons-vue'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { HtmlResize } from '@logicflow/extension'
import { h as lh } from '@logicflow/core'
import { createApp, h } from 'vue'
import directives from '@/directives'
import i18n from '@/locales'
import { WorkflowType } from '@/enums/workflow'
import { nodeDict } from '@/workflow/common/data'
class AppNode extends HtmlResize.view {
isMounted
r
app
constructor(props: any, VueNode: any) {
super(props)
this.isMounted = false
this.r = h(VueNode, {
properties: props.model.properties,
nodeModel: props.model
})
this.app = createApp({
render: () => this.r
})
this.app.use(ElementPlus, {
locale: zhCn
})
this.app.use(Components)
this.app.use(directives)
this.app.use(i18n)
for (const [key, component] of Object.entries(ElementPlusIcons)) {
this.app.component(key, component)
}
if (props.model.properties.noRender) {
delete props.model.properties.noRender
} else {
const filterNodes = props.graphModel.nodes.filter((v: any) => v.type === props.model.type)
if (filterNodes.length - 1 > 0) {
getNodesName(filterNodes.length - 1)
}
}
function getNodesName(num: number) {
let number = num
const name = props.model.properties.stepName + number
if (!props.graphModel.nodes?.some((node: any) => node.properties.stepName === name.trim())) {
props.model.properties.stepName = name
} else {
number += 1
getNodesName(number)
}
}
props.model.properties.config = nodeDict[props.model.type].properties.config
if (props.model.properties.height) {
props.model.height = props.model.properties.height
}
}
getAnchorShape(anchorData: any) {
const { x, y, type } = anchorData
let isConnect = false
if (type == 'left') {
isConnect = this.props.graphModel.edges.some((edge) => edge.targetAnchorId == anchorData.id)
} else {
isConnect = this.props.graphModel.edges.some((edge) => edge.sourceAnchorId == anchorData.id)
}
return lh(
'foreignObject',
{
...anchorData,
x: x - 10,
y: y - 12,
width: 30,
height: 30
},
[
lh('div', {
style: { zindex: 0 },
onClick: () => {
if (type == 'right') {
this.props.model.openNodeMenu(anchorData)
}
},
dangerouslySetInnerHTML: {
__html: isConnect
? `
`
: ``
}
})
]
)
}
setHtml(rootEl: HTMLElement) {
if (!this.isMounted) {
this.isMounted = true
const node = document.createElement('div')
rootEl.appendChild(node)
this.app?.mount(node)
} else {
if (this.r && this.r.component) {
this.r.component.props.properties = this.props.model.getProperties()
}
}
}
}
class AppNodeModel extends HtmlResize.model {
refreshDeges() {
// 更新节点连接边的path
this.incoming.edges.forEach((edge: any) => {
// 调用自定义的更新方案
edge.updatePathByAnchor()
})
this.outgoing.edges.forEach((edge: any) => {
edge.updatePathByAnchor()
})
}
set_position(position: { x?: number; y?: number }) {
const { x, y } = position
if (x) {
this.x = x
}
if (y) {
this.y = y
}
this.refreshDeges()
}
getResizeOutlineStyle() {
const style = super.getResizeOutlineStyle()
style.stroke = 'none'
return style
}
getControlPointStyle() {
const style = super.getControlPointStyle()
style.stroke = 'none'
style.fill = 'none'
return style
}
getNodeStyle() {
return {
overflow: 'visible'
}
}
getOutlineStyle() {
const style = super.getOutlineStyle()
style.stroke = 'none'
if (style.hover) {
style.hover.stroke = 'none'
}
return style
}
// 如果不用修改锚地形状,可以重写颜色相关样式
getAnchorStyle(anchorInfo: any) {
const style = super.getAnchorStyle(anchorInfo)
if (anchorInfo.type === 'left') {
style.fill = 'red'
style.hover.fill = 'transparent'
style.hover.stroke = 'transpanrent'
style.className = 'lf-hide-default'
} else {
style.fill = 'green'
}
return style
}
setHeight(height: number) {
const sourceHeight = this.height
const targetHeight = height + 100
this.height = targetHeight
this.properties['height'] = targetHeight
this.move(0, (targetHeight - sourceHeight) / 2)
this.outgoing.edges.forEach((edge: any) => {
// 调用自定义的更新方案
edge.updatePathByAnchor()
})
this.incoming.edges.forEach((edge: any) => {
// 调用自定义的更新方案
edge.updatePathByAnchor()
})
}
get_width() {
return this.properties?.width || 340
}
setAttributes() {
this.width = this.get_width()
const isLoop = (node_id: string, target_node_id: string) => {
const up_node_list = this.graphModel.getNodeIncomingNode(node_id)
for (const index in up_node_list) {
const item = up_node_list[index]
if (item.id === target_node_id) {
return true
} else {
const result = isLoop(item.id, target_node_id)
if (result) {
return true
}
}
}
return false
}
const circleOnlyAsTarget = {
message: '只允许从右边的锚点连出',
validate: (sourceNode: any, targetNode: any, sourceAnchor: any) => {
return sourceAnchor.type === 'right'
}
}
this.sourceRules.push({
message: '不可循环连线',
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
return !isLoop(sourceNode.id, targetNode.id)
}
})
this.sourceRules.push(circleOnlyAsTarget)
this.targetRules.push({
message: '只允许连接左边的锚点',
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
return targetAnchor.type === 'left'
}
})
}
getDefaultAnchor() {
const { id, x, y, width } = this
const showNode = this.properties.showNode === undefined ? true : this.properties.showNode
const anchors: any = []
if (this.type !== WorkflowType.Base) {
if (this.type !== WorkflowType.Start) {
anchors.push({
x: x - width / 2 + 10,
y: showNode ? y : y - 15,
id: `${id}_left`,
edgeAddable: false,
type: 'left'
})
}
anchors.push({
x: x + width / 2 - 10,
y: showNode ? y : y - 15,
id: `${id}_right`,
type: 'right'
})
}
return anchors
}
}
export { AppNodeModel, AppNode }