[Improvement-14318][UI] migrate version 2.x workflow definition d3 tree view to version 3.x (#14382)
* [Improvement][UI] migrate version 2.x workflow definition d3 tree view to version 3.x * remove unnessnary code * fix code smells * update lock file to fix front-end CI Build error * update package.json
This commit is contained in:
parent
babb8d9d85
commit
0880549440
|
|
@ -13,6 +13,7 @@
|
||||||
"@antv/x6": "^1.34.1",
|
"@antv/x6": "^1.34.1",
|
||||||
"@vueuse/core": "^9.2.0",
|
"@vueuse/core": "^9.2.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"d3": "7.8.5",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"date-fns-tz": "^1.3.7",
|
"date-fns-tz": "^1.3.7",
|
||||||
"echarts": "^5.3.3",
|
"echarts": "^5.3.3",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,37 @@
|
||||||
|
|
||||||
import Card from '@/components/card'
|
import Card from '@/components/card'
|
||||||
import { ArrowLeftOutlined } from '@vicons/antd'
|
import { ArrowLeftOutlined } from '@vicons/antd'
|
||||||
import { NButton, NFormItem, NIcon, NSelect, NSpace, NImage } from 'naive-ui'
|
import {
|
||||||
import { defineComponent, onMounted, Ref, ref, watch } from 'vue'
|
NButton,
|
||||||
|
NFormItem,
|
||||||
|
NIcon,
|
||||||
|
NSelect,
|
||||||
|
NSpace,
|
||||||
|
NImage,
|
||||||
|
NTooltip
|
||||||
|
} from 'naive-ui'
|
||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
onMounted,
|
||||||
|
Ref,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
h,
|
||||||
|
toRefs,
|
||||||
|
reactive,
|
||||||
|
getCurrentInstance
|
||||||
|
} from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import UseTree from '@/views/projects/workflow/definition/tree/use-tree'
|
import UseD3Tree from '@/views/projects/workflow/definition/tree/use-d3-tree'
|
||||||
|
import Tree from '@/views/projects/workflow/definition/tree/use-d3-tree/tree'
|
||||||
import { IChartDataItem } from '@/components/chart/modules/types'
|
import { IChartDataItem } from '@/components/chart/modules/types'
|
||||||
import { Router, useRouter } from 'vue-router'
|
import { Router, useRouter } from 'vue-router'
|
||||||
import { viewTree } from '@/service/modules/process-definition'
|
import { viewTree } from '@/service/modules/process-definition'
|
||||||
import { SelectMixedOption } from 'naive-ui/lib/select/src/interface'
|
import { SelectMixedOption } from 'naive-ui/lib/select/src/interface'
|
||||||
import { find } from 'lodash'
|
import { tasksState, uuid } from '@/common/common'
|
||||||
import { tasksState } from '@/common/common'
|
|
||||||
import type { ITaskTypeNodeOption } from './types'
|
import type { ITaskTypeNodeOption } from './types'
|
||||||
|
import { cloneDeep, map } from 'lodash'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'WorkflowDefinitionTree',
|
name: 'WorkflowDefinitionTree',
|
||||||
|
|
@ -146,6 +165,18 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const showTooltip = ref(false)
|
||||||
|
const tooltipText = ref('')
|
||||||
|
const tooltipProps = reactive({
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeTooltip = (options: any) => {
|
||||||
|
tooltipProps.x = options.x
|
||||||
|
tooltipProps.y = options.y - 20
|
||||||
|
}
|
||||||
|
|
||||||
const initTaskStateMap = () => {
|
const initTaskStateMap = () => {
|
||||||
taskStateMap.value = Object.entries(tasksState(t)).map(([key, item]) => ({
|
taskStateMap.value = Object.entries(tasksState(t)).map(([key, item]) => ({
|
||||||
state: key,
|
state: key,
|
||||||
|
|
@ -154,72 +185,39 @@ export default defineComponent({
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const initChartData = (node: any, newNode: any) => {
|
const currentInstance = getCurrentInstance()
|
||||||
newNode.children = []
|
|
||||||
node?.children.map((child: any) => {
|
|
||||||
const newChild = {}
|
|
||||||
initChartData(child, newChild)
|
|
||||||
newNode.children.push(newChild)
|
|
||||||
})
|
|
||||||
|
|
||||||
newNode.name = node.name
|
|
||||||
newNode.value = node.name === 'DAG' ? 'DAG' : node?.type
|
|
||||||
const taskTypeNodeOption = find(taskTypeNodeOptions.value, {
|
|
||||||
taskType: newNode.value
|
|
||||||
})
|
|
||||||
if (taskTypeNodeOption) {
|
|
||||||
newNode.itemStyle = { color: taskTypeNodeOption.color }
|
|
||||||
if (newNode.name !== 'DAG') {
|
|
||||||
let taskState = null
|
|
||||||
if (
|
|
||||||
node.instances &&
|
|
||||||
node.instances.length > 0 &&
|
|
||||||
node.instances[0].state
|
|
||||||
) {
|
|
||||||
taskState = find(taskStateMap.value, {
|
|
||||||
state: node.instances[0].state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newNode.label = {
|
|
||||||
show: true,
|
|
||||||
formatter: [
|
|
||||||
`{name|${t('project.task.task_name')}:${newNode.name}}`,
|
|
||||||
`{type|${t('project.task.task_type')}:${
|
|
||||||
taskTypeNodeOption.taskType
|
|
||||||
}}`,
|
|
||||||
taskState
|
|
||||||
? `{state|${t('project.workflow.task_state')}: ${
|
|
||||||
taskState.value
|
|
||||||
}}`
|
|
||||||
: ''
|
|
||||||
].join('\n'),
|
|
||||||
rich: {
|
|
||||||
type: {
|
|
||||||
lineHeight: 20,
|
|
||||||
align: 'left'
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
lineHeight: 20,
|
|
||||||
align: 'left'
|
|
||||||
},
|
|
||||||
state: {
|
|
||||||
lineHeight: 20,
|
|
||||||
align: 'left',
|
|
||||||
color: taskState ? taskState.color : 'black'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getWorkflowTreeData = async (limit: number) => {
|
const getWorkflowTreeData = async (limit: number) => {
|
||||||
if (projectCode.value && definitionCode) {
|
if (projectCode.value && definitionCode) {
|
||||||
|
Tree.reset()
|
||||||
|
|
||||||
const res = await viewTree(projectCode.value, definitionCode.value, {
|
const res = await viewTree(projectCode.value, definitionCode.value, {
|
||||||
limit: limit
|
limit: limit
|
||||||
})
|
})
|
||||||
chartData.value = [{ name: 'DAG', value: 'DAG' }]
|
|
||||||
initChartData(res, chartData.value[0])
|
const treeData = cloneDeep(res)
|
||||||
|
if (!treeData?.children) return
|
||||||
|
|
||||||
|
const recursiveChildren = (children: any) => {
|
||||||
|
if (children.length) {
|
||||||
|
map(children, (v) => {
|
||||||
|
v.uuid = `${uuid('uuid_')}${uuid('') + uuid('')}`
|
||||||
|
if (v.children.length) {
|
||||||
|
recursiveChildren(v.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recursiveChildren(treeData.children)
|
||||||
|
|
||||||
|
Tree.init({
|
||||||
|
data: cloneDeep(treeData),
|
||||||
|
limit: limit,
|
||||||
|
selfTree: currentInstance,
|
||||||
|
taskTypeNodeOptions: taskTypeNodeOptions.value,
|
||||||
|
tasksStateObj: tasksState(t)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,11 +247,15 @@ export default defineComponent({
|
||||||
chartData,
|
chartData,
|
||||||
options,
|
options,
|
||||||
onSelectChange,
|
onSelectChange,
|
||||||
taskTypeNodeOptions
|
taskTypeNodeOptions,
|
||||||
|
showTooltip,
|
||||||
|
tooltipText,
|
||||||
|
changeTooltip,
|
||||||
|
...toRefs(tooltipProps)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { chartData, options, onSelectChange, taskTypeNodeOptions } = this
|
const { options, onSelectChange, taskTypeNodeOptions } = this
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router: Router = useRouter()
|
const router: Router = useRouter()
|
||||||
|
|
||||||
|
|
@ -293,7 +295,24 @@ export default defineComponent({
|
||||||
</NButton>
|
</NButton>
|
||||||
))}
|
))}
|
||||||
</NSpace>
|
</NSpace>
|
||||||
<UseTree chartData={chartData} />
|
</Card>
|
||||||
|
{h(
|
||||||
|
NTooltip,
|
||||||
|
{
|
||||||
|
show: this.showTooltip,
|
||||||
|
placement: 'top',
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
duration: 10,
|
||||||
|
'show-arrow': false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => <div innerHTML={this.tooltipText}></div>,
|
||||||
|
trigger: () => ''
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
<Card>
|
||||||
|
<UseD3Tree />
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tree-model {
|
||||||
|
width: calc(100%);
|
||||||
|
height: calc(100vh - 300px);
|
||||||
|
overflow-x: scroll;
|
||||||
|
.d3-tree {
|
||||||
|
padding-left: 30px;
|
||||||
|
.node {
|
||||||
|
text {
|
||||||
|
font: 11px sans-serif;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rect {
|
||||||
|
cursor: pointer;
|
||||||
|
&.state {
|
||||||
|
stroke: #666;
|
||||||
|
shape-rendering: crispEdges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path {
|
||||||
|
&.link{
|
||||||
|
fill: none;
|
||||||
|
stroke: #666;
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
circle {
|
||||||
|
stroke: #666;
|
||||||
|
fill: #0097e0;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
const UseTree = defineComponent({
|
||||||
|
name: 'D3Tree',
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div class='tree-model'>
|
||||||
|
<div class='d3-tree'>
|
||||||
|
<svg class='tree-svg' width='100%'></svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default UseTree
|
||||||
|
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-nocheck
|
||||||
|
import * as d3 from 'd3'
|
||||||
|
import { rtInstancesTooltip, rtCountMethod } from './util'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
let self = this
|
||||||
|
|
||||||
|
const Tree = function () {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
self = this
|
||||||
|
this.selfTree = {}
|
||||||
|
this.tree = function () {}
|
||||||
|
// basic configuration
|
||||||
|
this.config = {
|
||||||
|
barHeight: 26,
|
||||||
|
axisHeight: 40,
|
||||||
|
squareSize: 10,
|
||||||
|
squarePading: 4,
|
||||||
|
taskNum: 25,
|
||||||
|
nodesMax: 0
|
||||||
|
}
|
||||||
|
// Margin configuration
|
||||||
|
this.config.margin = {
|
||||||
|
top: this.config.barHeight / 2 + this.config.axisHeight,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: this.config.barHeight / 2
|
||||||
|
}
|
||||||
|
// width
|
||||||
|
this.config.margin.width =
|
||||||
|
960 - this.config.margin.left - this.config.margin.right
|
||||||
|
// bar width
|
||||||
|
this.config.barWidth = parseInt(this.config.margin.width * 0.9)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init
|
||||||
|
*/
|
||||||
|
Tree.prototype.init = function ({
|
||||||
|
data,
|
||||||
|
limit,
|
||||||
|
selfTree,
|
||||||
|
taskTypeNodeOptions,
|
||||||
|
tasksStateObj
|
||||||
|
}) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.selfTree = selfTree
|
||||||
|
this.config.taskNum = limit
|
||||||
|
this.duration = 400
|
||||||
|
this.i = 0
|
||||||
|
this.tree = d3.tree().size([0, 46])
|
||||||
|
this.taskTypeNodeOptions = taskTypeNodeOptions
|
||||||
|
this.tasksStateObj = tasksStateObj
|
||||||
|
|
||||||
|
const root = d3.hierarchy(data)
|
||||||
|
const treeData = this.tree(root)
|
||||||
|
|
||||||
|
const tasks = treeData.descendants()
|
||||||
|
const links = treeData.links()
|
||||||
|
this.tasks = tasks
|
||||||
|
this.links = links
|
||||||
|
|
||||||
|
this.diagonal = d3
|
||||||
|
.linkHorizontal()
|
||||||
|
.x((d) => d.y)
|
||||||
|
.y((d) => d.x)
|
||||||
|
|
||||||
|
this.svg = d3
|
||||||
|
.select('.tree-svg')
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'level')
|
||||||
|
.attr(
|
||||||
|
'transform',
|
||||||
|
'translate(' +
|
||||||
|
this.config.margin.left +
|
||||||
|
',' +
|
||||||
|
this.config.margin.top +
|
||||||
|
')'
|
||||||
|
)
|
||||||
|
|
||||||
|
data.x0 = 0
|
||||||
|
data.y0 = 0
|
||||||
|
|
||||||
|
this.squareNum = tasks[tasks.length === 1 ? 0 : 1]?.data?.instances.length
|
||||||
|
|
||||||
|
// Calculate the maximum node length
|
||||||
|
this.config.nodesMax = rtCountMethod(data.children)
|
||||||
|
|
||||||
|
this.treeUpdate((this.root = data)).then(() => {
|
||||||
|
this.treeTooltip()
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tasks
|
||||||
|
*/
|
||||||
|
Tree.prototype.nodesClass = function (d) {
|
||||||
|
let sclass = 'node'
|
||||||
|
if (d.children === undefined && d._children === undefined) {
|
||||||
|
sclass += ' leaf'
|
||||||
|
} else {
|
||||||
|
sclass += ' parent'
|
||||||
|
if (d.children === undefined) {
|
||||||
|
sclass += ' collapsed'
|
||||||
|
} else {
|
||||||
|
sclass += ' expanded'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sclass
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tree Expand hidden
|
||||||
|
*/
|
||||||
|
Tree.prototype.treeToggles = function (e,clicked_d) { // eslint-disable-line
|
||||||
|
|
||||||
|
self.removeTooltip()
|
||||||
|
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
d3.selectAll("[task_id='" + clicked_d.data.uuid + "']").each((d) => {
|
||||||
|
if (clicked_d !== d && d.children) {
|
||||||
|
// eslint-disable-line
|
||||||
|
d._children = d.children
|
||||||
|
d.children = null
|
||||||
|
self.treeUpdate(d)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (clicked_d._children) {
|
||||||
|
clicked_d.children = clicked_d._children
|
||||||
|
clicked_d._children = null
|
||||||
|
} else {
|
||||||
|
clicked_d._children = clicked_d.children
|
||||||
|
clicked_d.children = null
|
||||||
|
}
|
||||||
|
self.treeUpdate(clicked_d)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update tree
|
||||||
|
*/
|
||||||
|
Tree.prototype.treeUpdate = function (source) {
|
||||||
|
const tasksStateObj = this.tasksStateObj
|
||||||
|
|
||||||
|
const tasksType = {}
|
||||||
|
|
||||||
|
this.taskTypeNodeOptions.map((v) => {
|
||||||
|
tasksType[v.taskType] = {
|
||||||
|
color: v.color
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const tasks = this.tasks
|
||||||
|
const height = Math.max(
|
||||||
|
500,
|
||||||
|
tasks.length * this.config.barHeight +
|
||||||
|
this.config.margin.top +
|
||||||
|
this.config.margin.bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
d3.select('.tree-svg')
|
||||||
|
.transition()
|
||||||
|
.duration(this.duration)
|
||||||
|
.attr('height', height)
|
||||||
|
|
||||||
|
tasks.forEach((n, i) => {
|
||||||
|
n.x = i * this.config.barHeight
|
||||||
|
})
|
||||||
|
|
||||||
|
const task = this.svg.selectAll('g.node').data(tasks, (d) => {
|
||||||
|
return d.id || (d.id = ++this.i)
|
||||||
|
})
|
||||||
|
|
||||||
|
const nodeEnter = task
|
||||||
|
.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr('class', this.nodesClass)
|
||||||
|
.attr('transform', () => 'translate(' + source.y0 + ',' + source.x0 + ')')
|
||||||
|
.style('opacity', 1e-6)
|
||||||
|
|
||||||
|
// Node circle
|
||||||
|
nodeEnter
|
||||||
|
.append('circle')
|
||||||
|
.attr('r', this.config.barHeight / 3)
|
||||||
|
.attr('class', 'task')
|
||||||
|
.attr('title', (d) => {
|
||||||
|
return d.data.type ? d.data.type : ''
|
||||||
|
})
|
||||||
|
.attr('height', this.config.barHeight)
|
||||||
|
.attr('width', (d) => this.config.barWidth - d.y)
|
||||||
|
.style('fill', (d) =>
|
||||||
|
d.data.type ? tasksType[d.data.type]?.color : '#fff'
|
||||||
|
)
|
||||||
|
.attr('task_id', (d) => {
|
||||||
|
return d.data.uuid
|
||||||
|
})
|
||||||
|
.on('click', this.treeToggles)
|
||||||
|
.on('mouseover', (e, d) => {
|
||||||
|
self.treeTooltip(d.data.type, e)
|
||||||
|
})
|
||||||
|
.on('mouseout', () => {
|
||||||
|
self.removeTooltip()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Node text
|
||||||
|
nodeEnter
|
||||||
|
.append('text')
|
||||||
|
.attr('dy', 3.5)
|
||||||
|
.attr('dx', this.config.barHeight / 2)
|
||||||
|
.text((d) => {
|
||||||
|
return d.data.name
|
||||||
|
})
|
||||||
|
.style('fill', 'var(--n-title-text-color)')
|
||||||
|
|
||||||
|
const translateRatio =
|
||||||
|
this.config.nodesMax > 10 ? (this.config.nodesMax > 20 ? 10 : 30) : 60
|
||||||
|
|
||||||
|
// Right node information
|
||||||
|
nodeEnter
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'stateboxes')
|
||||||
|
.attr(
|
||||||
|
'transform',
|
||||||
|
(d) =>
|
||||||
|
'translate(' + (this.config.nodesMax * translateRatio - d.y) + ',0)'
|
||||||
|
)
|
||||||
|
.selectAll('rect')
|
||||||
|
.data((d) => d.data.instances)
|
||||||
|
.enter()
|
||||||
|
.append('rect')
|
||||||
|
.on('click', () => {
|
||||||
|
this.removeTooltip()
|
||||||
|
})
|
||||||
|
.attr('class', 'state')
|
||||||
|
.style(
|
||||||
|
'fill',
|
||||||
|
(d) => (d.state && tasksStateObj[d.state].color) || '#ffffff'
|
||||||
|
)
|
||||||
|
.attr('rx', (d) => (d.type ? 0 : 12))
|
||||||
|
.attr('ry', (d) => (d.type ? 0 : 12))
|
||||||
|
.style('shape-rendering', (d) => (d.type ? 'crispEdges' : 'auto'))
|
||||||
|
.attr(
|
||||||
|
'x',
|
||||||
|
(d, i) => i * (this.config.squareSize + this.config.squarePading)
|
||||||
|
)
|
||||||
|
.attr('y', -(this.config.squareSize / 2))
|
||||||
|
.attr('width', 10)
|
||||||
|
.attr('height', 10)
|
||||||
|
.on('mouseover', (e, d) => {
|
||||||
|
self.treeTooltip(rtInstancesTooltip(d, tasksStateObj), e)
|
||||||
|
})
|
||||||
|
.on('mouseout', () => {
|
||||||
|
self.removeTooltip()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Convert nodes to their new location。
|
||||||
|
nodeEnter
|
||||||
|
.transition()
|
||||||
|
.duration(this.duration)
|
||||||
|
.attr('transform', (d) => 'translate(' + d.y + ',' + d.x + ')')
|
||||||
|
.style('opacity', 1)
|
||||||
|
|
||||||
|
// Node line
|
||||||
|
task
|
||||||
|
.transition()
|
||||||
|
.duration(this.duration)
|
||||||
|
.attr('class', this.nodesClass)
|
||||||
|
.attr('transform', (d) => 'translate(' + d.y + ',' + d.x + ')')
|
||||||
|
.style('opacity', 1)
|
||||||
|
|
||||||
|
// Convert the exit node to the new location of the parent node。
|
||||||
|
task
|
||||||
|
.exit()
|
||||||
|
.transition()
|
||||||
|
.duration(this.duration)
|
||||||
|
.attr('transform', () => 'translate(' + source.y + ',' + source.x + ')')
|
||||||
|
.style('opacity', 1e-6)
|
||||||
|
.remove()
|
||||||
|
|
||||||
|
// Update link
|
||||||
|
const link = this.svg
|
||||||
|
.selectAll('path.link')
|
||||||
|
.data(this.links, (d) => d.target.id)
|
||||||
|
|
||||||
|
// Enter any new links in the previous location of the parent node。
|
||||||
|
link
|
||||||
|
.enter()
|
||||||
|
.insert('path', 'g')
|
||||||
|
.attr('class', 'link')
|
||||||
|
.attr('d', () => {
|
||||||
|
const o = { x: source.x0, y: source.y0 }
|
||||||
|
return this.diagonal({ source: o, target: o })
|
||||||
|
})
|
||||||
|
.transition()
|
||||||
|
.duration(this.duration)
|
||||||
|
.attr('d', this.diagonal)
|
||||||
|
|
||||||
|
// Transition link
|
||||||
|
link.transition().duration(this.duration).attr('d', this.diagonal)
|
||||||
|
|
||||||
|
// Convert the exit node to the new location of the parent node
|
||||||
|
link
|
||||||
|
.exit()
|
||||||
|
.transition()
|
||||||
|
.duration(this.duration)
|
||||||
|
.attr('d', () => {
|
||||||
|
const o = { x: source.x, y: source.y }
|
||||||
|
return this.diagonal({ source: o, target: o })
|
||||||
|
})
|
||||||
|
.remove()
|
||||||
|
|
||||||
|
// Hide the old position for a transition.
|
||||||
|
tasks.forEach((d) => {
|
||||||
|
d.x0 = d.x
|
||||||
|
d.y0 = d.y
|
||||||
|
})
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset
|
||||||
|
*/
|
||||||
|
Tree.prototype.reset = function () {
|
||||||
|
// $('.d3-tree .tree').html('')
|
||||||
|
d3.select('.d3-tree .tree-svg').html('')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toottip handle
|
||||||
|
*/
|
||||||
|
Tree.prototype.treeTooltip = function (str, e) {
|
||||||
|
if (!str) return
|
||||||
|
|
||||||
|
this.selfTree.proxy.showTooltip = true
|
||||||
|
this.selfTree.proxy.tooltipText = str
|
||||||
|
this.selfTree.proxy.changeTooltip(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually clear tooltip
|
||||||
|
*/
|
||||||
|
Tree.prototype.removeTooltip = function () {
|
||||||
|
this.selfTree.proxy.showTooltip = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Tree()
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-nocheck
|
||||||
|
import { renderTableTime } from '@/common/common'
|
||||||
|
/**
|
||||||
|
* Node prompt dom
|
||||||
|
*/
|
||||||
|
const rtInstancesTooltip = (data, tasksStateObj) => {
|
||||||
|
let str = '<div style="text-align: left;word-break:break-all">'
|
||||||
|
str += `id : ${data.id}</br>`
|
||||||
|
str += `host : ${data.host}</br>`
|
||||||
|
str += `name : ${data.name}</br>`
|
||||||
|
str += `state : ${data.state ? tasksStateObj[data.state].desc : '-'}(${
|
||||||
|
data.state
|
||||||
|
})</br>`
|
||||||
|
if (data.type) {
|
||||||
|
str += `type : ${data.type}</br>`
|
||||||
|
}
|
||||||
|
str += `startTime : ${
|
||||||
|
data.startTime ? renderTableTime(data.startTime) : '-'
|
||||||
|
}</br>`
|
||||||
|
str += `endTime : ${data.endTime ? renderTableTime(data.endTime) : '-'}</br>`
|
||||||
|
str += `duration : ${data.duration}</br>`
|
||||||
|
str += '</div>'
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the maximum node length
|
||||||
|
* Easy to calculate the width dynamically
|
||||||
|
*/
|
||||||
|
const rtCountMethod = (list: any) => {
|
||||||
|
const arr: any = []
|
||||||
|
function count(list: any, t: string) {
|
||||||
|
let toggle = false
|
||||||
|
list.forEach((v) => {
|
||||||
|
if (v.children && v.children.length > 0) {
|
||||||
|
if (!toggle) {
|
||||||
|
toggle = true
|
||||||
|
t += '*'
|
||||||
|
arr.push(t)
|
||||||
|
}
|
||||||
|
count(v.children, t)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
count(list, '*')
|
||||||
|
let num = 6
|
||||||
|
arr.forEach((v) => {
|
||||||
|
if (v.length > num) {
|
||||||
|
num = v.length
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
export { rtInstancesTooltip, rtCountMethod }
|
||||||
Loading…
Reference in New Issue