Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 修复 extension/ vue-node-registry相关 bug,优化文档加载速度 #1788

Merged
merged 4 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/vue3-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
"@logicflow/engine": "workspace:*",
"@logicflow/extension": "workspace:*",
"@logicflow/vue-node-registry": "workspace:*",
"autoprefixer": "^10.4.20",
"echarts": "^5.5.1",
"element-plus": "^2.0.4",
"postcss": "^8.4.41",
"tailwindcss": "latest",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
Expand Down
6 changes: 6 additions & 0 deletions examples/vue3-app/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}
22 changes: 16 additions & 6 deletions examples/vue3-app/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { HomeFilled, TrendCharts } from '@element-plus/icons-vue'
import { HomeFilled, TrendCharts, Stopwatch } from '@element-plus/icons-vue'
import { RouterView } from 'vue-router'
</script>

Expand All @@ -9,19 +9,29 @@ import { RouterView } from 'vue-router'
<div class="wrapper">
<el-menu router class="el-menu-vertical-demo">
<el-menu-item index="/">
<el-icon><home-filled /></el-icon>
<el-icon><HomeFilled /></el-icon>
<span>Home</span>
</el-menu-item>

<el-menu-item index="/logicflow">
<el-icon><trend-charts /></el-icon>
<el-icon><TrendCharts /></el-icon>
<span>LogicFlow</span>
</el-menu-item>

<el-menu-item index="/performance">
<el-icon><trend-charts /></el-icon>
<el-icon><Stopwatch /></el-icon>
<span>performacne</span>
</el-menu-item>

<el-menu-item index="/keep-alive-and-teleport">
<el-icon><TrendCharts /></el-icon>
<span>KeepAlive</span>
</el-menu-item>

<el-menu-item index="/lf-chart">
<el-icon><TrendCharts /></el-icon>
<span>LFChartView</span>
</el-menu-item>
</el-menu>
</div>
</header>
Expand All @@ -32,15 +42,15 @@ import { RouterView } from 'vue-router'

<style scoped>
header {
width: 180px;
width: 280px;
position: relative;
left: 0;
line-height: 1.5;
max-height: 100vh;
}
.content {
position: relative;
width: calc(100% - 180px);
width: calc(100% - 340px);
height: 100%;
}

Expand Down
4 changes: 4 additions & 0 deletions examples/vue3-app/src/assets/main.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@import './base.css';

@tailwind base;
@tailwind components;
@tailwind utilities;

#app {
width: 100%;
display: flex;
Expand Down
13 changes: 13 additions & 0 deletions examples/vue3-app/src/components/chart/confg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const size = {
width: window.innerWidth,
height: window.innerHeight,
analysisNodeHeight: 100,
defaultNodeWidth: 300,
defaultNodeHeight: 40
}

export const gap = {
margin: 10,
padding: 20,
nodeGap: 40
}
129 changes: 129 additions & 0 deletions examples/vue3-app/src/components/chart/graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { IExtension, INode, IEdge } from './types.d'
// import { v4 as uuidv4 } from 'uuid'
import { size, gap } from './confg'

interface IMiddleStartYMap {
[key: string]: number
}

interface IMiddleTargetNodes {
[curNodeId: string]: {
sourceX: number
}
}

export default class Graph {
static pluginName = 'graph'

lf

constructor({ lf }: IExtension) {
this.lf = lf
}

init() {
const nodes = this.lf.graphModel.graphData.nodes
const edges = this.lf.graphModel.graphData.edges
const firstNodes = nodes.filter((node: INode) => node.properties.type === 'first')
const middleNodes = nodes.filter((node: INode) => node.properties.type === 'middle')
const landingNodes = nodes.filter((node: INode) => node.properties.type === 'landing')
const nodesData: INode[] = []
const startX = gap.margin + gap.padding
const firstStartY = gap.margin + gap.padding
let firstGroupHeight = 0
const middleStartYMap: IMiddleStartYMap = {}

const middleNodeIds = middleNodes.map((node: INode) => node.id)
const middleTargetNodes: IMiddleTargetNodes = {}

// 首跳节点
for (let i = 0; i < firstNodes.length; i++) {
const node = firstNodes[i]
const nodeHeight = this.getNodeHeight(node.type)

node.x = startX + (size.defaultNodeWidth + gap.nodeGap) * i + size.defaultNodeWidth * 0.5
node.y = firstStartY + nodeHeight * 0.5
nodesData.push(node)
firstGroupHeight = Math.max(firstGroupHeight, gap.padding * 2 + nodeHeight)

// 首跳的下一个中间跳节点
const targetNodeId = edges.find((edge: IEdge) => edge.sourceNodeId === node.id).targetNodeId
if (middleNodeIds.includes(targetNodeId)) {
if (!middleTargetNodes[targetNodeId]) {
middleTargetNodes[targetNodeId] = {
sourceX: node.x
}
}
}
}

// 中间跳节点
middleStartYMap[0] = gap.margin * 2 + gap.padding + firstGroupHeight

const createMddleNodes = (middleTargetNodes: IMiddleTargetNodes, yIndex: number) => {
const nextMiddleTargetNodes: IMiddleTargetNodes = {}
const nodeIds = Object.keys(middleTargetNodes)
const nodeHeights: number[] = []
for (const curNodeId in middleTargetNodes) {
const curNodeValue = middleTargetNodes[curNodeId]
const curNode = middleNodes.find((node: INode) => node.id === curNodeId)
if (curNode) {
const curNodeHeight = this.getNodeHeight(curNode.type)
curNode.x = curNodeValue.sourceX
curNode.y = middleStartYMap[yIndex] + curNodeHeight * 0.5
nodesData.push(curNode)
nodeHeights.push(curNodeHeight)

const targetNodeId = edges.find(
(edge: IEdge) => edge.sourceNodeId === curNode.id
).targetNodeId
if (middleNodeIds.includes(targetNodeId)) {
if (
!nextMiddleTargetNodes[targetNodeId] &&
nodesData.findIndex((node) => node.id === targetNodeId) < 0 &&
!nodeIds.includes(targetNodeId)
) {
nextMiddleTargetNodes[targetNodeId] = {
sourceX: curNode.x
}
}
}
}
}
if (Object.keys(nextMiddleTargetNodes).length) {
yIndex++
middleStartYMap[yIndex] =
middleStartYMap[yIndex - 1] + Math.max(...nodeHeights) + gap.padding
createMddleNodes(nextMiddleTargetNodes, yIndex)
}
}

createMddleNodes(middleTargetNodes, 0)

this.lf.render({
nodes: nodesData,
edges: []
})
}

getNodeHeight(type: string) {
return type === 'analysisNode' ? size.analysisNodeHeight : size.defaultNodeHeight
}

sortByGroup(nodes: INode[]) {
const sortObj = {
first: 0,
middle: 1,
landing: 2
}
nodes.sort((a, b) => {
const index1 = sortObj[a.properties!.type as keyof typeof sortObj]
const index2 = sortObj[b.properties!.type as keyof typeof sortObj]
return index1 - index2
})
}

layout() {
// console.log(this.lf.graphModel)
}
}
39 changes: 39 additions & 0 deletions examples/vue3-app/src/components/chart/linkChart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import LogicFlow from '@logicflow/core'
import { Group } from '@logicflow/extension'
import NodeExtension from './nodes'
import Graph from './graph'
import type { IGraphData } from './types.d'

