Browse Source

[Feature][UI Next][V1.0.0-Alpha] Add the tree chart of the work flow definition to the project management. (#8694)

* add the component of the use-tree

* modify tree view

* add the component of the tree-view

* developed the tree view of the work flow definition

* merge from dev
3.0.0/version-upgrade
calvin 3 years ago committed by GitHub
parent
commit
79c9d7dd36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      dolphinscheduler-ui-next/.eslintrc.js
  2. 2725
      dolphinscheduler-ui-next/pnpm-lock.yaml
  3. 4
      dolphinscheduler-ui-next/src/App.tsx
  4. 117
      dolphinscheduler-ui-next/src/components/chart/modules/Tree.tsx
  5. 22
      dolphinscheduler-ui-next/src/components/chart/modules/types.ts
  6. 4
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  7. 6
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  8. 10
      dolphinscheduler-ui-next/src/router/modules/projects.ts
  9. 4
      dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
  10. 10
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/table-action.tsx
  11. 36
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/index.module.scss
  12. 346
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/index.tsx
  13. 27
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/types.ts
  14. 58
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/use-tree.tsx
  15. 10
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts

2
dolphinscheduler-ui-next/.eslintrc.js

@ -66,6 +66,6 @@ module.exports = {
'vue/component-definition-name-casing': 'off',
'vue/require-valid-default-prop': 'off',
'no-console': 'error',
'vue/no-setup-props-destructure': 'off',
'vue/no-setup-props-destructure': 'off'
}
}

2725
dolphinscheduler-ui-next/pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

4
dolphinscheduler-ui-next/src/App.tsx

