From 1f87eb67609d52fa0d5cf780e406572458b17836 Mon Sep 17 00:00:00 2001 From: songjianet <1778651752@qq.com> Date: Thu, 3 Feb 2022 15:20:56 +0800 Subject: [PATCH] [Feature][UI Next] Add task result. (#8277) --- .../src/layouts/content/index.tsx | 3 +- .../src/layouts/content/use-dataList.ts | 17 +- .../src/locales/modules/en_US.ts | 39 +++- .../src/locales/modules/zh_CN.ts | 39 +++- .../src/router/modules/data-quality.ts | 42 ++++ dolphinscheduler-ui-next/src/router/routes.ts | 4 +- .../src/service/modules/data-quality/index.ts | 35 +++ .../src/service/modules/data-quality/types.ts | 74 +++++++ .../task-result/index.module.scss | 26 +++ .../views/data-quality/task-result/index.tsx | 177 +++++++++++++++ .../data-quality/task-result/use-table.ts | 201 ++++++++++++++++++ 11 files changed, 650 insertions(+), 7 deletions(-) create mode 100644 dolphinscheduler-ui-next/src/router/modules/data-quality.ts create mode 100644 dolphinscheduler-ui-next/src/service/modules/data-quality/index.ts create mode 100644 dolphinscheduler-ui-next/src/service/modules/data-quality/types.ts create mode 100644 dolphinscheduler-ui-next/src/views/data-quality/task-result/index.module.scss create mode 100644 dolphinscheduler-ui-next/src/views/data-quality/task-result/index.tsx create mode 100644 dolphinscheduler-ui-next/src/views/data-quality/task-result/use-table.ts diff --git a/dolphinscheduler-ui-next/src/layouts/content/index.tsx b/dolphinscheduler-ui-next/src/layouts/content/index.tsx index dfb2ca4c48..a8dbf2e97b 100644 --- a/dolphinscheduler-ui-next/src/layouts/content/index.tsx +++ b/dolphinscheduler-ui-next/src/layouts/content/index.tsx @@ -61,7 +61,8 @@ const Content = defineComponent({ const getSideMenu = (state: any) => { const key = menuStore.getMenuKey state.sideMenuOptions = - state.menuOptions.filter((menu: { key: string }) => menu.key === key)[0]?.children || state.menuOptions + state.menuOptions.filter((menu: { key: string }) => menu.key === key)[0] + ?.children || state.menuOptions state.isShowSide = menuStore.getShowSideStatus } diff --git a/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts b/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts index cffa40cedf..76ee66294d 100644 --- a/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts +++ b/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts @@ -42,7 +42,9 @@ import { EnvironmentOutlined, KeyOutlined, SafetyOutlined, - GroupOutlined + GroupOutlined, + ContainerOutlined, + ApartmentOutlined } from '@vicons/antd' import { useMenuStore } from '@/store/menu/menu' @@ -172,6 +174,19 @@ export function useDataList() { } ] }, + { + label: t('menu.data_quality'), + key: 'data-quality', + icon: renderIcon(ContainerOutlined), + isShowSide: true, + children: [ + { + label: t('menu.task_result'), + key: `/data-quality/task-result`, + icon: renderIcon(ApartmentOutlined) + } + ] + }, { label: t('menu.datasource'), key: 'datasource', diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 4800632443..db1fb23ca9 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -76,7 +76,9 @@ const menu = { token_manage: 'Token Manage', task_group_manage: 'Task Group Manage', task_group_option: 'Task Group Option', - task_group_queue: 'Task Group Queue' + task_group_queue: 'Task Group Queue', + data_quality: 'Data Quality', + task_result: 'Task Result' } const home = { @@ -715,6 +717,38 @@ const datasource = { user_password_tips: 'Please enter your password' } +const data_quality = { + task_result: { + task_name: 'Task Name', + workflow_instance: 'Workflow Instance', + rule_type: 'Rule Type', + rule_name: 'Rule Name', + state: 'State', + actual_value: 'Actual Value', + excepted_value: 'Excepted Value', + check_type: 'Check Type', + operator: 'Operator', + threshold: 'Threshold', + failure_strategy: 'Failure Strategy', + excepted_value_type: 'Excepted Value Type', + error_output_path: 'Error Output Path', + username: 'Username', + create_time: 'Create Time', + update_time: 'Update Time', + undone: 'Undone', + success: 'Success', + failure: 'Failure', + single_table: 'Single Table', + single_table_custom_sql: 'Single Table Custom Sql', + multi_table_accuracy: 'Multi Table Accuracy', + multi_table_comparison: 'Multi Table Comparison', + expected_and_actual_or_expected: '(Expected - Actual) / Expected x 100%', + expected_and_actual: 'Expected - Actual', + actual_and_expected: 'Actual - Expected', + actual_or_expected: 'Actual / Expected x 100%' + } +} + export default { login, modal, @@ -728,5 +762,6 @@ export default { resource, project, security, - datasource + datasource, + data_quality } diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index fecc01b87a..20c41a870f 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -76,7 +76,9 @@ const menu = { token_manage: '令牌管理', task_group_manage: '任务组管理', task_group_option: '任务组配置', - task_group_queue: '任务组队列' + task_group_queue: '任务组队列', + data_quality: '数据质量', + task_result: '任务结果' } const home = { @@ -708,6 +710,38 @@ const datasource = { user_password_tips: '请输入密码' } +const data_quality = { + task_result: { + task_name: '任务名称', + workflow_instance: '工作流实例', + rule_type: '规则类型', + rule_name: '规则名称', + state: '状态', + actual_value: '实际值', + excepted_value: '期望值', + check_type: '检测类型', + operator: '操作符', + threshold: '阈值', + failure_strategy: '失败策略', + excepted_value_type: '期望值类型', + error_output_path: '错误数据路径', + username: '用户名', + create_time: '创建时间', + update_time: '更新时间', + undone: '未完成', + success: '成功', + failure: '失败', + single_table: '单表检测', + single_table_custom_sql: '自定义SQL', + multi_table_accuracy: '多表准确性', + multi_table_comparison: '两表值对比', + expected_and_actual_or_expected: '(期望值-实际值)/实际值 x 100%', + expected_and_actual: '期望值-实际值', + actual_and_expected: '实际值-期望值', + actual_or_expected: '实际值/期望值 x 100%' + } +} + export default { login, modal, @@ -721,5 +755,6 @@ export default { resource, project, security, - datasource + datasource, + data_quality } diff --git a/dolphinscheduler-ui-next/src/router/modules/data-quality.ts b/dolphinscheduler-ui-next/src/router/modules/data-quality.ts new file mode 100644 index 0000000000..fe4c106807 --- /dev/null +++ b/dolphinscheduler-ui-next/src/router/modules/data-quality.ts @@ -0,0 +1,42 @@ +/* + * 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 type { Component } from 'vue' +import utils from '@/utils' + +// All TSX files under the views folder automatically generate mapping relationship +const modules = import.meta.glob('/src/views/**/**.tsx') +const components: { [key: string]: Component } = utils.mapping(modules) + +export default { + path: '/data-quality', + name: 'data-quality', + meta: { title: 'data-quality' }, + redirect: { name: 'task-result' }, + component: () => import('@/layouts/content'), + children: [ + { + path: '/data-quality/task-result', + name: 'task-result', + component: components['data-quality-task-result'], + meta: { + title: '数据质量-task-result', + showSide: true + } + } + ] +} diff --git a/dolphinscheduler-ui-next/src/router/routes.ts b/dolphinscheduler-ui-next/src/router/routes.ts index a821f23b41..2c2506a8c8 100644 --- a/dolphinscheduler-ui-next/src/router/routes.ts +++ b/dolphinscheduler-ui-next/src/router/routes.ts @@ -23,6 +23,7 @@ import resourcesPage from './modules/resources' import datasourcePage from './modules/datasource' import monitorPage from './modules/monitor' import securityPage from './modules/security' +import dataQualityPage from './modules/data-quality' // All TSX files under the views folder automatically generate mapping relationship const modules = import.meta.glob('/src/views/**/**.tsx') @@ -68,7 +69,8 @@ const basePage: RouteRecordRaw[] = [ resourcesPage, datasourcePage, monitorPage, - securityPage + securityPage, + dataQualityPage ] /** diff --git a/dolphinscheduler-ui-next/src/service/modules/data-quality/index.ts b/dolphinscheduler-ui-next/src/service/modules/data-quality/index.ts new file mode 100644 index 0000000000..2250b633a1 --- /dev/null +++ b/dolphinscheduler-ui-next/src/service/modules/data-quality/index.ts @@ -0,0 +1,35 @@ +/* + * 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 { axios } from '@/service/service' +import type { RuleListReq, ResultListReq } from './types' + +export function queryRuleListPaging(params: RuleListReq): any { + return axios({ + url: '/data-quality/rule/page', + method: 'get', + params + }) +} + +export function queryExecuteResultListPaging(params: ResultListReq): any { + return axios({ + url: '/data-quality/result/page', + method: 'get', + params + }) +} diff --git a/dolphinscheduler-ui-next/src/service/modules/data-quality/types.ts b/dolphinscheduler-ui-next/src/service/modules/data-quality/types.ts new file mode 100644 index 0000000000..9ad447c193 --- /dev/null +++ b/dolphinscheduler-ui-next/src/service/modules/data-quality/types.ts @@ -0,0 +1,74 @@ +/* + * 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 ListReq { + pageNo: number + pageSize: number + searchVal?: string +} + +interface RuleListReq extends ListReq { + endDate?: string + startDate?: string + ruleType?: string +} + +interface ResultListReq extends ListReq { + endDate?: string + startDate?: string + ruleType?: string + state?: string +} + +interface ResultItem { + id: number + processDefinitionId: number + processDefinitionName: string + processDefinitionCode: number + processInstanceId: number + processInstanceName: string + projectCode: number + taskInstanceId: number + taskName: string + ruleType: number + ruleName: string + statisticsValue: number + comparisonValue: number + comparisonType: number + comparisonTypeName: string + checkType: number + threshold: number + operator: number + failureStrategy: number + userId: number + userName: string + state: number + errorOutputPath: string + createTime: string + updateTime: string +} + +interface ResultListRes { + totalList: ResultItem[] + total: number + totalPage: number + pageSize: number + currentPage: number + start: number +} + +export { RuleListReq, ResultListReq, ResultItem, ResultListRes } diff --git a/dolphinscheduler-ui-next/src/views/data-quality/task-result/index.module.scss b/dolphinscheduler-ui-next/src/views/data-quality/task-result/index.module.scss new file mode 100644 index 0000000000..e6b0dfe014 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/data-quality/task-result/index.module.scss @@ -0,0 +1,26 @@ +/* + * 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. + */ + +.table-card { + margin-top: 8px; + + .pagination { + margin-top: 20px; + display: flex; + justify-content: center; + } +} diff --git a/dolphinscheduler-ui-next/src/views/data-quality/task-result/index.tsx b/dolphinscheduler-ui-next/src/views/data-quality/task-result/index.tsx new file mode 100644 index 0000000000..3ac5311a1f --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/data-quality/task-result/index.tsx @@ -0,0 +1,177 @@ +/* + * 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 { + NSpace, + NInput, + NSelect, + NDatePicker, + NButton, + NIcon, + NDataTable, + NPagination, + NCard +} from 'naive-ui' +import { SearchOutlined } from '@vicons/antd' +import { useTable } from './use-table' +import { useI18n } from 'vue-i18n' +import Card from '@/components/card' +import styles from './index.module.scss' + +const TaskResult = defineComponent({ + name: 'task-result', + setup() { + const { t, variables, getTableData, createColumns } = useTable() + + const requestTableData = () => { + getTableData({ + pageSize: variables.pageSize, + pageNo: variables.page, + ruleType: variables.ruleType, + state: variables.state, + searchVal: variables.searchVal, + datePickerRange: variables.datePickerRange + }) + } + + const onUpdatePageSize = () => { + variables.page = 1 + requestTableData() + } + + const onSearch = () => { + variables.page = 1 + requestTableData() + } + + onMounted(() => { + createColumns(variables) + requestTableData() + }) + + watch(useI18n().locale, () => { + createColumns(variables) + }) + + return { + t, + ...toRefs(variables), + requestTableData, + onUpdatePageSize, + onSearch + } + }, + render() { + const { t, requestTableData, onUpdatePageSize, onSearch } = this + + return ( + <> + + + + + + + + {{ + icon: () => ( + + + + ) + }} + + + + + +
+ +
+
+ + ) + } +}) + +export default TaskResult diff --git a/dolphinscheduler-ui-next/src/views/data-quality/task-result/use-table.ts b/dolphinscheduler-ui-next/src/views/data-quality/task-result/use-table.ts new file mode 100644 index 0000000000..04157d1c7f --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/data-quality/task-result/use-table.ts @@ -0,0 +1,201 @@ +/* + * 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 { useI18n } from 'vue-i18n' +import { reactive, ref } from 'vue' +import { useAsyncState } from '@vueuse/core' +import { queryExecuteResultListPaging } from '@/service/modules/data-quality' +import { format } from 'date-fns' +import type { + ResultItem, + ResultListRes +} from '@/service/modules/data-quality/types' + +export function useTable() { + const { t } = useI18n() + + const variables = reactive({ + columns: [], + tableData: [], + page: ref(1), + pageSize: ref(10), + ruleType: ref(null), + state: ref(null), + searchVal: ref(null), + datePickerRange: ref(null), + totalPage: ref(1) + }) + + const createColumns = (variables: any) => { + variables.columns = [ + { + title: '#', + key: 'index' + }, + { + title: t('data_quality.task_result.task_name'), + key: 'userName' + }, + { + title: t('data_quality.task_result.workflow_instance'), + key: 'processInstanceName' + }, + { + title: t('data_quality.task_result.rule_type'), + key: 'ruleType', + render: (row: ResultItem) => { + if (row.ruleType === 0) { + return t('data_quality.task_result.single_table') + } else if (row.ruleType === 1) { + return t('data_quality.task_result.single_table_custom_sql') + } else if (row.ruleType === 2) { + return t('data_quality.task_result.multi_table_accuracy') + } else if (row.ruleType === 3) { + return t('data_quality.task_result.multi_table_comparison') + } + } + }, + { + title: t('data_quality.task_result.rule_name'), + key: 'ruleName' + }, + { + title: t('data_quality.task_result.state'), + key: 'state', + render: (row: ResultItem) => { + if (row.state === 0) { + return t('data_quality.task_result.undone') + } else if (row.state === 1) { + return t('data_quality.task_result.success') + } else if (row.state === 2) { + return t('data_quality.task_result.failure') + } + } + }, + { + title: t('data_quality.task_result.actual_value'), + key: 'statisticsValue' + }, + { + title: t('data_quality.task_result.excepted_value'), + key: 'comparisonValue' + }, + { + title: t('data_quality.task_result.check_type'), + key: 'checkType', + render: (row: ResultItem) => { + if (row.checkType === 0) { + return t('data_quality.task_result.expected_and_actual') + } else if (row.checkType === 1) { + return t('data_quality.task_result.actual_and_expected') + } else if (row.checkType === 2) { + return t('data_quality.task_result.actual_or_expected') + } else if (row.checkType === 3) { + return t('data_quality.task_result.expected_and_actual_or_expected') + } + } + }, + { + title: t('data_quality.task_result.operator'), + key: 'operator', + render: (row: ResultItem) => { + if (row.operator === 0) { + return '=' + } else if (row.operator === 1) { + return '<' + } else if (row.operator === 2) { + return '<=' + } else if (row.operator === 3) { + return '>' + } else if (row.operator === 4) { + return '>=' + } else if (row.operator === 5) { + return '!=' + } + } + }, + { + title: t('data_quality.task_result.threshold'), + key: 'threshold' + }, + { + title: t('data_quality.task_result.failure_strategy'), + key: 'failureStrategy' + }, + { + title: t('data_quality.task_result.excepted_value_type'), + key: 'comparisonTypeName' + }, + { + title: t('data_quality.task_result.error_output_path'), + key: 'errorOutputPath', + render: (row: ResultItem) => { + return row.errorOutputPath ? row : '-' + } + }, + { + title: t('data_quality.task_result.username'), + key: 'userName' + }, + { + title: t('data_quality.task_result.create_time'), + key: 'createTime' + }, + { + title: t('data_quality.task_result.update_time'), + key: 'updateTime' + } + ] + } + + const getTableData = (params: any) => { + const data = { + pageSize: params.pageSize, + pageNo: params.pageNo, + ruleType: params.ruleType, + state: params.state, + searchVal: params.searchVal, + startDate: params.datePickerRange + ? format(new Date(params.datePickerRange[0]), 'yyyy-MM-dd HH:mm:ss') + : '', + endDate: params.datePickerRange + ? format(new Date(params.datePickerRange[1]), 'yyyy-MM-dd HH:mm:ss') + : '' + } + + const { state } = useAsyncState( + queryExecuteResultListPaging(data).then((res: ResultListRes) => { + variables.tableData = res.totalList.map((item, index) => { + return { + index: index + 1, + ...item + } + }) as any + }), + {} + ) + + return state + } + + return { + t, + variables, + getTableData, + createColumns + } +}