interface IOptions {
container: HTMLDivElement
graphData: IGraphData
}

export default class LinkChart {
lf: LogicFlow

constructor(options: IOptions) {
this.lf = new LogicFlow({
container: options.container,
snapline: false,
grid: {
visible: true,
type: 'mesh',
size: 10,
config: {
color: '#eee'
}
},
plugins: [NodeExtension, Group, Graph]
})
this.lf.graphModel.graphData = options.graphData
const g = this.lf.extension.graph as any
g.init()

// this.lf.render()
}

static create(options: IOptions) {
return new LinkChart(options)
}
}
49 changes: 49 additions & 0 deletions examples/vue3-app/src/components/chart/nodes/AnalysisNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { HtmlNode, HtmlNodeModel } from '@logicflow/core'
import { createApp, h } from 'vue'
import type { INodeProps } from '../types.d'
import { size } from '../confg'
import AnalysisNode from './AnalysisNode'

class AnalysisNodeView extends HtmlNode {
isMounted
r
app

constructor(props: INodeProps) {
super(props)
this.isMounted = false
this.r = h(AnalysisNode, {
properties: props.model.getProperties()
})
this.app = createApp({
render: () => this.r
})
}

setHtml(rootEl: SVGForeignObjectElement) {
if (!this.isMounted) {
this.isMounted = true
const node = document.createElement('div')
node.className = 'w-full h-full'
rootEl.appendChild(node)
this.app.mount(node)
} else {
this.r.component!.props.properties = this.props.model.getProperties()
}
}
}

class AnalysisNodeModel extends HtmlNodeModel {
initNodeData(data: any) {
super.initNodeData(data)
this.width = size.defaultNodeWidth
this.height = size.analysisNodeHeight
this.text.editable = false
}
}

export default {
type: 'analysisNode',
view: AnalysisNodeView,
model: AnalysisNodeModel
}
18 changes: 18 additions & 0 deletions examples/vue3-app/src/components/chart/nodes/AnalysisNode.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<div class="w-full h-full px-4 py-2 leading-6 bg-[#e2f0ff]">
<p>{{ properties.url }}</p>
<p>{{ properties.type }}</p>
<p>{{ properties.category }}</p>
</div>
</template>

<script setup>
import { inject } from 'vue'
import { size } from '../confg'

const getNode = inject('getNode')
const nodeModel = getNode()
nodeModel.width = size.defaultNodeWidth
nodeModel.height = size.analysisNodeHeight
const properties = nodeModel.properties
</script>
49 changes: 49 additions & 0 deletions examples/vue3-app/src/components/chart/nodes/DefaultNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { HtmlNode, HtmlNodeModel } from '@logicflow/core'
import { createApp, h } from 'vue'
import type { INodeProps } from '../types.d'
import { size } from '../confg'
import DefaultNode from './DefaultNode.vue'

class DefaultNodeView extends HtmlNode {
isMounted
r
app

constructor(props: INodeProps) {
super(props)
this.isMounted = false
this.r = h(DefaultNode, {
properties: props.model.getProperties()
})
this.app = createApp({
render: () => this.r
})
}

setHtml(rootEl: SVGForeignObjectElement) {
if (!this.isMounted) {
this.isMounted = true
const node = document.createElement('div')
node.className = 'w-full h-full'
rootEl.appendChild(node)
this.app.mount(node)
} else {
this.r.component!.props.properties = this.props.model.getProperties()
}
}
}

class DefaultNodeModel extends HtmlNodeModel {
initNodeData(data: any) {
super.initNodeData(data)
this.width = size.defaultNodeWidth
this.height = size.defaultNodeHeight
this.text.editable = false
}
}

export default {
type: 'defaultNode',
view: DefaultNodeView,
model: DefaultNodeModel
}
Loading