@ -65,7 +65,9 @@ const App = defineComponent({
theme={this.currentTheme}
theme-overrides={themeOverrides}
style={{ width: '100%', height: '100vh' }}
date-locale={String(this.localesStore.getLocales) === 'zh_CN' ? dateZhCN : dateEnUS}
date-locale={
String(this.localesStore.getLocales) === 'zh_CN' ? dateZhCN : dateEnUS
}
locale={String(this.localesStore.getLocales) === 'zh_CN' ? zhCN : enUS}
>
<NMessageProvider>

117
dolphinscheduler-ui-next/src/components/chart/modules/Tree.tsx

@ -0,0 +1,117 @@
/*
* 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, onMounted, PropType, ref, watch, reactive } from 'vue'
import initChart from '@/components/chart'
import type { Ref } from 'vue'
import { IChartDataItem } from '@/components/chart/modules/types'
const props = {
height: {
type: [String, Number] as PropType<string | number>,
default: 590
},
width: {
type: [String, Number] as PropType<string | number>,
default: '100%'
},
data: {
type: Array as PropType<Array<IChartDataItem>>
}
}
const TreeChart = defineComponent({
name: 'TreeChart',
props,
setup(props) {
const treeChartRef: Ref<HTMLDivElement | null> = ref(null)
const option = reactive({
tooltip: {
trigger: 'item',
backgroundColor: '#fff'
},
textStyle: {
fontSize: 14
},
series: [
{
type: 'tree',
id: 0,
name: 'tree1',
data: props.data,
top: '10%',
left: '5%',
bottom: '10%',
right: '15%',
symbol: 'circle',
symbolSize: 18,
edgeShape: 'polyline',
edgeForkPosition: '63%',
initialTreeDepth: 3,
lineStyle: {
width: 3
},
label: {
backgroundColor: '#fff',
position: 'left',
verticalAlign: 'middle',
align: 'right'
},
leaves: {
label: {
position: 'right',
verticalAlign: 'middle',
align: 'left'
}
},
emphasis: {
focus: 'descendant'
},
expandAndCollapse: true,
animationDuration: 550,
animationDurationUpdate: 750
}
]
})
initChart(treeChartRef, option)
watch(
() => props.data,
() => {
option.series[0].data = props.data
}
)
return { treeChartRef }
},
render() {
const { height, width } = this
return (
<div
ref='treeChartRef'
style={{
height: typeof height === 'number' ? height + 'px' : height,
width: typeof width === 'number' ? width + 'px' : width
}}
/>
)
}
})
export default TreeChart

22
dolphinscheduler-ui-next/src/components/chart/modules/types.ts

@ -0,0 +1,22 @@
/*
* 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.
*/
export interface IChartDataItem {
name: string
value?: string | number
children?: IChartDataItem[]
}

4
dolphinscheduler-ui-next/src/locales/modules/en_US.ts

@ -400,6 +400,7 @@ const project = {
cron_manage: 'Cron manage',
delete: 'Delete',
tree_view: 'Tree View',
tree_limit: 'Limit Size',
export: 'Export',
version_info: 'Version Info',
version: 'Version',
@ -501,7 +502,8 @@ const project = {
download_log: 'Download Log',
refresh_log: 'Refresh Log',
enter_full_screen: 'Enter full screen',
cancel_full_screen: 'Cancel full screen'
cancel_full_screen: 'Cancel full screen',
task_state: 'Task status'
},
task: {
task_name: 'Task Name',

6
dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts

@ -396,7 +396,8 @@ const project = {
copy_workflow: '复制工作流',
cron_manage: '定时管理',
delete: '删除',
tree_view: '树形图',
tree_view: '工作流树形图',
tree_limit: '限制大小',
export: '导出',
version_info: '版本信息',
version: '版本',
@ -498,7 +499,8 @@ const project = {
download_log: '下载日志',
refresh_log: '刷新日志',
enter_full_screen: '进入全屏',
cancel_full_screen: '取消全屏'
cancel_full_screen: '取消全屏',
task_state: '任务状态'
},
task: {
task_name: '任务名称',

10
dolphinscheduler-ui-next/src/router/modules/projects.ts

@ -140,6 +140,16 @@ export default {
showSide: true,
auth: []
}
},
{
path: '/projects/:projectCode/workflow-definition/tree/:definitionCode',
name: 'workflow-definition-tree',
component: components['projects-workflow-definition-tree'],
meta: {
title: '工作流定义树形图',
showSide: true,
auth: []
}
}
]
}

4
dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts

@ -231,8 +231,8 @@ export function deleteVersion(
}
export function viewTree(
code: CodeReq,
processCode: CodeReq,
code: number,
processCode: number,
params: LimitReq
): any {
return axios({

10
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/table-action.tsx

@ -51,7 +51,8 @@ export default defineComponent({
'releaseWorkflow',
'copyWorkflow',
'exportWorkflow',
'gotoTimingManage'
'gotoTimingManage',
'gotoWorkflowTree'
],
setup(props, ctx) {
const handleStartWorkflow = () => {
@ -86,6 +87,10 @@ export default defineComponent({
ctx.emit('gotoTimingManage')
}
const handleGotoWorkflowTree = () => {
ctx.emit('gotoWorkflowTree')
}
return {
handleStartWorkflow,
handleTimingWorkflow,
@ -95,6 +100,7 @@ export default defineComponent({
handleCopyWorkflow,
handleExportWorkflow,
handleGotoTimingManage,
handleGotoWorkflowTree,
...toRefs(props)
}
},
@ -261,7 +267,7 @@ export default defineComponent({
type='info'
tag='div'
circle
/* TODO: Goto tree view*/
onClick={this.handleGotoWorkflowTree}
>
<NIcon>
<ApartmentOutlined />

36
dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/index.module.scss

@ -0,0 +1,36 @@
/*
* 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.
*/
.content {
width: 100%;
.card {
margin-bottom: 8px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 10px 0;
.right {
float: right;
margin: 3px 0 3px 4px;
width: 15%;
}
}
}

346
dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/index.tsx

@ -0,0 +1,346 @@
/*
* 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 Card from '@/components/card'
import { ArrowLeftOutlined } from '@vicons/antd'
import { NButton, NFormItem, NIcon, NSelect, NSpace, NImage } from 'naive-ui'
import { defineComponent, onMounted, Ref, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import styles from './index.module.scss'
import UseTree from '@/views/projects/workflow/definition/tree/use-tree'
import { IChartDataItem } from '@/components/chart/modules/types'
import { Router, useRouter } from 'vue-router'
import { viewTree } from '@/service/modules/process-definition'
import { SelectMixedOption } from 'naive-ui/lib/select/src/interface'
import { find } from 'lodash'
import { ITaskTypeNodeOption, ITaskStateOption } from './types'
export default defineComponent({
name: 'WorkflowDefinitionTiming',
setup() {
const router: Router = useRouter()
const { t, locale } = useI18n()
const options: Ref<Array<SelectMixedOption>> = ref([
{ label: '25', value: 25 },
{ label: '50', value: 50 },
{ label: '75', value: 75 },
{ label: '100', value: 100 }
])
const projectCode = ref(
Number(router.currentRoute.value.params.projectCode)
)
const definitionCode = ref(
Number(router.currentRoute.value.params.definitionCode)
)
const chartData: Ref<Array<IChartDataItem>> = ref([] as IChartDataItem[])
const taskStateMap = ref()
const taskTypeNodeOptions: Ref<Array<ITaskTypeNodeOption>> = ref([
{
taskType: 'SHELL',
color: '#646464',
image: '/src/assets/images/task-icons/shell.png'
},
{
taskType: 'SUB_PROCESS',
color: '#4295DA',
image: '/src/assets/images/task-icons/sub_process.png'
},
{
taskType: 'PROCEDURE',
color: '#545CC6',
image: '/src/assets/images/task-icons/procedure.png'
},
{
taskType: 'SQL',
color: '#8097A0',
image: '/src/assets/images/task-icons/sql.png'
},
{
taskType: 'SPARK',
color: '#a16435',
image: '/src/assets/images/task-icons/spark.png'
},
{
taskType: 'FLINK',
color: '#d68f5b',
image: '/src/assets/images/task-icons/flink.png'
},
{
taskType: 'MR',
color: '#A1A5C9',
image: '/src/assets/images/task-icons/mr.png'
},
{
taskType: 'PYTHON',
color: '#60BCD5',
image: '/src/assets/images/task-icons/python.png'
},
{
taskType: 'DEPENDENT',
color: '#60BCD5',
image: '/src/assets/images/task-icons/dependent.png'
},
{
taskType: 'HTTP',
color: '#7f3903',
image: '/src/assets/images/task-icons/http.png'
},
{
taskType: 'DATAX',
color: '#75CC71',
image: '/src/assets/images/task-icons/datax.png'
},
{
taskType: 'PIGEON',
color: '#5EC459',
image: '/src/assets/images/task-icons/pigeon.png'
},
{
taskType: 'SQOOP',
color: '#f98b3d',
image: '/src/assets/images/task-icons/sqoop.png'
},
{
taskType: 'CONDITIONS',
color: '#b99376',
image: '/src/assets/images/task-icons/conditions.png'
},
{
taskType: 'SWITCH',
color: '#ff6f00',
image: '/src/assets/images/task-icons/switch.png'
},
{
taskType: 'SEATUNNEL',
color: '#8c8c8f',
image: '/src/assets/images/task-icons/seatunnel.png'
},
{ taskType: 'DAG', color: '#bbdde9' }
])
const initTaskStateMap = () => {
taskStateMap.value = [
{
state: 'SUBMITTED_SUCCESS',
value: t('project.task.submitted_success'),
color: '#A9A9A9'
},
{
state: 'RUNNING_EXECUTION',
value: t('project.task.running_execution'),
color: '#4295DA'
},
{
state: 'READY_PAUSE',
value: t('project.task.ready_pause'),
color: '#50AEA3'
},
{ state: 'PAUSE', value: t('project.task.pause'), color: '#367A72' },
{
state: 'READY_STOP',
value: t('project.task.ready_stop'),
color: '#E93424'
},
{ state: 'STOP', value: t('project.task.stop'), color: '#D62E20' },
{ state: 'FAILURE', value: t('project.task.failed'), color: '#000000' },
{
state: 'SUCCESS',
value: t('project.task.success'),
color: '#67C93B'
},
{
state: 'NEED_FAULT_TOLERANCE',
value: t('project.task.need_fault_tolerance'),
color: '#F09235'
},
{ state: 'KILL', value: t('project.task.kill'), color: '#991F14' },
{
state: 'WAITING_THREAD',
value: t('project.task.waiting_thread'),
color: '#8635E4'
},
{
state: 'WAITING_DEPEND',
value: t('project.task.waiting_depend'),
color: '#4A0AB6'
},
{
state: 'DELAY_EXECUTION',
value: t('project.task.delay_execution'),
color: '#c5b4ec'
},
{
state: 'FORCED_SUCCESS',
value: t('project.task.forced_success'),
color: '#453463'
},
{
state: 'SERIAL_WAIT',
value: t('project.task.serial_wait'),
color: '#1b0446'
}
]
}
const initChartData = (node: any, newNode: any) => {
newNode.children = []
node?.children.map((child: any) => {
let newChild = {}
initChartData(child, newChild)
newNode.children.push(newChild)
})
newNode.name = node.name
newNode.value = node.name === 'DAG' ? 'DAG' : node?.type
let 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) => {
if (projectCode && definitionCode) {
const res = await viewTree(projectCode.value, definitionCode.value, {
limit: limit
})
chartData.value = [{ name: 'DAG', value: 'DAG' }]
initChartData(res, chartData.value[0])
}
}
const onSelectChange = (value: number) => {
if (value) {
getWorkflowTreeData(value)
}
}
const initData = () => {
initTaskStateMap()
getWorkflowTreeData(25)
}
onMounted(() => {
initData()
})
watch(
() => locale.value,
() => {
initData()
}
)
return {
chartData,
options,
onSelectChange,
taskTypeNodeOptions
}
},
render() {
const { chartData, options, onSelectChange, taskTypeNodeOptions } = this
const { t } = useI18n()
const router: Router = useRouter()
return (
<div class={styles.content}>
<Card class={styles.card}>
<div class={styles.header}>
<NButton type='primary' onClick={() => router.go(-1)}>
<NIcon>
<ArrowLeftOutlined />
</NIcon>
</NButton>
<NFormItem
size={'small'}
class={styles.right}
showFeedback={false}
labelPlacement={'left'}
label={t('project.workflow.tree_limit')}
>
<NSelect
size='small'
defaultValue={25}
onUpdateValue={onSelectChange}
options={options}
/>
</NFormItem>
</div>
</Card>
<Card title={t('project.workflow.tree_view')}>
<NSpace align='center'>
{taskTypeNodeOptions
.filter((option: any) => option.image)
.map((option: any, index: number) => (
<NButton text size='tiny' color={option.color}>
<NImage width='20' src={option.image} />
{option.taskType}
</NButton>
))}
</NSpace>
<UseTree chartData={chartData} />
</Card>
</div>
)
}
})

27
dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/types.ts

@ -0,0 +1,27 @@
/*
* 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.
*/
export interface ITaskTypeNodeOption {
taskType: string
color?: string
image?: string
}
export interface ITaskStateOption {
state: string
value?: string
color?: string
}

58
dolphinscheduler-ui-next/src/views/projects/workflow/definition/tree/use-tree.tsx

@ -0,0 +1,58 @@
/*
* 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, PropType } from 'vue'
import { NGrid, NGi } from 'naive-ui'
import TreeChart from '@/components/chart/modules/Tree'
import Card from '@/components/card'
import { IChartDataItem } from '@/components/chart/modules/types'
const props = {
title: {
type: String as PropType<string>
},
chartData: {
type: Array as PropType<Array<IChartDataItem>>,
default: () => []
}
}
const UseTree = defineComponent({
name: 'TreeCard',
props,
emits: ['updateDatePickerValue'],
setup(props, ctx) {
const onUpdateDatePickerValue = (val: any) => {
ctx.emit('updateDatePickerValue', val)
}
return { onUpdateDatePickerValue }
},
render() {
const { title, chartData } = this
return (
<Card title={title}>
<NGrid x-gap={12} cols={1}>
<NGi>{chartData.length > 0 && <TreeChart data={chartData} />}</NGi>
</NGrid>
</Card>
)
}
})
export default UseTree

10
dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts

@ -159,7 +159,8 @@ export function useTable() {
onReleaseWorkflow: () => releaseWorkflow(row),
onCopyWorkflow: () => copyWorkflow(row),
onExportWorkflow: () => exportWorkflow(row),
onGotoTimingManage: () => gotoTimingManage(row)
onGotoTimingManage: () => gotoTimingManage(row),
onGotoWorkflowTree: () => gotoWorkflowTree(row)
})
}
] as TableColumns<any>
@ -281,6 +282,13 @@ export function useTable() {
})
}
const gotoWorkflowTree = (row: any) => {
router.push({
name: 'workflow-definition-tree',
params: { projectCode: variables.projectCode, definitionCode: row.code }
})
}
const getTableData = (params: IDefinitionParam) => {
const { state } = useAsyncState(
queryListPaging({ ...params }, variables.projectCode).then((res: any) => {

Loading…
Cancel
Save