You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by so...@apache.org on 2022/03/04 09:38:47 UTC
[dolphinscheduler] branch dev updated: [Feature][UI Next][V1.0.0-Alpha] Support gantt (#8697)
This is an automated email from the ASF dual-hosted git repository.
songjian pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git
The following commit(s) were added to refs/heads/dev by this push:
new c8b5d1e [Feature][UI Next][V1.0.0-Alpha] Support gantt (#8697)
c8b5d1e is described below
commit c8b5d1e5088241faaf29f61a5625d33fffb27a8e
Author: Devosend <de...@gmail.com>
AuthorDate: Fri Mar 4 17:37:40 2022 +0800
[Feature][UI Next][V1.0.0-Alpha] Support gantt (#8697)
---
.../src/router/modules/projects.ts | 10 +
.../src/service/modules/process-instances/index.ts | 2 +-
dolphinscheduler-ui-next/src/utils/common.ts | 2 +-
.../workflow/instance/components/table-action.tsx | 11 +-
.../instance/gantt/components/gantt-chart.tsx | 214 +++++++++++++++++++++
.../projects/workflow/instance/gantt/index.tsx | 73 +++++++
.../views/projects/workflow/instance/gantt/type.ts | 39 ++++
.../projects/workflow/instance/gantt/use-gantt.ts | 53 +++++
8 files changed, 401 insertions(+), 3 deletions(-)
diff --git a/dolphinscheduler-ui-next/src/router/modules/projects.ts b/dolphinscheduler-ui-next/src/router/modules/projects.ts
index 683517c..afc401d 100644
--- a/dolphinscheduler-ui-next/src/router/modules/projects.ts
+++ b/dolphinscheduler-ui-next/src/router/modules/projects.ts
@@ -122,6 +122,16 @@ export default {
}
},
{
+ path: '/projects/:projectCode/workflow/instances/:id/gantt',
+ name: 'workflow-instance-gantt',
+ component: components['projects-workflow-instance-gantt'],
+ meta: {
+ title: '工作流实例甘特图',
+ showSide: true,
+ auth: []
+ }
+ },
+ {
path: '/projects/:projectCode/task/definitions',
name: 'task-definition',
component: components['projects-task-definition'],
diff --git a/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts b/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts
index 67b6323..42578ab 100644
--- a/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts
@@ -118,7 +118,7 @@ export function queryTaskListByProcessId(id: number, code: number): any {
})
}
-export function vieGanttTree(id: IdReq, code: CodeReq): any {
+export function viewGanttTree(id: number, code: number): any {
return axios({
url: `/projects/${code}/process-instances/${id}/view-gantt`,
method: 'get'
diff --git a/dolphinscheduler-ui-next/src/utils/common.ts b/dolphinscheduler-ui-next/src/utils/common.ts
index e5675b9..f328dbb 100644
--- a/dolphinscheduler-ui-next/src/utils/common.ts
+++ b/dolphinscheduler-ui-next/src/utils/common.ts
@@ -261,7 +261,7 @@ export const tasksState = (t: any): ITaskState => ({
SUCCESS: {
id: 7,
desc: `${t('project.workflow.success')}`,
- color: '#33cc00',
+ color: '#95DF96',
icon: CheckCircleOutlined,
isSpin: false,
classNames: 'success'
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx
index a480041..faa9e60 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/components/table-action.tsx
@@ -62,6 +62,14 @@ export default defineComponent({
})
}
+ const handleGantt = () => {
+ router.push({
+ name: 'workflow-instance-gantt',
+ params: { id: props.row!.id },
+ query: { code: props.row!.processDefinitionCode }
+ })
+ }
+
const handleReRun = () => {
ctx.emit('reRun')
}
@@ -89,6 +97,7 @@ export default defineComponent({
handleStop,
handleSuspend,
handleDeleteInstance,
+ handleGantt,
...toRefs(props)
}
},
@@ -280,8 +289,8 @@ export default defineComponent({
size='tiny'
type='info'
circle
- /* TODO: Goto gantt*/
disabled={this.row?.disabled}
+ onClick={this.handleGantt}
>
<NIcon>
<ControlOutlined />
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/components/gantt-chart.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/components/gantt-chart.tsx
new file mode 100644
index 0000000..f4785b6
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/components/gantt-chart.tsx
@@ -0,0 +1,214 @@
+/*
+ * 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, ref, PropType } from 'vue'
+import * as echarts from 'echarts'
+import type { Ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import initChart from '@/components/chart'
+import { tasksState } from '@/utils/common'
+import { format } from 'date-fns'
+import { parseTime } from '@/utils/common'
+import { ISeriesData } from '../type'
+
+const props = {
+ height: {
+ type: [String, Number] as PropType<string | number>,
+ default: window.innerHeight - 174
+ },
+ width: {
+ type: [String, Number] as PropType<string | number>,
+ default: '100%'
+ },
+ seriesData: {
+ type: Array as PropType<Array<any>>,
+ default: () => []
+ },
+ taskList: {
+ type: Array as PropType<Array<string>>,
+ default: []
+ }
+}
+
+const GanttChart = defineComponent({
+ name: 'GanttChart',
+ props,
+ setup(props) {
+ const graphChartRef: Ref<HTMLDivElement | null> = ref(null)
+ const { t } = useI18n()
+
+ const state = tasksState(t)
+
+ const data: ISeriesData = {}
+ Object.keys(state).forEach((key) => (data[key] = []))
+ const series = Object.keys(state).map((key) => ({
+ id: key,
+ type: 'custom',
+ name: state[key].desc,
+ renderItem: renderItem,
+ itemStyle: {
+ opacity: 0.8,
+ color: state[key].color,
+ color0: state[key].color
+ },
+ encode: {
+ x: [1, 2],
+ y: 0
+ },
+ data: data[key]
+ }))
+
+ // format series data
+ let minTime = Number(new Date())
+ props.seriesData.forEach(function (task, index) {
+ minTime = minTime < task.startDate[0] ? minTime : task.startDate[0]
+ data[task.status].push({
+ name: task.name,
+ value: [
+ index,
+ task.startDate[0],
+ task.endDate[0],
+ task.endDate[0] - task.startDate[0]
+ ],
+ itemStyle: {
+ color: state[task.status].color
+ }
+ })
+ })
+
+ // customer render
+ function renderItem(params: any, api: any) {
+ const taskIndex = api.value(0)
+ const start = api.coord([api.value(1), taskIndex])
+ const end = api.coord([api.value(2), taskIndex])
+ const height = api.size([0, 1])[1] * 0.6
+
+ const rectShape = echarts.graphic.clipRectByRect(
+ {
+ x: start[0],
+ y: start[1] - height / 2,
+ width: end[0] - start[0],
+ height: height
+ },
+ {
+ x: params.coordSys.x,
+ y: params.coordSys.y,
+ width: params.coordSys.width,
+ height: params.coordSys.height
+ }
+ )
+ return (
+ rectShape && {
+ type: 'rect',
+ transition: ['shape'],
+ shape: rectShape,
+ style: api.style()
+ }
+ )
+ }
+
+ const option = {
+ title: {
+ text: t('project.workflow.task_state'),
+ textStyle: {
+ fontWeight: 'normal',
+ fontSize: 14
+ },
+ left: 50
+ },
+ tooltip: {
+ formatter: function (params: any) {
+ const taskName = params.data.name
+ const data = props.seriesData.filter(
+ (item) => item.taskName === taskName
+ )
+ let str = `taskName : ${taskName}</br>`
+ str += `status : ${state[data[0].status].desc} (${
+ data[0].status
+ })</br>`
+ str += `startTime : ${data[0].isoStart}</br>`
+ str += `endTime : ${data[0].isoEnd}</br>`
+ str += `duration : ${data[0].duration}</br>`
+ return str
+ }
+ },
+ legend: {
+ left: 150,
+ padding: [5, 5, 5, 5]
+ },
+ dataZoom: [
+ {
+ type: 'slider',
+ filterMode: 'weakFilter',
+ showDataShadow: false,
+ top:
+ props.taskList.length * 100 > 200
+ ? props.taskList.length * 100
+ : 200,
+ labelFormatter: ''
+ },
+ {
+ type: 'inside',
+ filterMode: 'weakFilter'
+ }
+ ],
+ grid: {
+ height: props.taskList.length * 50,
+ top: 80
+ },
+ xAxis: {
+ min: minTime,
+ scale: true,
+ position: 'top',
+ axisTick: { show: true },
+ splitLine: { show: false },
+ axisLabel: {
+ formatter: function (val: number) {
+ return format(parseTime(val), 'HH:mm:ss')
+ }
+ }
+ },
+ yAxis: {
+ axisTick: { show: false },
+ splitLine: { show: false },
+ axisLine: { show: false },
+ max: props.taskList.length,
+ data: props.taskList
+ },
+ series: series
+ }
+
+ initChart(graphChartRef, option)
+
+ return { graphChartRef }
+ },
+ render() {
+ const { height, width } = this
+
+ return (
+ <div
+ ref='graphChartRef'
+ style={{
+ height: typeof height === 'number' ? height + 'px' : height,
+ width: typeof width === 'number' ? width + 'px' : width
+ }}
+ />
+ )
+ }
+})
+
+export default GanttChart
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/index.tsx
new file mode 100644
index 0000000..9f68e37
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/index.tsx
@@ -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.
+ */
+
+import { defineComponent, onMounted, toRefs, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useRoute } from 'vue-router'
+import Card from '@/components/card'
+import GanttChart from './components/gantt-chart'
+import { useGantt } from './use-gantt'
+
+const workflowRelation = defineComponent({
+ name: 'workflow-relation',
+ setup() {
+ const { t, locale } = useI18n()
+ const route = useRoute()
+
+ const { variables, getGantt } = useGantt()
+
+ const id = Number(route.params.id)
+ const code = Number(route.params.projectCode)
+
+ const handleResetDate = () => {
+ variables.seriesData = []
+ variables.taskList = []
+ getGantt(id, code)
+ }
+
+ onMounted(() => {
+ getGantt(id, code)
+ })
+
+ watch(
+ () => [locale.value],
+ () => {
+ handleResetDate()
+ }
+ )
+
+ return { t, ...toRefs(variables) }
+ },
+ render() {
+ const { t } = this
+ return (
+ <Card title={t('project.workflow.gantt')}>
+ {{
+ default: () =>
+ this.seriesData.length > 0 && (
+ <GanttChart
+ seriesData={this.seriesData}
+ taskList={this.taskList}
+ />
+ )
+ }}
+ </Card>
+ )
+ }
+})
+
+export default workflowRelation
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/type.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/type.ts
new file mode 100644
index 0000000..24068b5
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/type.ts
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+interface ITask {
+ taskName: string
+ startDate: Array<number>
+ endDate: Array<number>
+ isoStart: string
+ isoEnd: string
+ status: string
+ duration: string
+}
+
+interface IGanttRes {
+ height: number
+ taskNames: Array<number>
+ taskStatus: Object
+ tasks: Array<ITask>
+}
+
+interface ISeriesData {
+ [taskState: string]: Array<any>
+}
+
+export { ITask, IGanttRes, ISeriesData }
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/use-gantt.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/use-gantt.ts
new file mode 100644
index 0000000..1560d45
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/gantt/use-gantt.ts
@@ -0,0 +1,53 @@
+/*
+ * 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 { reactive } from 'vue'
+import { useAsyncState } from '@vueuse/core'
+import { viewGanttTree } from '@/service/modules/process-instances'
+import { IGanttRes } from './type'
+
+export function useGantt() {
+ const variables = reactive({
+ seriesData: [],
+ taskList: [] as Array<string>
+ })
+
+ const formatGantt = (obj: IGanttRes) => {
+ variables.seriesData = []
+ variables.taskList = []
+
+ variables.seriesData = obj.tasks.map((item) => {
+ variables.taskList.push(item.taskName)
+ return {
+ name: item.taskName,
+ ...item
+ }
+ }) as any
+ }
+
+ const getGantt = (id: number, code: number) => {
+ const { state } = useAsyncState(
+ viewGanttTree(id, code).then((res: IGanttRes) => {
+ formatGantt(res)
+ }),
+ {}
+ )
+ return state
+ }
+
+ return { variables, getGantt }
+}