You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by ki...@apache.org on 2022/01/21 11:26:00 UTC
[dolphinscheduler] branch dev updated: [Feature][UI Next] Add project workflow relation. (#8155)
This is an automated email from the ASF dual-hosted git repository.
kirs 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 8c65b19 [Feature][UI Next] Add project workflow relation. (#8155)
8c65b19 is described below
commit 8c65b194c7614943d3ba0f310fbc0da1ea692f0e
Author: songjianet <17...@qq.com>
AuthorDate: Fri Jan 21 19:25:55 2022 +0800
[Feature][UI Next] Add project workflow relation. (#8155)
* [Feature][UI Next] Add project workflow relation.
* [Feature][UI Next] Add tooltip component.
---
.../src/components/chart/index.ts | 2 +-
.../src/components/chart/modules/Graph.tsx | 152 -----------------
.../src/locales/modules/en_US.ts | 16 ++
.../src/locales/modules/zh_CN.ts | 16 ++
.../src/service/modules/lineages/index.ts | 18 +-
.../src/service/modules/lineages/types.ts | 32 +++-
.../workflow/relation/components/Graph.tsx | 189 +++++++++++++++++++++
.../src/views/projects/workflow/relation/index.tsx | 106 +++++++++---
.../projects/workflow/relation/use-relation.ts | 97 +++++++++++
9 files changed, 444 insertions(+), 184 deletions(-)
diff --git a/dolphinscheduler-ui-next/src/components/chart/index.ts b/dolphinscheduler-ui-next/src/components/chart/index.ts
index 9edd075..3522e76 100644
--- a/dolphinscheduler-ui-next/src/components/chart/index.ts
+++ b/dolphinscheduler-ui-next/src/components/chart/index.ts
@@ -38,7 +38,7 @@ function initChart<Opt extends ECBasicOption>(
const init = () => {
chart = globalProperties?.echarts.init(
domRef.value,
- themeStore.darkTheme ? 'dark-bold' : 'macarons'
+ themeStore.darkTheme && 'dark-bold'
)
chart && chart.setOption(option)
}
diff --git a/dolphinscheduler-ui-next/src/components/chart/modules/Graph.tsx b/dolphinscheduler-ui-next/src/components/chart/modules/Graph.tsx
deleted file mode 100644
index c11c54a..0000000
--- a/dolphinscheduler-ui-next/src/components/chart/modules/Graph.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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, ref } from 'vue'
-import initChart from '@/components/chart'
-import type { Ref } from 'vue'
-
-const props = {
- height: {
- type: [String, Number] as PropType<string | number>,
- default: 400
- },
- width: {
- type: [String, Number] as PropType<string | number>,
- default: '100%'
- },
- tooltipFormat: {
- type: String as PropType<string>,
- default: ''
- },
- legendData: {
- type: Array as PropType<Array<string>>,
- default: () => []
- },
- seriesData: {
- type: Array as PropType<Array<string>>,
- default: () => []
- },
- labelShow: {
- type: Array as PropType<Array<string>>,
- default: () => []
- },
- linksData: {
- type: Array as PropType<Array<string>>,
- default: () => []
- },
- labelFormat: {
- type: String as PropType<string>,
- default: ''
- }
-}
-
-const GraphChart = defineComponent({
- name: 'GraphChart',
- props,
- setup(props) {
- const graphChartRef: Ref<HTMLDivElement | null> = ref(null)
-
- const option = {
- tooltip: {
- trigger: 'item',
- triggerOn: 'mousemove',
- backgroundColor: '#2D303A',
- padding: [8, 12],
- formatter: props.tooltipFormat,
- color: '#2D303A',
- textStyle: {
- rich: {
- a: {
- fontSize: 12,
- color: '#2D303A',
- lineHeight: 12,
- align: 'left',
- padding: [4, 4, 4, 4]
- }
- }
- }
- },
- legend: [
- {
- orient: 'horizontal',
- top: 6,
- left: 6,
- data: props.legendData
- }
- ],
- series: [
- {
- type: 'graph',
- layout: 'force',
- nodeScaleRatio: 1.2,
- draggable: true,
- animation: false,
- data: props.seriesData,
- roam: true,
- symbol: 'roundRect',
- symbolSize: 70,
- categories: props.legendData,
- label: {
- show: props.labelShow,
- position: 'inside',
- formatter: props.labelFormat,
- color: '#222222',
- textStyle: {
- rich: {
- a: {
- fontSize: 12,
- color: '#222222',
- lineHeight: 12,
- align: 'left',
- padding: [4, 4, 4, 4]
- }
- }
- }
- },
- edgeSymbol: ['circle', 'arrow'],
- edgeSymbolSize: [4, 12],
- force: {
- repulsion: 1000,
- edgeLength: 300
- },
- links: props.linksData,
- lineStyle: {
- color: '#999999'
- }
- }
- ]
- }
-
- 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 GraphChart
diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index ca0b64e..709966e 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -323,6 +323,22 @@ const project = {
workflow: {
workflow_relation: 'Workflow Relation',
create_workflow: 'Create Workflow',
+ workflow_name: 'Workflow Name',
+ current_selection: 'Current Selection',
+ online: 'Online',
+ offline: 'Offline',
+ refresh: 'Refresh',
+ show_hide_label: 'Show / Hide Label',
+ workflow_offline: 'Workflow Offline',
+ schedule_offline: 'Schedule Offline',
+ schedule_start_time: 'Schedule Start Time',
+ schedule_end_time: 'Schedule End Time',
+ crontab_expression: 'Crontab',
+ workflow_publish_status: 'Workflow Publish Status',
+ schedule_publish_status: 'Schedule Publish Status'
+ },
+ dag: {
+ createWorkflow: 'Create Workflow',
search: 'Search',
download_png: 'Download PNG',
fullscreen_open: 'Open Fullscreen',
diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
index 0b82718..fab3be9 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -322,6 +322,22 @@ const project = {
workflow: {
workflow_relation: '工作流关系',
create_workflow: '创建工作流',
+ workflow_name: '工作流名称',
+ current_selection: '当前选择',
+ online: '已上线',
+ offline: '已下线',
+ refresh: '刷新',
+ show_hide_label: '显示 / 隐藏标签',
+ workflow_offline: '工作流下线',
+ schedule_offline: '调度下线',
+ schedule_start_time: '定时开始时间',
+ schedule_end_time: '定时结束时间',
+ crontab_expression: 'Crontab',
+ workflow_publish_status: '工作流上线状态',
+ schedule_publish_status: '定时状态'
+ },
+ dag: {
+ createWorkflow: '创建工作流',
search: '搜索',
download_png: '下载工作流图片',
fullscreen_open: '全屏',
diff --git a/dolphinscheduler-ui-next/src/service/modules/lineages/index.ts b/dolphinscheduler-ui-next/src/service/modules/lineages/index.ts
index f4eb825..982ed90 100644
--- a/dolphinscheduler-ui-next/src/service/modules/lineages/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/lineages/index.ts
@@ -16,32 +16,28 @@
*/
import { axios } from '@/service/service'
-import { ProjectCodeReq, WorkFlowNameReq } from './types'
+import { ProjectCodeReq, WorkFlowNameReq, WorkflowCodeReq } from './types'
export function queryWorkFlowList(projectCode: ProjectCodeReq): any {
return axios({
- url: `/projects/${projectCode}/lineages/list`,
+ url: `/projects/${projectCode.projectCode}/lineages/list`,
method: 'get'
})
}
-export function queryLineageByWorkFlowName(
- params: WorkFlowNameReq,
- projectCode: ProjectCodeReq
-): any {
+export function queryLineageByWorkFlowName(projectCode: ProjectCodeReq): any {
return axios({
- url: `/projects/${projectCode}/lineages/query-by-name`,
- method: 'get',
- params
+ url: `/projects/${projectCode.projectCode}/lineages/query-by-name`,
+ method: 'get'
})
}
export function queryLineageByWorkFlowCode(
- workFlowCode: WorkFlowNameReq,
+ workFlowCode: WorkflowCodeReq,
projectCode: ProjectCodeReq
): any {
return axios({
- url: `/projects/${projectCode}/lineages/${workFlowCode}`,
+ url: `/projects/${projectCode.projectCode}/lineages/${workFlowCode.workFlowCode}`,
method: 'get'
})
}
diff --git a/dolphinscheduler-ui-next/src/service/modules/lineages/types.ts b/dolphinscheduler-ui-next/src/service/modules/lineages/types.ts
index 1c748b1..63294b7 100644
--- a/dolphinscheduler-ui-next/src/service/modules/lineages/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/lineages/types.ts
@@ -19,8 +19,38 @@ interface ProjectCodeReq {
projectCode: number
}
+interface WorkflowCodeReq {
+ workFlowCode: number
+}
+
interface WorkFlowNameReq {
workFlowName: string
}
-export { ProjectCodeReq, WorkFlowNameReq }
+interface WorkFlowListRes extends WorkflowCodeReq {
+ workFlowName: string
+ workFlowPublishStatus: string
+ scheduleStartTime?: any
+ scheduleEndTime?: any
+ crontab?: any
+ schedulePublishStatus: number
+ sourceWorkFlowCode: string
+}
+
+interface WorkFlowRelationList {
+ sourceWorkFlowCode: number
+ targetWorkFlowCode: number
+}
+
+interface WorkflowRes {
+ workFlowList: WorkFlowListRes[]
+ workFlowRelationList: WorkFlowRelationList[]
+}
+
+export {
+ ProjectCodeReq,
+ WorkflowCodeReq,
+ WorkFlowNameReq,
+ WorkflowRes,
+ WorkFlowListRes
+}
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/relation/components/Graph.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/relation/components/Graph.tsx
new file mode 100644
index 0000000..725471d
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/relation/components/Graph.tsx
@@ -0,0 +1,189 @@
+/*
+ * 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, ref } from 'vue'
+import initChart from '@/components/chart'
+import { useI18n } from 'vue-i18n'
+import type { Ref } from 'vue'
+import { format } from 'date-fns'
+
+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: () => []
+ },
+ labelShow: {
+ type: Boolean as PropType<boolean>,
+ default: true
+ }
+}
+
+const GraphChart = defineComponent({
+ name: 'GraphChart',
+ props,
+ setup(props) {
+ const graphChartRef: Ref<HTMLDivElement | null> = ref(null)
+ const { t } = useI18n()
+
+ console.log(props.seriesData)
+
+ const legendData = [
+ { name: t('project.workflow.online') },
+ { name: t('project.workflow.workflow_offline') },
+ { name: t('project.workflow.schedule_offline') }
+ ]
+
+ const getCategory = (schedulerStatus: number, workflowStatus: number) => {
+ console.log(schedulerStatus, workflowStatus)
+
+ switch (true) {
+ case workflowStatus === 0:
+ return 1
+ case workflowStatus === 1 && schedulerStatus === 0:
+ return 2
+ case workflowStatus === 1 && schedulerStatus === 1:
+ default:
+ return 0
+ }
+ }
+
+ const option: any = {
+ tooltip: {
+ confine: true,
+ backgroundColor: '#fff',
+ formatter: (params: any) => {
+ if (!params.data.name) {
+ return false
+ }
+
+ const {
+ name,
+ scheduleStartTime,
+ scheduleEndTime,
+ crontab,
+ workFlowPublishStatus,
+ schedulePublishStatus
+ } = params.data
+
+ return `
+ ${t('project.workflow.workflow_name')}:${name}<br/>
+ ${t(
+ 'project.workflow.schedule_start_time'
+ )}:${scheduleStartTime}<br/>
+ ${t('project.workflow.schedule_end_time')}:${scheduleEndTime}<br/>
+ ${t('project.workflow.crontab_expression')}:${
+ crontab ? crontab : ' - '
+ }<br/>
+ ${t(
+ 'project.workflow.workflow_publish_status'
+ )}:${workFlowPublishStatus}<br/>
+ ${t(
+ 'project.workflow.schedule_publish_status'
+ )}:${schedulePublishStatus}<br/>
+ `
+ }
+ },
+ legend: [
+ {
+ data: legendData?.map((item) => item.name)
+ }
+ ],
+ series: [
+ {
+ type: 'graph',
+ layout: 'force',
+ draggable: true,
+ force: {
+ repulsion: 300,
+ edgeLength: 100
+ },
+ symbol: 'roundRect',
+ symbolSize: 70,
+ roam: false,
+ label: {
+ show: props.labelShow,
+ formatter: (val: any) => {
+ let newStr = ''
+ const str = val.data.name.split('')
+
+ for (let i = 0, s; (s = str[i++]); ) {
+ newStr += s
+ if (!(i % 10)) newStr += '\n'
+ }
+
+ return newStr.length > 60 ? newStr.slice(0, 60) + '...' : newStr
+ }
+ },
+ data: props.seriesData.map((item) => {
+ return {
+ name: item.name,
+ id: item.id,
+ category: getCategory(
+ Number(item.schedulePublishStatus),
+ Number(item.workFlowPublishStatus)
+ ),
+ workFlowPublishStatus: format(
+ new Date(item.workFlowPublishStatus),
+ 'yyyy-MM-dd HH:mm:ss'
+ ),
+ schedulePublishStatus: format(
+ new Date(item.schedulePublishStatus),
+ 'yyyy-MM-dd HH:mm:ss'
+ ),
+ crontab: item.crontab,
+ scheduleStartTime:
+ Number(item.scheduleStartTime) === 0
+ ? t('project.workflow.offline')
+ : t('project.workflow.online'),
+ scheduleEndTime:
+ Number(item.scheduleEndTime) === 0
+ ? t('project.workflow.offline')
+ : t('project.workflow.online')
+ }
+ }),
+ categories: legendData
+ }
+ ]
+ }
+
+ 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 GraphChart
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/relation/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/relation/index.tsx
index b47a6d0..0768480 100644
--- a/dolphinscheduler-ui-next/src/views/projects/workflow/relation/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/relation/index.tsx
@@ -15,37 +15,105 @@
* limitations under the License.
*/
-import { defineComponent } from 'vue'
+import { defineComponent, onMounted, toRefs, watch } from 'vue'
import { useI18n } from 'vue-i18n'
-import { NSelect, NButton, NIcon } from 'naive-ui'
-import { ReloadOutlined, EyeOutlined } from '@vicons/antd'
+import { useRoute } from 'vue-router'
+import {NSelect, NButton, NIcon, NSpace, NTooltip} from 'naive-ui'
+import {ReloadOutlined, EyeOutlined, EditOutlined} from '@vicons/antd'
+import { useRelation } from './use-relation'
import Card from '@/components/card'
+import Graph from './components/Graph'
const workflowRelation = defineComponent({
name: 'workflow-relation',
setup() {
- const { t } = useI18n()
+ const { t, locale } = useI18n()
+ const route = useRoute()
+ const { variables, getWorkflowName, getOneWorkflow, getWorkflowList } =
+ useRelation()
- return { t }
+ onMounted(() => {
+ getWorkflowList(Number(route.params.projectCode))
+ getWorkflowName(Number(route.params.projectCode))
+ })
+
+ const handleResetDate = () => {
+ variables.seriesData = []
+ variables.workflow && variables.workflow !== 0
+ ? getOneWorkflow(
+ Number(variables.workflow),
+ Number(route.params.projectCode)
+ )
+ : getWorkflowList(Number(route.params.projectCode))
+ }
+
+ watch(
+ () => [variables.workflow, variables.labelShow, locale.value],
+ () => {
+ handleResetDate()
+ }
+ )
+
+ return { t, handleResetDate, ...toRefs(variables) }
},
render() {
- const { t } = this
+ const { t, handleResetDate } = this
return (
<Card title={t('project.workflow.workflow_relation')}>
- <div>
- <NSelect />
- <NButton strong secondary circle type='info'>
- <NIcon>
- <ReloadOutlined />
- </NIcon>
- </NButton>
- <NButton strong secondary circle type='info'>
- <NIcon>
- <EyeOutlined />
- </NIcon>
- </NButton>
- </div>
+ {{
+ default: () =>
+ Object.keys(this.seriesData).length > 0 && (
+ <Graph seriesData={this.seriesData} labelShow={this.labelShow} />
+ ),
+ 'header-extra': () => (
+ <NSpace>
+ <NSelect
+ clearable
+ style={{ width: '300px' }}
+ placeholder={t('project.workflow.workflow_name')}
+ options={this.workflowOptions}
+ v-model={[this.workflow, 'value']}
+ />
+ <NTooltip trigger={'hover'}>
+ {{
+ default: () => t('project.workflow.refresh'),
+ trigger: () => (
+ <NButton
+ strong
+ secondary
+ circle
+ type='info'
+ onClick={handleResetDate}
+ >
+ <NIcon>
+ <ReloadOutlined />
+ </NIcon>
+ </NButton>
+ )
+ }}
+ </NTooltip>
+ <NTooltip trigger={'hover'}>
+ {{
+ default: () => t('project.workflow.show_hide_label'),
+ trigger: () => (
+ <NButton
+ strong
+ secondary
+ circle
+ type='info'
+ onClick={() => (this.labelShow = !this.labelShow)}
+ >
+ <NIcon>
+ <EyeOutlined />
+ </NIcon>
+ </NButton>
+ )
+ }}
+ </NTooltip>
+ </NSpace>
+ )
+ }}
</Card>
)
}
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/relation/use-relation.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/relation/use-relation.ts
new file mode 100644
index 0000000..8ef7cf4
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/relation/use-relation.ts
@@ -0,0 +1,97 @@
+/*
+ * 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, ref } from 'vue'
+import { useAsyncState } from '@vueuse/core'
+import {
+ queryWorkFlowList,
+ queryLineageByWorkFlowCode,
+ queryLineageByWorkFlowName
+} from '@/service/modules/lineages'
+import type {
+ WorkflowRes,
+ WorkFlowListRes
+} from '@/service/modules/lineages/types'
+
+export function useRelation() {
+ const variables = reactive({
+ workflowOptions: [],
+ workflow: ref(null),
+ seriesData: [],
+ labelShow: ref(true)
+ })
+
+ const formatWorkflow = (obj: Array<WorkFlowListRes>) => {
+ variables.seriesData = []
+
+ variables.seriesData = obj.map((item) => {
+ console.log(item)
+ return {
+ name: item.workFlowName,
+ id: item.workFlowCode,
+ ...item
+ }
+ }) as any
+ }
+
+ const getWorkflowName = (projectCode: number) => {
+ const { state } = useAsyncState(
+ queryLineageByWorkFlowName({ projectCode }).then(
+ (res: Array<WorkFlowListRes>) => {
+ variables.workflowOptions = res.map((item) => {
+ return {
+ label: item.workFlowName,
+ value: item.workFlowCode
+ }
+ }) as any
+ }
+ ),
+ {}
+ )
+
+ return state
+ }
+
+ const getOneWorkflow = (workflowCode: number, projectCode: number) => {
+ const { state } = useAsyncState(
+ queryLineageByWorkFlowCode(
+ { workFlowCode: workflowCode },
+ { projectCode }
+ ).then((res: WorkflowRes) => {
+ formatWorkflow(res.workFlowList)
+ }),
+ {}
+ )
+
+ return state
+ }
+
+ const getWorkflowList = (projectCode: number) => {
+ const { state } = useAsyncState(
+ queryWorkFlowList({
+ projectCode
+ }).then((res: WorkflowRes) => {
+ formatWorkflow(res.workFlowList)
+ }),
+ {}
+ )
+
+ return state
+ }
+
+ return { variables, getWorkflowName, getOneWorkflow, getWorkflowList }
+}