Browse Source

[Feature][UI Next] Add Process Definition (#8266)

3.0.0/version-upgrade
Devosend 3 years ago committed by GitHub
parent
commit
5a94981ca7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      dolphinscheduler-ui-next/src/env.d.ts
  2. 2
      dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts
  3. 66
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  4. 69
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  5. 11
      dolphinscheduler-ui-next/src/router/modules/projects.ts
  6. 2
      dolphinscheduler-ui-next/src/service/modules/executors/index.ts
  7. 31
      dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
  8. 32
      dolphinscheduler-ui-next/src/service/modules/schedules/index.ts
  9. 611
      dolphinscheduler-ui-next/src/utils/timezone.ts
  10. 103
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/import-modal.tsx
  11. 388
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx
  12. 309
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/table-action.tsx
  13. 321
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/timing-modal.tsx
  14. 92
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts
  15. 247
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts
  16. 198
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-table.ts
  17. 95
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/version-modal.tsx
  18. 88
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.module.scss
  19. 132
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/index.tsx
  20. 113
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/timing/index.tsx
  21. 22
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/timing/types.ts
  22. 245
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/timing/use-table.ts
  23. 72
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/types.ts
  24. 286
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/use-table.ts

6
dolphinscheduler-ui-next/src/env.d.ts vendored

@ -30,6 +30,12 @@ declare global {
} }
} }
declare global {
interface Navigator {
msSaveBlob?: (blob: any, defaultName?: string) => boolean
}
}
declare namespace jquery {} declare namespace jquery {}
declare module '*.png' declare module '*.png'

2
dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts

@ -104,7 +104,7 @@ export function useDataList() {
}, },
{ {
label: t('menu.workflow_definition'), label: t('menu.workflow_definition'),
key: `/projects/${menuStore.getProjectCode}/workflow/definitions` key: `/projects/${menuStore.getProjectCode}/workflow-definition`
}, },
{ {
label: t('menu.workflow_instance'), label: t('menu.workflow_instance'),

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

@ -353,6 +353,7 @@ const project = {
workflow: { workflow: {
workflow_relation: 'Workflow Relation', workflow_relation: 'Workflow Relation',
create_workflow: 'Create Workflow', create_workflow: 'Create Workflow',
import_workflow: 'Import Workflow',
workflow_name: 'Workflow Name', workflow_name: 'Workflow Name',
current_selection: 'Current Selection', current_selection: 'Current Selection',
online: 'Online', online: 'Online',
@ -365,7 +366,70 @@ const project = {
schedule_end_time: 'Schedule End Time', schedule_end_time: 'Schedule End Time',
crontab_expression: 'Crontab', crontab_expression: 'Crontab',
workflow_publish_status: 'Workflow Publish Status', workflow_publish_status: 'Workflow Publish Status',
schedule_publish_status: 'Schedule Publish Status' schedule_publish_status: 'Schedule Publish Status',
workflow_definition: 'Workflow Definition',
id: '#',
status: 'Status',
create_time: 'Create Time',
update_time: 'Update Time',
description: 'Description',
create_user: 'Create User',
modify_user: 'Modify User',
operation: 'Operation',
edit: 'Edit',
start: 'Start',
timing: 'Timing',
timezone: 'Timezone',
upline: 'Online',
copy_workflow: 'Copy Workflow',
cron_manage: 'Cron manage',
delete: 'Delete',
tree_view: 'Tree View',
export: 'Export',
version_info: 'Version Info',
version: 'Version',
file_upload: 'File Upload',
upload_file: 'Upload File',
upload: 'Upload',
file_name: 'File Name',
success: 'Success',
set_parameters_before_starting: 'Please set the parameters before starting',
set_parameters_before_timing: 'Set parameters before timing',
start_and_stop_time: 'Start and stop time',
next_five_execution_times: 'Next five execution times',
execute_time: 'Execute time',
failure_strategy: 'Failure Strategy',
notification_strategy: 'Notification Strategy',
workflow_priority: 'Workflow Priority',
worker_group: 'Worker Group',
environment_name: 'Environment Name',
alarm_group: 'Alarm Group',
complement_data: 'Complement Data',
startup_parameter: 'Startup Parameter',
whether_dry_run: 'Whether Dry-Run',
continue: 'Continue',
end: 'End',
none_send: 'None',
success_send: 'Success',
failure_send: 'Failure',
all_send: 'All',
whether_complement_data: 'Whether it is a complement process?',
schedule_date: 'Schedule date',
mode_of_execution: 'Mode of execution',
serial_execution: 'Serial execution',
parallel_execution: 'Parallel execution',
parallelism: 'Parallelism',
custom_parallelism: 'Custom Parallelism',
please_enter_parallelism: 'Please enter Parallelism',
please_choose: 'Please Choose',
start_time: 'Start Time',
end_time: 'End Time',
crontab: 'Crontab',
delete_confirm: 'Delete?',
enter_name_tips: 'Please enter name',
switch_version: 'Switch To This Version',
confirm_switch_version: 'Confirm Switch To This Version?',
current_version: 'Current Version'
}, },
task: { task: {
task_name: 'Task Name', task_name: 'Task Name',

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

@ -348,6 +348,7 @@ const project = {
workflow: { workflow: {
workflow_relation: '工作流关系', workflow_relation: '工作流关系',
create_workflow: '创建工作流', create_workflow: '创建工作流',
import_workflow: '导入工作流',
workflow_name: '工作流名称', workflow_name: '工作流名称',
current_selection: '当前选择', current_selection: '当前选择',
online: '已上线', online: '已上线',
@ -360,7 +361,73 @@ const project = {
schedule_end_time: '定时结束时间', schedule_end_time: '定时结束时间',
crontab_expression: 'Crontab', crontab_expression: 'Crontab',
workflow_publish_status: '工作流上线状态', workflow_publish_status: '工作流上线状态',
schedule_publish_status: '定时状态' schedule_publish_status: '定时状态',
workflow_definition: '工作流定义',
id: '编号',
status: '状态',
create_time: '创建时间',
update_time: '更新时间',
description: '描述',
create_user: '创建用户',
modify_user: '修改用户',
operation: '操作',
edit: '编辑',
confirm: '确定',
cancel: '取消',
start: '运行',
timing: '定时',
timezone: '时区',
up_line: '上线',
down_line: '下线',
copy_workflow: '复制工作流',
cron_manage: '定时管理',
delete: '删除',
tree_view: '树形图',
export: '导出',
version_info: '版本信息',
version: '版本',
file_upload: '文件上传',
upload_file: '上传文件',
upload: '上传',
file_name: '文件名称',
success: '成功',
set_parameters_before_starting: '启动前请先设置参数',
set_parameters_before_timing: '定时前请先设置参数',
start_and_stop_time: '起止时间',
next_five_execution_times: '接下来五次执行时间',
execute_time: '执行时间',
failure_strategy: '失败策略',
notification_strategy: '通知策略',
workflow_priority: '流程优先级',
worker_group: 'Worker分组',
environment_name: '环境名称',
alarm_group: '告警组',
complement_data: '补数',
startup_parameter: '启动参数',
whether_dry_run: '是否空跑',
continue: '继续',
end: '结束',
none_send: '都不发',
success_send: '成功发',
failure_send: '失败发',
all_send: '成功或失败都发',
whether_complement_data: '是否是补数',
schedule_date: '调度日期',
mode_of_execution: '执行方式',
serial_execution: '串行执行',
parallel_execution: '并行执行',
parallelism: '并行度',
custom_parallelism: '自定义并行度',
please_enter_parallelism: '请输入并行度',
please_choose: '请选择',
start_time: '开始时间',
end_time: '结束时间',
crontab: 'Crontab',
delete_confirm: '确定删除吗?',
enter_name_tips: '请输入名称',
switch_version: '切换到该版本',
confirm_switch_version: '确定切换到该版本吗?',
current_version: '当前版本'
}, },
task: { task: {
task_name: '任务名称', task_name: '任务名称',

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

@ -60,7 +60,7 @@ export default {
} }
}, },
{ {
path: '/projects/:projectCode/workflow/definitions', path: '/projects/:projectCode/workflow-definition',
name: 'workflow-definition-list', name: 'workflow-definition-list',
component: components['projects-workflow-definition'], component: components['projects-workflow-definition'],
meta: { meta: {
@ -68,6 +68,15 @@ export default {
showSide: true showSide: true
} }
}, },
{
path: '/projects/:projectCode/workflow-definition/timing/:definitionCode',
name: 'workflow-definition-timing',
component: components['projects-workflow-definition-timing'],
meta: {
title: '定时管理',
showSide: true
}
},
{ {
path: '/projects/:projectCode/workflow/definitions/create', path: '/projects/:projectCode/workflow/definitions/create',
name: 'workflow-definition-create', name: 'workflow-definition-create',

2
dolphinscheduler-ui-next/src/service/modules/executors/index.ts

@ -44,7 +44,7 @@ export function startCheckProcessDefinition(
export function startProcessInstance( export function startProcessInstance(
data: ProcessInstanceReq, data: ProcessInstanceReq,
code: ProjectCodeReq code: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/executors/start-process-instance`, url: `/projects/${code}/executors/start-process-instance`,

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

@ -30,7 +30,7 @@ import {
TargetCodeReq TargetCodeReq
} from './types' } from './types'
export function queryListPaging(params: PageReq & ListReq, code: CodeReq): any { export function queryListPaging(params: PageReq & ListReq, code: number): any {
return axios({ return axios({
url: `/projects/${code}/process-definition`, url: `/projects/${code}/process-definition`,
method: 'get', method: 'get',
@ -58,7 +58,7 @@ export function queryAllByProjectCode(code: CodeReq): any {
export function batchCopyByCodes( export function batchCopyByCodes(
data: TargetCodeReq & CodesReq, data: TargetCodeReq & CodesReq,
code: CodeReq code: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/batch-copy`, url: `/projects/${code}/process-definition/batch-copy`,
@ -75,10 +75,11 @@ export function batchDeleteByCodes(data: CodesReq, code: CodeReq): any {
}) })
} }
export function batchExportByCodes(data: CodesReq, code: CodeReq): any { export function batchExportByCodes(data: CodesReq, code: number): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/batch-export`, url: `/projects/${code}/process-definition/batch-export`,
method: 'post', method: 'post',
responseType: 'blob',
data data
}) })
} }
@ -105,7 +106,7 @@ export function getTaskListByDefinitionCodes(
}) })
} }
export function importProcessDefinition(data: FileReq, code: CodeReq): any { export function importProcessDefinition(data: FormData, code: number): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/import`, url: `/projects/${code}/process-definition/import`,
method: 'post', method: 'post',
@ -168,7 +169,7 @@ export function update(
}) })
} }
export function deleteByCode(code: CodeReq, processCode: CodeReq): any { export function deleteByCode(code: number, processCode: number): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/${processCode}`, url: `/projects/${code}/process-definition/${processCode}`,
method: 'delete' method: 'delete'
@ -177,8 +178,8 @@ export function deleteByCode(code: CodeReq, processCode: CodeReq): any {
export function release( export function release(
data: NameReq & ReleaseStateReq, data: NameReq & ReleaseStateReq,
code: CodeReq, code: number,
processCode: CodeReq processCode: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/${processCode}/release`, url: `/projects/${code}/process-definition/${processCode}/release`,
@ -199,8 +200,8 @@ export function getTasksByDefinitionCode(
export function queryVersions( export function queryVersions(
params: PageReq, params: PageReq,
code: CodeReq, code: number,
processCode: CodeReq processCode: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/${processCode}/versions`, url: `/projects/${code}/process-definition/${processCode}/versions`,
@ -210,9 +211,9 @@ export function queryVersions(
} }
export function switchVersion( export function switchVersion(
code: CodeReq, code: number,
processCode: CodeReq, processCode: number,
version: VersionReq version: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/${processCode}/versions/${version}`, url: `/projects/${code}/process-definition/${processCode}/versions/${version}`,
@ -221,9 +222,9 @@ export function switchVersion(
} }
export function deleteVersion( export function deleteVersion(
code: CodeReq, code: number,
processCode: CodeReq, processCode: number,
version: VersionReq version: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/${processCode}/versions/${version}`, url: `/projects/${code}/process-definition/${processCode}/versions/${version}`,

32
dolphinscheduler-ui-next/src/service/modules/schedules/index.ts

@ -20,16 +20,17 @@ import {
ProjectCodeReq, ProjectCodeReq,
IdReq, IdReq,
CodeReq, CodeReq,
ListReq,
ScheduleReq, ScheduleReq,
WorkerGroupIdReq, WorkerGroupIdReq,
ScheduleListReq,
CreateScheduleReq, CreateScheduleReq,
DeleteScheduleReq DeleteScheduleReq,
ProcessDefinitionCodeReq
} from './types' } from './types'
export function queryScheduleListPaging( export function queryScheduleListPaging(
params: ScheduleListReq, params: ListReq & ProcessDefinitionCodeReq,
projectCode: ProjectCodeReq projectCode: number
): any { ): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules`, url: `/projects/${projectCode}/schedules`,
@ -40,7 +41,7 @@ export function queryScheduleListPaging(
export function createSchedule( export function createSchedule(
data: CreateScheduleReq & WorkerGroupIdReq, data: CreateScheduleReq & WorkerGroupIdReq,
projectCode: ProjectCodeReq projectCode: number
): any { ): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules`, url: `/projects/${projectCode}/schedules`,
@ -56,10 +57,7 @@ export function queryScheduleList(projectCode: ProjectCodeReq): any {
}) })
} }
export function previewSchedule( export function previewSchedule(data: ScheduleReq, projectCode: number): any {
data: ScheduleReq,
projectCode: ProjectCodeReq
): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules/preview`, url: `/projects/${projectCode}/schedules/preview`,
method: 'post', method: 'post',
@ -81,8 +79,8 @@ export function updateScheduleByProcessDefinitionCode(
export function updateSchedule( export function updateSchedule(
data: CreateScheduleReq, data: CreateScheduleReq,
projectCode: ProjectCodeReq, projectCode: number,
id: IdReq id: number
): any { ): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules/${id}`, url: `/projects/${projectCode}/schedules/${id}`,
@ -92,24 +90,24 @@ export function updateSchedule(
} }
export function deleteScheduleById( export function deleteScheduleById(
data: DeleteScheduleReq, scheduleId: number,
projectCode: ProjectCodeReq projectCode: number
): any { ): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules/${data.id}`, url: `/projects/${projectCode}/schedules/${scheduleId}`,
method: 'delete', method: 'delete',
data params: { scheduleId }
}) })
} }
export function offline(projectCode: ProjectCodeReq, id: IdReq): any { export function offline(projectCode: number, id: number): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules/${id}/offline`, url: `/projects/${projectCode}/schedules/${id}/offline`,
method: 'post' method: 'post'
}) })
} }
export function online(projectCode: ProjectCodeReq, id: IdReq): any { export function online(projectCode: number, id: number): any {
return axios({ return axios({
url: `/projects/${projectCode}/schedules/${id}/online`, url: `/projects/${projectCode}/schedules/${id}/online`,
method: 'post' method: 'post'

611
dolphinscheduler-ui-next/src/utils/timezone.ts

@ -0,0 +1,611 @@
/*
* 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 const timezoneList = [
'Africa/Abidjan',
'Africa/Accra',
'Africa/Addis_Ababa',
'Africa/Algiers',
'Africa/Asmara',
'Africa/Asmera',
'Africa/Bamako',
'Africa/Bangui',
'Africa/Banjul',
'Africa/Bissau',
'Africa/Blantyre',
'Africa/Brazzaville',
'Africa/Bujumbura',
'Africa/Cairo',
'Africa/Casablanca',
'Africa/Ceuta',
'Africa/Conakry',
'Africa/Dakar',
'Africa/Dar_es_Salaam',
'Africa/Djibouti',
'Africa/Douala',
'Africa/El_Aaiun',
'Africa/Freetown',
'Africa/Gaborone',
'Africa/Harare',
'Africa/Johannesburg',
'Africa/Juba',
'Africa/Kampala',
'Africa/Khartoum',
'Africa/Kigali',
'Africa/Kinshasa',
'Africa/Lagos',
'Africa/Libreville',
'Africa/Lome',
'Africa/Luanda',
'Africa/Lubumbashi',
'Africa/Lusaka',
'Africa/Malabo',
'Africa/Maputo',
'Africa/Maseru',
'Africa/Mbabane',
'Africa/Mogadishu',
'Africa/Monrovia',
'Africa/Nairobi',
'Africa/Ndjamena',
'Africa/Niamey',
'Africa/Nouakchott',
'Africa/Ouagadougou',
'Africa/Porto-Novo',
'Africa/Sao_Tome',
'Africa/Timbuktu',
'Africa/Tripoli',
'Africa/Tunis',
'Africa/Windhoek',
'America/Adak',
'America/Anchorage',
'America/Anguilla',
'America/Antigua',
'America/Araguaina',
'America/Argentina/Buenos_Aires',
'America/Argentina/Catamarca',
'America/Argentina/ComodRivadavia',
'America/Argentina/Cordoba',
'America/Argentina/Jujuy',
'America/Argentina/La_Rioja',
'America/Argentina/Mendoza',
'America/Argentina/Rio_Gallegos',
'America/Argentina/Salta',
'America/Argentina/San_Juan',
'America/Argentina/San_Luis',
'America/Argentina/Tucuman',
'America/Argentina/Ushuaia',
'America/Aruba',
'America/Asuncion',
'America/Atikokan',
'America/Atka',
'America/Bahia',
'America/Bahia_Banderas',
'America/Barbados',
'America/Belem',
'America/Belize',
'America/Blanc-Sablon',
'America/Boa_Vista',
'America/Bogota',
'America/Boise',
'America/Buenos_Aires',
'America/Cambridge_Bay',
'America/Campo_Grande',
'America/Cancun',
'America/Caracas',
'America/Catamarca',
'America/Cayenne',
'America/Cayman',
'America/Chicago',
'America/Chihuahua',
'America/Coral_Harbour',
'America/Cordoba',
'America/Costa_Rica',
'America/Creston',
'America/Cuiaba',
'America/Curacao',
'America/Danmarkshavn',
'America/Dawson',
'America/Dawson_Creek',
'America/Denver',
'America/Detroit',
'America/Dominica',
'America/Edmonton',
'America/Eirunepe',
'America/El_Salvador',
'America/Ensenada',
'America/Fort_Nelson',
'America/Fort_Wayne',
'America/Fortaleza',
'America/Glace_Bay',
'America/Godthab',
'America/Goose_Bay',
'America/Grand_Turk',
'America/Grenada',
'America/Guadeloupe',
'America/Guatemala',
'America/Guayaquil',
'America/Guyana',
'America/Halifax',
'America/Havana',
'America/Hermosillo',
'America/Indiana/Indianapolis',
'America/Indiana/Knox',
'America/Indiana/Marengo',
'America/Indiana/Petersburg',
'America/Indiana/Tell_City',
'America/Indiana/Vevay',
'America/Indiana/Vincennes',
'America/Indiana/Winamac',
'America/Indianapolis',
'America/Inuvik',
'America/Iqaluit',
'America/Jamaica',
'America/Jujuy',
'America/Juneau',
'America/Kentucky/Louisville',
'America/Kentucky/Monticello',
'America/Knox_IN',
'America/Kralendijk',
'America/La_Paz',
'America/Lima',
'America/Los_Angeles',
'America/Louisville',
'America/Lower_Princes',
'America/Maceio',
'America/Managua',
'America/Manaus',
'America/Marigot',
'America/Martinique',
'America/Matamoros',
'America/Mazatlan',
'America/Mendoza',
'America/Menominee',
'America/Merida',
'America/Metlakatla',
'America/Mexico_City',
'America/Miquelon',
'America/Moncton',
'America/Monterrey',
'America/Montevideo',
'America/Montreal',
'America/Montserrat',
'America/Nassau',
'America/New_York',
'America/Nipigon',
'America/Nome',
'America/Noronha',
'America/North_Dakota/Beulah',
'America/North_Dakota/Center',
'America/North_Dakota/New_Salem',
'America/Ojinaga',
'America/Panama',
'America/Pangnirtung',
'America/Paramaribo',
'America/Phoenix',
'America/Port-au-Prince',
'America/Port_of_Spain',
'America/Porto_Acre',
'America/Porto_Velho',
'America/Puerto_Rico',
'America/Punta_Arenas',
'America/Rainy_River',
'America/Rankin_Inlet',
'America/Recife',
'America/Regina',
'America/Resolute',
'America/Rio_Branco',
'America/Rosario',
'America/Santa_Isabel',
'America/Santarem',
'America/Santiago',
'America/Santo_Domingo',
'America/Sao_Paulo',
'America/Scoresbysund',
'America/Shiprock',
'America/Sitka',
'America/St_Barthelemy',
'America/St_Johns',
'America/St_Kitts',
'America/St_Lucia',
'America/St_Thomas',
'America/St_Vincent',
'America/Swift_Current',
'America/Tegucigalpa',
'America/Thule',
'America/Thunder_Bay',
'America/Tijuana',
'America/Toronto',
'America/Tortola',
'America/Vancouver',
'America/Virgin',
'America/Whitehorse',
'America/Winnipeg',
'America/Yakutat',
'America/Yellowknife',
'Antarctica/Casey',
'Antarctica/Davis',
'Antarctica/DumontDUrville',
'Antarctica/Macquarie',
'Antarctica/Mawson',
'Antarctica/McMurdo',
'Antarctica/Palmer',
'Antarctica/Rothera',
'Antarctica/South_Pole',
'Antarctica/Syowa',
'Antarctica/Troll',
'Antarctica/Vostok',
'Arctic/Longyearbyen',
'Asia/Aden',
'Asia/Almaty',
'Asia/Amman',
'Asia/Anadyr',
'Asia/Aqtau',
'Asia/Aqtobe',
'Asia/Ashgabat',
'Asia/Ashkhabad',
'Asia/Atyrau',
'Asia/Baghdad',
'Asia/Bahrain',
'Asia/Baku',
'Asia/Bangkok',
'Asia/Barnaul',
'Asia/Beirut',
'Asia/Bishkek',
'Asia/Brunei',
'Asia/Calcutta',
'Asia/Chita',
'Asia/Choibalsan',
'Asia/Chongqing',
'Asia/Chungking',
'Asia/Colombo',
'Asia/Dacca',
'Asia/Damascus',
'Asia/Dhaka',
'Asia/Dili',
'Asia/Dubai',
'Asia/Dushanbe',
'Asia/Famagusta',
'Asia/Gaza',
'Asia/Harbin',
'Asia/Hebron',
'Asia/Ho_Chi_Minh',
'Asia/Hong_Kong',
'Asia/Hovd',
'Asia/Irkutsk',
'Asia/Istanbul',
'Asia/Jakarta',
'Asia/Jayapura',
'Asia/Jerusalem',
'Asia/Kabul',
'Asia/Kamchatka',
'Asia/Karachi',
'Asia/Kashgar',
'Asia/Kathmandu',
'Asia/Katmandu',
'Asia/Khandyga',
'Asia/Kolkata',
'Asia/Krasnoyarsk',
'Asia/Kuala_Lumpur',
'Asia/Kuching',
'Asia/Kuwait',
'Asia/Macao',
'Asia/Macau',
'Asia/Magadan',
'Asia/Makassar',
'Asia/Manila',
'Asia/Muscat',
'Asia/Nicosia',
'Asia/Novokuznetsk',
'Asia/Novosibirsk',
'Asia/Omsk',
'Asia/Oral',
'Asia/Phnom_Penh',
'Asia/Pontianak',
'Asia/Pyongyang',
'Asia/Qatar',
'Asia/Qyzylorda',
'Asia/Rangoon',
'Asia/Riyadh',
'Asia/Saigon',
'Asia/Sakhalin',
'Asia/Samarkand',
'Asia/Seoul',
'Asia/Shanghai',
'Asia/Singapore',
'Asia/Srednekolymsk',
'Asia/Taipei',
'Asia/Tashkent',
'Asia/Tbilisi',
'Asia/Tehran',
'Asia/Tel_Aviv',
'Asia/Thimbu',
'Asia/Thimphu',
'Asia/Tokyo',
'Asia/Tomsk',
'Asia/Ujung_Pandang',
'Asia/Ulaanbaatar',
'Asia/Ulan_Bator',
'Asia/Urumqi',
'Asia/Ust-Nera',
'Asia/Vientiane',
'Asia/Vladivostok',
'Asia/Yakutsk',
'Asia/Yangon',
'Asia/Yekaterinburg',
'Asia/Yerevan',
'Atlantic/Azores',
'Atlantic/Bermuda',
'Atlantic/Canary',
'Atlantic/Cape_Verde',
'Atlantic/Faeroe',
'Atlantic/Faroe',
'Atlantic/Jan_Mayen',
'Atlantic/Madeira',
'Atlantic/Reykjavik',
'Atlantic/South_Georgia',
'Atlantic/St_Helena',
'Atlantic/Stanley',
'Australia/ACT',
'Australia/Adelaide',
'Australia/Brisbane',
'Australia/Broken_Hill',
'Australia/Canberra',
'Australia/Currie',
'Australia/Darwin',
'Australia/Eucla',
'Australia/Hobart',
'Australia/LHI',
'Australia/Lindeman',
'Australia/Lord_Howe',
'Australia/Melbourne',
'Australia/NSW',
'Australia/North',
'Australia/Perth',
'Australia/Queensland',
'Australia/South',
'Australia/Sydney',
'Australia/Tasmania',
'Australia/Victoria',
'Australia/West',
'Australia/Yancowinna',
'Brazil/Acre',
'Brazil/DeNoronha',
'Brazil/East',
'Brazil/West',
'CET',
'CST6CDT',
'Canada/Atlantic',
'Canada/Central',
'Canada/Eastern',
'Canada/Mountain',
'Canada/Newfoundland',
'Canada/Pacific',
'Canada/Saskatchewan',
'Canada/Yukon',
'Chile/Continental',
'Chile/EasterIsland',
'Cuba',
'EET',
'EST',
'EST5EDT',
'Egypt',
'Eire',
'Etc/GMT',
'Etc/GMT+0',
'Etc/GMT+1',
'Etc/GMT+10',
'Etc/GMT+11',
'Etc/GMT+12',
'Etc/GMT+2',
'Etc/GMT+3',
'Etc/GMT+4',
'Etc/GMT+5',
'Etc/GMT+6',
'Etc/GMT+7',
'Etc/GMT+8',
'Etc/GMT+9',
'Etc/GMT-0',
'Etc/GMT-1',
'Etc/GMT-10',
'Etc/GMT-11',
'Etc/GMT-12',
'Etc/GMT-13',
'Etc/GMT-14',
'Etc/GMT-2',
'Etc/GMT-3',
'Etc/GMT-4',
'Etc/GMT-5',
'Etc/GMT-6',
'Etc/GMT-7',
'Etc/GMT-8',
'Etc/GMT-9',
'Etc/GMT0',
'Etc/Greenwich',
'Etc/UCT',
'Etc/UTC',
'Etc/Universal',
'Etc/Zulu',
'Europe/Amsterdam',
'Europe/Andorra',
'Europe/Astrakhan',
'Europe/Athens',
'Europe/Belfast',
'Europe/Belgrade',
'Europe/Berlin',
'Europe/Bratislava',
'Europe/Brussels',
'Europe/Bucharest',
'Europe/Budapest',
'Europe/Busingen',
'Europe/Chisinau',
'Europe/Copenhagen',
'Europe/Dublin',
'Europe/Gibraltar',
'Europe/Guernsey',
'Europe/Helsinki',
'Europe/Isle_of_Man',
'Europe/Istanbul',
'Europe/Jersey',
'Europe/Kaliningrad',
'Europe/Kiev',
'Europe/Kirov',
'Europe/Lisbon',
'Europe/Ljubljana',
'Europe/London',
'Europe/Luxembourg',
'Europe/Madrid',
'Europe/Malta',
'Europe/Mariehamn',
'Europe/Minsk',
'Europe/Monaco',
'Europe/Moscow',
'Europe/Nicosia',
'Europe/Oslo',
'Europe/Paris',
'Europe/Podgorica',
'Europe/Prague',
'Europe/Riga',
'Europe/Rome',
'Europe/Samara',
'Europe/San_Marino',
'Europe/Sarajevo',
'Europe/Saratov',
'Europe/Simferopol',
'Europe/Skopje',
'Europe/Sofia',
'Europe/Stockholm',
'Europe/Tallinn',
'Europe/Tirane',
'Europe/Tiraspol',
'Europe/Ulyanovsk',
'Europe/Uzhgorod',
'Europe/Vaduz',
'Europe/Vatican',
'Europe/Vienna',
'Europe/Vilnius',
'Europe/Volgograd',
'Europe/Warsaw',
'Europe/Zagreb',
'Europe/Zaporozhye',
'Europe/Zurich',
'GB',
'GB-Eire',
'GMT',
'GMT+0',
'GMT-0',
'GMT0',
'Greenwich',
'HST',
'Hongkong',
'Iceland',
'Indian/Antananarivo',
'Indian/Chagos',
'Indian/Christmas',
'Indian/Cocos',
'Indian/Comoro',
'Indian/Kerguelen',
'Indian/Mahe',
'Indian/Maldives',
'Indian/Mauritius',
'Indian/Mayotte',
'Indian/Reunion',
'Iran',
'Israel',
'Jamaica',
'Japan',
'Kwajalein',
'Libya',
'MET',
'MST',
'MST7MDT',
'Mexico/BajaNorte',
'Mexico/BajaSur',
'Mexico/General',
'NZ',
'NZ-CHAT',
'Navajo',
'PRC',
'PST8PDT',
'Pacific/Apia',
'Pacific/Auckland',
'Pacific/Bougainville',
'Pacific/Chatham',
'Pacific/Chuuk',
'Pacific/Easter',
'Pacific/Efate',
'Pacific/Enderbury',
'Pacific/Fakaofo',
'Pacific/Fiji',
'Pacific/Funafuti',
'Pacific/Galapagos',
'Pacific/Gambier',
'Pacific/Guadalcanal',
'Pacific/Guam',
'Pacific/Honolulu',
'Pacific/Johnston',
'Pacific/Kiritimati',
'Pacific/Kosrae',
'Pacific/Kwajalein',
'Pacific/Majuro',
'Pacific/Marquesas',
'Pacific/Midway',
'Pacific/Nauru',
'Pacific/Niue',
'Pacific/Norfolk',
'Pacific/Noumea',
'Pacific/Pago_Pago',
'Pacific/Palau',
'Pacific/Pitcairn',
'Pacific/Pohnpei',
'Pacific/Ponape',
'Pacific/Port_Moresby',
'Pacific/Rarotonga',
'Pacific/Saipan',
'Pacific/Samoa',
'Pacific/Tahiti',
'Pacific/Tarawa',
'Pacific/Tongatapu',
'Pacific/Truk',
'Pacific/Wake',
'Pacific/Wallis',
'Pacific/Yap',
'Poland',
'Portugal',
'ROC',
'ROK',
'Singapore',
'Turkey',
'UCT',
'US/Alaska',
'US/Aleutian',
'US/Arizona',
'US/Central',
'US/East-Indiana',
'US/Eastern',
'US/Hawaii',
'US/Indiana-Starke',
'US/Michigan',
'US/Mountain',
'US/Pacific',
'US/Pacific-New',
'US/Samoa',
'UTC',
'Universal',
'W-SU',
'WET',
'Zulu'
]

103
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/import-modal.tsx

@ -0,0 +1,103 @@
/*
* 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, toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import Modal from '@/components/modal'
import { useForm } from './use-form'
import { useModal } from './use-modal'
import { NForm, NFormItem, NButton, NUpload, NIcon, NInput } from 'naive-ui'
import { CloudUploadOutlined } from '@vicons/antd'
const props = {
show: {
type: Boolean as PropType<boolean>,
default: false
}
}
export default defineComponent({
name: 'workflowDefinitionImport',
props,
emits: ['update:show', 'update:row', 'updateList'],
setup(props, ctx) {
const { importState } = useForm()
const { handleImportDefinition } = useModal(importState, ctx)
const hideModal = () => {
ctx.emit('update:show')
}
const handleImport = () => {
handleImportDefinition()
}
const customRequest = ({ file }: any) => {
importState.importForm.name = file.name
importState.importForm.file = file.file
}
return {
hideModal,
handleImport,
customRequest,
...toRefs(importState)
}
},
render() {
const { t } = useI18n()
return (
<Modal
show={this.$props.show}
title={t('project.workflow.upload')}
onCancel={this.hideModal}
onConfirm={this.handleImport}
>
<NForm
rules={this.importRules}
ref='importFormRef'
label-placement='left'
label-width='160'
>
<NFormItem label={t('project.workflow.upload_file')} path='file'>
<NButton>
<NUpload
v-model={[this.importForm.file, 'value']}
customRequest={this.customRequest}
showFileList={false}
>
<NButton text>
<NIcon>
<CloudUploadOutlined />
</NIcon>
</NButton>
</NUpload>
</NButton>
</NFormItem>
<NFormItem label={t('project.workflow.file_name')} path='name'>
<NInput
v-model={[this.importForm.name, 'value']}
placeholder={t('project.workflow.enter_name_tips')}
/>
</NFormItem>
</NForm>
</Modal>
)
}
})

388
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/start-modal.tsx

@ -0,0 +1,388 @@
/*
* 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, toRefs, h, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import Modal from '@/components/modal'
import { useForm } from './use-form'
import { useModal } from './use-modal'
import {
NForm,
NFormItem,
NButton,
NIcon,
NInput,
NSpace,
NRadio,
NRadioGroup,
NSelect,
NSwitch,
NCheckbox,
NDatePicker
} from 'naive-ui'
import {
ArrowDownOutlined,
ArrowUpOutlined,
DeleteOutlined,
PlusCircleOutlined
} from '@vicons/antd'
import { IDefinitionData } from '../types'
import styles from '../index.module.scss'
const props = {
row: {
type: Object as PropType<IDefinitionData>,
default: {}
},
show: {
type: Boolean as PropType<boolean>,
default: false
}
}
export default defineComponent({
name: 'workflowDefinitionStart',
props,
emits: ['update:show', 'update:row', 'updateList'],
setup(props, ctx) {
const parallelismRef = ref(false)
const { t } = useI18n()
const { startState } = useForm()
const {
variables,
handleStartDefinition,
getWorkerGroups,
getAlertGroups,
getEnvironmentList
} = useModal(startState, ctx)
const hideModal = () => {
ctx.emit('update:show')
}
const handleStart = () => {
handleStartDefinition(props.row.code)
}
const generalWarningTypeListOptions = () => [
{
value: 'NONE',
label: t('project.workflow.none_send')
},
{
value: 'SUCCESS',
label: t('project.workflow.success_send')
},
{
value: 'FAILURE',
label: t('project.workflow.failure_send')
},
{
value: 'ALL',
label: t('project.workflow.all_send')
}
]
const generalPriorityList = () => [
{
value: 'HIGHEST',
label: 'HIGHEST',
color: '#ff0000',
icon: ArrowUpOutlined
},
{
value: 'HIGH',
label: 'HIGH',
color: '#ff0000',
icon: ArrowUpOutlined
},
{
value: 'MEDIUM',
label: 'MEDIUM',
color: '#EA7D24',
icon: ArrowUpOutlined
},
{
value: 'LOW',
label: 'LOW',
color: '#2A8734',
icon: ArrowDownOutlined
},
{
value: 'LOWEST',
label: 'LOWEST',
color: '#2A8734',
icon: ArrowDownOutlined
}
]
const renderLabel = (option: any) => {
return [
h(
NIcon,
{
style: {
verticalAlign: 'middle',
marginRight: '4px',
marginBottom: '3px'
},
color: option.color
},
{
default: () => h(option.icon)
}
),
option.label
]
}
const updateWorkerGroup = () => {
startState.startForm.environmentCode = null
}
const addStartParams = () => {
variables.startParamsList.push({
prop: '',
value: ''
})
}
const updateParamsList = (index: number, param: Array<string>) => {
variables.startParamsList[index].prop = param[0]
variables.startParamsList[index].value = param[1]
}
const removeStartParams = (index: number) => {
variables.startParamsList.splice(index, 1)
}
onMounted(() => {
getWorkerGroups()
getAlertGroups()
getEnvironmentList()
})
return {
t,
parallelismRef,
hideModal,
handleStart,
generalWarningTypeListOptions,
generalPriorityList,
renderLabel,
updateWorkerGroup,
removeStartParams,
addStartParams,
updateParamsList,
...toRefs(variables),
...toRefs(startState),
...toRefs(props)
}
},
render() {
const { t } = this
return (
<Modal
show={this.show}
title={t('project.workflow.set_parameters_before_starting')}
onCancel={this.hideModal}
onConfirm={this.handleStart}
>
<NForm ref='startFormRef' label-placement='left' label-width='160'>
<NFormItem
label={t('project.workflow.workflow_name')}
path='workflow_name'
>
{this.row.name}
</NFormItem>
<NFormItem
label={t('project.workflow.failure_strategy')}
path='failureStrategy'
>
<NRadioGroup v-model:value={this.startForm.failureStrategy}>
<NSpace>
<NRadio value='CONTINUE'>
{t('project.workflow.continue')}
</NRadio>
<NRadio value='END'>{t('project.workflow.end')}</NRadio>
</NSpace>
</NRadioGroup>
</NFormItem>
<NFormItem
label={t('project.workflow.notification_strategy')}
path='warningType'
>
<NSelect
options={this.generalWarningTypeListOptions()}
v-model:value={this.startForm.warningType}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.workflow_priority')}
path='processInstancePriority'
>
<NSelect
options={this.generalPriorityList()}
renderLabel={this.renderLabel}
v-model:value={this.startForm.processInstancePriority}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.worker_group')}
path='workerGroup'
>
<NSelect
options={this.workerGroups}
onUpdateValue={this.updateWorkerGroup}
v-model:value={this.startForm.workerGroup}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.environment_name')}
path='environmentCode'
>
<NSelect
options={this.environmentList.filter((item: any) =>
item.workerGroups?.includes(this.startForm.workerGroup)
)}
v-model:value={this.startForm.environmentCode}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.alarm_group')}
path='warningGroupId'
>
<NSelect
options={this.alertGroups}
placeholder={t('project.workflow.please_choose')}
v-model:value={this.startForm.warningGroupId}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.complement_data')}
path='complement_data'
>
<NCheckbox
checkedValue={'COMPLEMENT_DATA'}
uncheckedValue={undefined}
v-model:checked={this.startForm.execType}
>
{t('project.workflow.whether_complement_data')}
</NCheckbox>
</NFormItem>
{this.startForm.execType && (
<NSpace>
<NFormItem
label={t('project.workflow.mode_of_execution')}
path='runMode'
>
<NRadioGroup v-model:value={this.startForm.runMode}>
<NSpace>
<NRadio value={'RUN_MODE_SERIAL'}>
{t('project.workflow.serial_execution')}
</NRadio>
<NRadio value={'RUN_MODE_PARALLEL'}>
{t('project.workflow.parallel_execution')}
</NRadio>
</NSpace>
</NRadioGroup>
</NFormItem>
{this.startForm.runMode === 'RUN_MODE_PARALLEL' && (
<NFormItem
label={t('project.workflow.parallelism')}
path='expectedParallelismNumber'
>
<NCheckbox v-model:checked={this.parallelismRef}>
{t('project.workflow.custom_parallelism')}
</NCheckbox>
<NInput
disabled={!this.parallelismRef}
placeholder={t('project.workflow.please_enter_parallelism')}
v-model:value={this.startForm.expectedParallelismNumber}
/>
</NFormItem>
)}
<NFormItem
label={t('project.workflow.schedule_date')}
path='startEndTime'
>
<NDatePicker
type='datetimerange'
clearable
v-model:value={this.startForm.startEndTime}
/>
</NFormItem>
</NSpace>
)}
<NFormItem
label={t('project.workflow.startup_parameter')}
path='startup_parameter'
>
{this.startParamsList.length === 0 ? (
<NButton text type='primary' onClick={this.addStartParams}>
<NIcon>
<PlusCircleOutlined />
</NIcon>
</NButton>
) : (
<NSpace vertical>
{this.startParamsList.map((item, index) => (
<NSpace class={styles.startup} key={index}>
<NInput
pair
separator=':'
placeholder={['prop', 'value']}
onUpdateValue={(param) =>
this.updateParamsList(index, param)
}
/>
<NButton
text
type='error'
onClick={() => this.removeStartParams(index)}
>
<NIcon>
<DeleteOutlined />
</NIcon>
</NButton>
<NButton text type='primary' onClick={this.addStartParams}>
<NIcon>
<PlusCircleOutlined />
</NIcon>
</NButton>
</NSpace>
))}
</NSpace>
)}
</NFormItem>
<NFormItem
label={t('project.workflow.whether_dry_run')}
path='dryRun'
>
<NSwitch
checkedValue={1}
uncheckedValue={0}
v-model:value={this.startForm.dryRun}
/>
</NFormItem>
</NForm>
</Modal>
)
}
})

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

@ -0,0 +1,309 @@
/*
* 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, toRefs } from 'vue'
import { NSpace, NTooltip, NButton, NIcon, NPopconfirm } from 'naive-ui'
import {
DeleteOutlined,
DownloadOutlined,
FormOutlined,
InfoCircleFilled,
PlayCircleOutlined,
ClockCircleOutlined,
CopyOutlined,
FieldTimeOutlined,
ExportOutlined,
ApartmentOutlined,
UploadOutlined
} from '@vicons/antd'
import { useI18n } from 'vue-i18n'
import { IDefinitionData } from '../types'
const props = {
row: {
type: Object as PropType<IDefinitionData>
}
}
export default defineComponent({
name: 'TableAction',
props,
emits: [
'updateList',
'startWorkflow',
'timingWorkflow',
'versionWorkflow',
'deleteWorkflow',
'releaseWorkflow',
'copyWorkflow',
'exportWorkflow',
'gotoTimingManage'
],
setup(props, ctx) {
const handleStartWorkflow = () => {
ctx.emit('startWorkflow')
}
const handleTimingWorkflow = () => {
ctx.emit('timingWorkflow')
}
const handleVersionWorkflow = () => {
ctx.emit('versionWorkflow')
}
const handleDeleteWorkflow = () => {
ctx.emit('deleteWorkflow')
}
const handleReleaseWorkflow = () => {
ctx.emit('releaseWorkflow')
}
const handleCopyWorkflow = () => {
ctx.emit('copyWorkflow')
}
const handleExportWorkflow = () => {
ctx.emit('exportWorkflow')
}
const handleGotoTimingManage = () => {
ctx.emit('gotoTimingManage')
}
return {
handleStartWorkflow,
handleTimingWorkflow,
handleVersionWorkflow,
handleDeleteWorkflow,
handleReleaseWorkflow,
handleCopyWorkflow,
handleExportWorkflow,
handleGotoTimingManage,
...toRefs(props)
}
},
render() {
const { t } = useI18n()
const releaseState = this.row?.releaseState
const scheduleReleaseState = this.row?.scheduleReleaseState
return (
<NSpace>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.edit'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
disabled={releaseState === 'ONLINE'}
/* TODO: Edit workflow */
>
<NIcon>
<FormOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.start'),
trigger: () => (
<NButton
size='tiny'
type='primary'
circle
onClick={this.handleStartWorkflow}
disabled={releaseState === 'OFFLINE'}
>
<NIcon>
<PlayCircleOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.timing'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
onClick={this.handleTimingWorkflow}
disabled={releaseState !== 'ONLINE' || !!scheduleReleaseState}
>
<NIcon>
<ClockCircleOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.up_line'),
trigger: () => (
<NButton
size='tiny'
type={releaseState === 'ONLINE' ? 'warning' : 'error'}
circle
onClick={this.handleReleaseWorkflow}
>
<NIcon>
{releaseState === 'ONLINE' ? (
<DownloadOutlined />
) : (
<UploadOutlined />
)}
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.copy_workflow'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
disabled={releaseState === 'ONLINE'}
onClick={this.handleCopyWorkflow}
>
<NIcon>
<CopyOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.cron_manage'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
disabled={releaseState === 'OFFLINE'}
onClick={this.handleGotoTimingManage}
>
<NIcon>
<FieldTimeOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.delete'),
trigger: () => (
<NButton
size='tiny'
type='error'
circle
disabled={releaseState === 'ONLINE'}
>
<NPopconfirm onPositiveClick={this.handleDeleteWorkflow}>
{{
default: () => t('project.workflow.delete_confirm'),
icon: () => (
<NIcon>
<InfoCircleFilled />
</NIcon>
),
trigger: () => (
<NIcon>
<DeleteOutlined />
</NIcon>
)
}}
</NPopconfirm>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.tree_view'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
/* TODO: Goto tree view*/
>
<NIcon>
<ApartmentOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.export'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
onClick={this.handleExportWorkflow}
>
<NIcon>
<ExportOutlined />
</NIcon>
</NButton>
)
}}
</NTooltip>
<NTooltip trigger={'hover'}>
{{
default: () => t('project.workflow.version_info'),
trigger: () => (
<NButton
size='tiny'
type='info'
tag='div'
circle
onClick={this.handleVersionWorkflow}
>
<NIcon>
<InfoCircleFilled />
</NIcon>
</NButton>
)
}}
</NTooltip>
</NSpace>
)
}
})

321
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/timing-modal.tsx

@ -0,0 +1,321 @@
/*
* 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, toRefs, h, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import Modal from '@/components/modal'
import { useForm } from './use-form'
import { useModal } from './use-modal'
import {
NForm,
NFormItem,
NButton,
NIcon,
NInput,
NSpace,
NRadio,
NRadioGroup,
NSelect,
NDatePicker,
NInputGroup,
NList,
NListItem,
NThing
} from 'naive-ui'
import { ArrowDownOutlined, ArrowUpOutlined } from '@vicons/antd'
import { timezoneList } from '@/utils/timezone'
const props = {
row: {
type: Object,
default: {}
},
show: {
type: Boolean as PropType<boolean>,
default: false
},
type: {
type: String as PropType<String>,
default: 'create'
}
}
export default defineComponent({
name: 'workflowDefinitionStart',
props,
emits: ['update:show', 'update:row', 'updateList'],
setup(props, ctx) {
const parallelismRef = ref(false)
const { t } = useI18n()
const { timingState } = useForm()
const {
variables,
handleCreateTiming,
handleUpdateTiming,
getWorkerGroups,
getAlertGroups,
getEnvironmentList,
getPreviewSchedule
} = useModal(timingState, ctx)
const hideModal = () => {
ctx.emit('update:show')
}
const handleTiming = () => {
if (props.type === 'create') {
handleCreateTiming(props.row.code as number)
} else {
handleUpdateTiming(props.row.id)
}
}
const generalWarningTypeListOptions = () => [
{
value: 'NONE',
label: t('project.workflow.none_send')
},
{
value: 'SUCCESS',
label: t('project.workflow.success_send')
},
{
value: 'FAILURE',
label: t('project.workflow.failure_send')
},
{
value: 'ALL',
label: t('project.workflow.all_send')
}
]
const generalPriorityList = () => [
{
value: 'HIGHEST',
label: 'HIGHEST',
color: '#ff0000',
icon: ArrowUpOutlined
},
{
value: 'HIGH',
label: 'HIGH',
color: '#ff0000',
icon: ArrowUpOutlined
},
{
value: 'MEDIUM',
label: 'MEDIUM',
color: '#EA7D24',
icon: ArrowUpOutlined
},
{
value: 'LOW',
label: 'LOW',
color: '#2A8734',
icon: ArrowDownOutlined
},
{
value: 'LOWEST',
label: 'LOWEST',
color: '#2A8734',
icon: ArrowDownOutlined
}
]
const timezoneOptions = () =>
timezoneList.map((item) => ({ label: item, value: item }))
const renderLabel = (option: any) => {
return [
h(
NIcon,
{
style: {
verticalAlign: 'middle',
marginRight: '4px',
marginBottom: '3px'
},
color: option.color
},
{
default: () => h(option.icon)
}
),
option.label
]
}
const updateWorkerGroup = () => {
timingState.timingForm.environmentCode = null
}
const handlePreview = () => {
getPreviewSchedule()
}
onMounted(() => {
getWorkerGroups()
getAlertGroups()
getEnvironmentList()
})
return {
t,
parallelismRef,
hideModal,
handleTiming,
generalWarningTypeListOptions,
generalPriorityList,
timezoneOptions,
renderLabel,
updateWorkerGroup,
handlePreview,
...toRefs(variables),
...toRefs(timingState),
...toRefs(props)
}
},
render() {
const { t } = this
if (Number(this.timingForm.warningGroupId) === 0) {
this.timingForm.warningGroupId = ''
}
return (
<Modal
show={this.show}
title={t('project.workflow.set_parameters_before_timing')}
onCancel={this.hideModal}
onConfirm={this.handleTiming}
>
<NForm ref='timingFormRef' label-placement='left' label-width='160'>
<NFormItem
label={t('project.workflow.start_and_stop_time')}
path='startEndTime'
>
<NDatePicker
type='datetimerange'
clearable
v-model:value={this.timingForm.startEndTime}
/>
</NFormItem>
<NFormItem label={t('project.workflow.timing')} path='crontab'>
<NInputGroup>
<NInput
style={{ width: '80%' }}
v-model:value={this.timingForm.crontab}
></NInput>
<NButton type='primary' ghost onClick={this.handlePreview}>
{t('project.workflow.execute_time')}
</NButton>
</NInputGroup>
</NFormItem>
<NFormItem
label={t('project.workflow.timezone')}
path='timezoneId'
showFeedback={false}
>
<NSelect
v-model:value={this.timingForm.timezoneId}
options={this.timezoneOptions()}
/>
</NFormItem>
<NFormItem label=' ' showFeedback={false}>
<NList>
<NListItem>
<NThing
description={t('project.workflow.next_five_execution_times')}
>
{this.schedulePreviewList.map((item: string) => (
<NSpace>
{item}
<br />
</NSpace>
))}
</NThing>
</NListItem>
</NList>
</NFormItem>
<NFormItem
label={t('project.workflow.failure_strategy')}
path='failureStrategy'
>
<NRadioGroup v-model:value={this.timingForm.failureStrategy}>
<NSpace>
<NRadio value='CONTINUE'>
{t('project.workflow.continue')}
</NRadio>
<NRadio value='END'>{t('project.workflow.end')}</NRadio>
</NSpace>
</NRadioGroup>
</NFormItem>
<NFormItem
label={t('project.workflow.notification_strategy')}
path='warningType'
>
<NSelect
options={this.generalWarningTypeListOptions()}
v-model:value={this.timingForm.warningType}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.workflow_priority')}
path='processInstancePriority'
>
<NSelect
options={this.generalPriorityList()}
renderLabel={this.renderLabel}
v-model:value={this.timingForm.processInstancePriority}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.worker_group')}
path='workerGroup'
>
<NSelect
options={this.workerGroups}
onUpdateValue={this.updateWorkerGroup}
v-model:value={this.timingForm.workerGroup}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.environment_name')}
path='environmentCode'
>
<NSelect
options={this.environmentList.filter((item: any) =>
item.workerGroups?.includes(this.timingForm.workerGroup)
)}
v-model:value={this.timingForm.environmentCode}
/>
</NFormItem>
<NFormItem
label={t('project.workflow.alarm_group')}
path='warningGroupId'
>
<NSelect
options={this.alertGroups}
placeholder={t('project.workflow.please_choose')}
v-model:value={this.timingForm.warningGroupId}
/>
</NFormItem>
</NForm>
</Modal>
)
}
})

92
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-form.ts

@ -0,0 +1,92 @@
/*
* 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 { useI18n } from 'vue-i18n'
import type { FormRules } from 'naive-ui'
export const useForm = () => {
const { t } = useI18n()
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth()
const day = date.getDate()
const importState = reactive({
importFormRef: ref(),
importForm: {
name: '',
file: ''
},
importRules: {
file: {
required: true,
trigger: ['input', 'blur'],
validator() {
if (importState.importForm.name === '') {
return new Error(t('project.workflow.enter_name_tips'))
}
}
}
} as FormRules
})
const startState = reactive({
startFormRef: ref(),
startForm: {
processDefinitionCode: -1,
startEndTime: [new Date(year, month, day), new Date(year, month, day)],
scheduleTime: null,
failureStrategy: 'CONTINUE',
warningType: 'NONE',
warningGroupId: null,
execType: '',
startNodeList: '',
taskDependType: 'TASK_POST',
runMode: 'RUN_MODE_SERIAL',
processInstancePriority: 'MEDIUM',
workerGroup: 'default',
environmentCode: null,
startParams: null,
expectedParallelismNumber: '',
dryRun: 0
}
})
const timingState = reactive({
timingFormRef: ref(),
timingForm: {
startEndTime: [
new Date(year, month, day),
new Date(year + 100, month, day)
],
crontab: '0 0 * * * ? *',
timezoneId: Intl.DateTimeFormat().resolvedOptions().timeZone,
failureStrategy: 'CONTINUE',
warningType: 'NONE',
processInstancePriority: 'MEDIUM',
warningGroupId: '',
workerGroup: 'default',
environmentCode: null
}
})
return {
importState,
startState,
timingState
}
}

247
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/use-modal.ts

@ -0,0 +1,247 @@
/*
* 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 _ from 'lodash'
import { reactive, SetupContext } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import type { Router } from 'vue-router'
import { format } from 'date-fns'
import { importProcessDefinition } from '@/service/modules/process-definition'
import { queryAllWorkerGroups } from '@/service/modules/worker-groups'
import { queryAllEnvironmentList } from '@/service/modules/environment'
import { listAlertGroupById } from '@/service/modules/alert-group'
import { startProcessInstance } from '@/service/modules/executors'
import {
createSchedule,
updateSchedule,
previewSchedule
} from '@/service/modules/schedules'
export function useModal(
state: any,
ctx: SetupContext<('update:show' | 'update:row' | 'updateList')[]>
) {
const { t } = useI18n()
const router: Router = useRouter()
const variables = reactive({
projectCode: Number(router.currentRoute.value.params.projectCode),
workerGroups: [],
alertGroups: [],
environmentList: [],
startParamsList: [] as Array<{ prop: string; value: string }>,
schedulePreviewList: []
})
const resetImportForm = () => {
state.importFormRef.name = ''
state.importFormRef.file = ''
}
const handleImportDefinition = () => {
state.importFormRef.validate(async (valid: any) => {
if (!valid) {
try {
const formData = new FormData()
formData.append('file', state.importForm.file)
const code = Number(router.currentRoute.value.params.projectCode)
await importProcessDefinition(formData, code)
window.$message.success(t('project.workflow.success'))
ctx.emit('updateList')
ctx.emit('update:show')
resetImportForm()
} catch (error: any) {
window.$message.error(error.message)
}
}
})
}
const handleStartDefinition = (code: number) => {
state.startFormRef.validate(async (valid: any) => {
if (!valid) {
state.startForm.processDefinitionCode = code
if (state.startForm.startEndTime) {
const start = format(
new Date(state.startForm.startEndTime[0]),
'yyyy-MM-dd hh:mm:ss'
)
const end = format(
new Date(state.startForm.startEndTime[1]),
'yyyy-MM-dd hh:mm:ss'
)
state.startForm.scheduleTime = `${start},${end}`
}
const startParams = {} as any
for (const item of variables.startParamsList) {
if (item.value !== '') {
startParams[item.prop] = item.value
}
}
state.startForm.startParams = !_.isEmpty(startParams)
? JSON.stringify(startParams)
: ''
try {
await startProcessInstance(state.startForm, variables.projectCode)
window.$message.success(t('project.workflow.success'))
ctx.emit('updateList')
ctx.emit('update:show')
} catch (error: any) {
window.$message.error(error.message)
}
}
})
}
const handleCreateTiming = (code: number) => {
state.timingFormRef.validate(async (valid: any) => {
if (!valid) {
const data: any = getTimingData()
data.processDefinitionCode = code
try {
await createSchedule(data, variables.projectCode)
window.$message.success(t('project.workflow.success'))
ctx.emit('updateList')
ctx.emit('update:show')
} catch (error: any) {
window.$message.error(error.message)
}
}
})
}
const handleUpdateTiming = (id: number) => {
state.timingFormRef.validate(async (valid: any) => {
if (!valid) {
const data: any = getTimingData()
data.id = id
try {
await updateSchedule(data, variables.projectCode, id)
window.$message.success(t('project.workflow.success'))
ctx.emit('updateList')
ctx.emit('update:show')
} catch (error: any) {
window.$message.error(error.message)
}
}
})
}
const getTimingData = () => {
const start = format(
new Date(state.timingForm.startEndTime[0]),
'yyyy-MM-dd hh:mm:ss'
)
const end = format(
new Date(state.timingForm.startEndTime[1]),
'yyyy-MM-dd hh:mm:ss'
)
const data = {
schedule: JSON.stringify({
startTime: start,
endTime: end,
crontab: state.timingForm.crontab
}),
failureStrategy: state.timingForm.failureStrategy,
warningType: state.timingForm.warningType,
processInstancePriority: state.timingForm.processInstancePriority,
warningGroupId:
state.timingForm.warningGroupId === ''
? 0
: state.timingForm.warningGroupId,
workerGroup: state.timingForm.workerGroups,
environmentCode: state.timingForm.environmentCode
}
return data
}
const getWorkerGroups = () => {
queryAllWorkerGroups().then((res: any) => {
variables.workerGroups = res.map((item: string) => ({
label: item,
value: item
}))
})
}
const getEnvironmentList = () => {
queryAllEnvironmentList().then((res: any) => {
variables.environmentList = res.map((item: any) => ({
label: item.name,
value: item.code,
workerGroups: item.workerGroups
}))
})
}
const getAlertGroups = () => {
listAlertGroupById().then((res: any) => {
variables.alertGroups = res.map((item: any) => ({
label: item.groupName,
value: item.id
}))
})
}
const getPreviewSchedule = () => {
state.timingFormRef.validate(async (valid: any) => {
if (!valid) {
const projectCode = Number(router.currentRoute.value.params.projectCode)
const start = format(
new Date(state.timingForm.startEndTime[0]),
'yyyy-MM-dd hh:mm:ss'
)
const end = format(
new Date(state.timingForm.startEndTime[1]),
'yyyy-MM-dd hh:mm:ss'
)
const schedule = JSON.stringify({
startTime: start,
endTime: end,
crontab: state.timingForm.crontab
})
previewSchedule({ schedule }, projectCode)
.then((res: any) => {
variables.schedulePreviewList = res
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
})
}
return {
variables,
handleImportDefinition,
handleStartDefinition,
handleCreateTiming,
handleUpdateTiming,
getWorkerGroups,
getAlertGroups,
getEnvironmentList,
getPreviewSchedule
}
}

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

@ -0,0 +1,198 @@
/*
* 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 { h, ref, reactive, SetupContext } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { NSpace, NTooltip, NButton, NPopconfirm, NTag } from 'naive-ui'
import {
deleteVersion,
queryVersions,
switchVersion
} from '@/service/modules/process-definition'
import type { Router } from 'vue-router'
import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
import { DeleteOutlined, ExclamationCircleOutlined } from '@vicons/antd'
import styles from '../index.module.scss'
export function useTable(
ctx: SetupContext<('update:show' | 'update:row' | 'updateList')[]>
) {
const { t } = useI18n()
const router: Router = useRouter()
const columns: TableColumns<any> = [
{
title: t('project.workflow.id'),
key: 'id',
width: 50,
render: (_row, index) => index + 1
},
{
title: t('project.workflow.version'),
key: 'version',
render: (_row) => {
if (_row.version === variables.row.version) {
return h(
NTag,
{ type: 'success', size: 'small' },
{
default: () =>
`V${_row.version} ${t('project.workflow.current_version')}`
}
)
} else {
return `V${_row.version}`
}
}
},
{
title: t('project.workflow.description'),
key: 'description'
},
{
title: t('project.workflow.create_time'),
key: 'createTime'
},
{
title: t('project.workflow.operation'),
key: 'operation',
className: styles.operation,
render: (_row) => {
return h(NSpace, null, {
default: () => [
h(
NPopconfirm,
{
onPositiveClick: () => {
handleSwitchVersion(_row.version)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'info',
size: 'tiny',
disabled: _row.version === variables.row.version
},
{
icon: () => h(ExclamationCircleOutlined)
}
),
default: () => t('project.workflow.switch_version')
}
),
default: () => t('project.workflow.confirm_switch_version')
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDeleteVersion(_row.version)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'tiny',
disabled: _row.version === variables.row.version
},
{
icon: () => h(DeleteOutlined)
}
),
default: () => t('project.workflow.delete')
}
),
default: () => t('project.workflow.delete_confirm')
}
)
]
})
}
}
]
const variables = reactive({
columns,
row: {} as any,
tableData: [],
projectCode: ref(Number(router.currentRoute.value.params.projectCode))
})
const getTableData = (row: any) => {
variables.row = row
const params = {
pageSize: 10,
pageNo: 1
}
queryVersions(
{ ...params },
variables.projectCode,
variables.row.code
).then((res: any) => {
variables.tableData = res.totalList.map((item: any) => ({ ...item }))
})
}
const handleSwitchVersion = (version: number) => {
switchVersion(variables.projectCode, variables.row.code, version)
.then(() => {
window.$message.success(t('project.workflow.success'))
ctx.emit('updateList')
getTableData(variables.row)
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
const handleDeleteVersion = (version: number) => {
deleteVersion(variables.projectCode, variables.row.code, version)
.then(() => {
window.$message.success(t('project.workflow.success'))
ctx.emit('updateList')
getTableData(variables.row)
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
return {
variables,
getTableData
}
}

95
dolphinscheduler-ui-next/src/views/projects/workflow/definition/components/version-modal.tsx

@ -0,0 +1,95 @@
/*
* 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, toRefs, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { NDataTable } from 'naive-ui'
import Modal from '@/components/modal'
import { useForm } from './use-form'
import { useModal } from './use-modal'
import { useTable } from './use-table'
import { IDefinitionData } from '../types'
import styles from '../index.module.scss'
const props = {
show: {
type: Boolean as PropType<boolean>,
default: false
},
row: {
type: Object as PropType<IDefinitionData>,
default: {}
}
}
export default defineComponent({
name: 'workflowDefinitionVersion',
props,
emits: ['update:show', 'update:row', 'updateList'],
setup(props, ctx) {
const { variables, getTableData } = useTable(ctx)
const { importState } = useForm()
const { handleImportDefinition } = useModal(importState, ctx)
const hideModal = () => {
ctx.emit('update:show')
}
const handleImport = () => {
handleImportDefinition()
}
const customRequest = ({ file }: any) => {
importState.importForm.name = file.name
importState.importForm.file = file.file
}
watch(
() => props.row.code,
() => {
getTableData(props.row)
}
)
return {
hideModal,
handleImport,
customRequest,
...toRefs(variables)
}
},
render() {
const { t } = useI18n()
return (
<Modal
show={this.$props.show}
title={t('project.workflow.version_info')}
onCancel={this.hideModal}
onConfirm={this.hideModal}
>
<NDataTable
columns={this.columns}
data={this.tableData}
striped
size={'small'}
class={styles.table}
/>
</Modal>
)
}
})

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

@ -0,0 +1,88 @@
/*
* 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 {
> .search {
.list {
float: right;
margin: 3px 0 3px 4px;
}
}
}
}
}
.table {
table {
width: 100%;
tr {
height: 40px;
font-size: 12px;
th,
td {
&:nth-child(1) {
width: 50px;
text-align: center;
}
}
th {
&:nth-child(1) {
width: 60px;
text-align: center;
}
> span {
font-size: 12px;
color: #555;
}
}
}
}
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.operation {
> div {
> div {
margin-right: 5px !important;
}
}
}
.startup {
align-items: center;
> div:first-child {
width: 86%;
}
}

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

@ -15,11 +15,139 @@
* limitations under the License. * limitations under the License.
*/ */
import { defineComponent } from 'vue' import Card from '@/components/card'
import { SearchOutlined } from '@vicons/antd'
import {
NButton,
NDataTable,
NIcon,
NInput,
NPagination,
NSpace
} from 'naive-ui'
import { defineComponent, onMounted, toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import { useTable } from './use-table'
import ImportModal from './components/import-modal'
import StartModal from './components/start-modal'
import TimingModal from './components/timing-modal'
import VersionModal from './components/version-modal'
import styles from './index.module.scss'
export default defineComponent({ export default defineComponent({
name: 'WorkflowDefinitionList', name: 'WorkflowDefinitionList',
setup() { setup() {
return () => <div>WorkflowDefinitionList</div> const { variables, getTableData } = useTable()
const requestData = () => {
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
}
const handleUpdateList = () => {
requestData()
}
const handleSearch = () => {
variables.page = 1
requestData()
}
const handleChangePageSize = () => {
variables.page = 1
requestData()
}
onMounted(() => {
requestData()
})
return {
requestData,
handleSearch,
handleUpdateList,
handleChangePageSize,
...toRefs(variables)
}
},
render() {
const { t } = useI18n()
return (
<div class={styles.content}>
<Card class={styles.card}>
<div class={styles.header}>
<NSpace>
<NButton type='primary' /* TODO: Create workflow */>
{t('project.workflow.create_workflow')}
</NButton>
<NButton strong secondary onClick={() => (this.showRef = true)}>
{t('project.workflow.import_workflow')}
</NButton>
</NSpace>
<div class={styles.right}>
<div class={styles.search}>
<div class={styles.list}>
<NButton type='primary' onClick={this.handleSearch}>
<NIcon>
<SearchOutlined />
</NIcon>
</NButton>
</div>
<div class={styles.list}>
<NInput
placeholder={t('resource.function.enter_keyword_tips')}
v-model={[this.searchVal, 'value']}
/>
</div>
</div>
</div>
</div>
</Card>
<Card title={t('project.workflow.workflow_definition')}>
<NDataTable
columns={this.columns}
data={this.tableData}
striped
size={'small'}
class={styles.table}
/>
<div class={styles.pagination}>
<NPagination
v-model:page={this.page}
v-model:page-size={this.pageSize}
page-count={this.totalPage}
show-size-picker
page-sizes={[10, 30, 50]}
show-quick-jumper
onUpdatePage={this.requestData}
onUpdatePageSize={this.handleChangePageSize}
/>
</div>
</Card>
<ImportModal
v-model:show={this.showRef}
onUpdateList={this.handleUpdateList}
/>
<StartModal
v-model:row={this.row}
v-model:show={this.startShowRef}
onUpdateList={this.handleUpdateList}
/>
<TimingModal
v-model:row={this.row}
v-model:show={this.timingShowRef}
onUpdateList={this.handleUpdateList}
/>
<VersionModal
v-model:row={this.row}
v-model:show={this.versionShowRef}
onUpdateList={this.handleUpdateList}
/>
</div>
)
} }
}) })

113
dolphinscheduler-ui-next/src/views/projects/workflow/definition/timing/index.tsx

@ -0,0 +1,113 @@
/*
* 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, NDataTable, NIcon, NPagination } from 'naive-ui'
import { defineComponent, onMounted, toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import type { Router } from 'vue-router'
import { useTable } from './use-table'
import TimingModal from '../components/timing-modal'
import styles from '../index.module.scss'
export default defineComponent({
name: 'WorkflowDefinitionTiming',
setup() {
const { variables, getTableData } = useTable()
const requestData = () => {
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
}
const handleUpdateList = () => {
requestData()
}
const handleSearch = () => {
variables.page = 1
requestData()
}
const handleChangePageSize = () => {
variables.page = 1
requestData()
}
onMounted(() => {
requestData()
})
return {
requestData,
handleSearch,
handleUpdateList,
handleChangePageSize,
...toRefs(variables)
}
},
render() {
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>
</div>
</Card>
<Card title={t('project.workflow.cron_manage')}>
<NDataTable
columns={this.columns}
data={this.tableData}
striped
size={'small'}
class={styles.table}
/>
<div class={styles.pagination}>
<NPagination
v-model:page={this.page}
v-model:page-size={this.pageSize}
page-count={this.totalPage}
show-size-picker
page-sizes={[10, 30, 50]}
show-quick-jumper
onUpdatePage={this.requestData}
onUpdatePageSize={this.handleChangePageSize}
/>
</div>
</Card>
<TimingModal
type={'update'}
v-model:row={this.row}
v-model:show={this.showRef}
onUpdateList={this.handleUpdateList}
/>
</div>
)
}
})

22
dolphinscheduler-ui-next/src/views/projects/workflow/definition/timing/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 ISearchParam {
pageSize: number
pageNo: number
searchVal: string | undefined
}

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

@ -0,0 +1,245 @@
/*
* 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 { h, ref, reactive } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { NSpace, NTooltip, NButton, NPopconfirm, NEllipsis } from 'naive-ui'
import {
deleteScheduleById,
offline,
online,
queryScheduleListPaging
} from '@/service/modules/schedules'
import {
ArrowDownOutlined,
ArrowUpOutlined,
DeleteOutlined,
EditOutlined
} from '@vicons/antd'
import type { Router } from 'vue-router'
import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
import { ISearchParam } from './types'
import styles from '../index.module.scss'
export function useTable() {
const { t } = useI18n()
const router: Router = useRouter()
const columns: TableColumns<any> = [
{
title: t('project.workflow.id'),
key: 'id',
width: 50,
render: (_row, index) => index + 1
},
{
title: t('project.workflow.workflow_name'),
key: 'processDefinitionName',
width: 200,
render: (_row) =>
h(
NEllipsis,
{ style: 'max-width: 200px' },
{
default: () => _row.processDefinitionName
}
)
},
{
title: t('project.workflow.start_time'),
key: 'startTime'
},
{
title: t('project.workflow.end_time'),
key: 'endTime'
},
{
title: t('project.workflow.crontab'),
key: 'crontab'
},
{
title: t('project.workflow.failure_strategy'),
key: 'failureStrategy'
},
{
title: t('project.workflow.status'),
key: 'releaseState',
render: (_row) =>
_row.releaseState === 'ONLINE'
? t('project.workflow.up_line')
: t('project.workflow.down_line')
},
{
title: t('project.workflow.create_time'),
key: 'createTime'
},
{
title: t('project.workflow.update_time'),
key: 'updateTime'
},
{
title: t('project.workflow.operation'),
key: 'operation',
fixed: 'right',
className: styles.operation,
render: (row) => {
return h(NSpace, null, {
default: () => [
h(
NButton,
{
circle: true,
type: 'info',
size: 'tiny',
disabled: row.releaseState === 'ONLINE',
onClick: () => {
handleEdit(row)
}
},
{
icon: () => h(EditOutlined)
}
),
h(
NButton,
{
circle: true,
type: row.releaseState === 'ONLINE' ? 'error' : 'warning',
size: 'tiny',
onClick: () => {
handleReleaseState(row)
}
},
{
icon: () =>
h(
row.releaseState === 'ONLINE'
? ArrowDownOutlined
: ArrowUpOutlined
)
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDelete(row.id)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'tiny'
},
{
icon: () => h(DeleteOutlined)
}
),
default: () => t('project.workflow.delete')
}
),
default: () => t('project.workflow.delete_confirm')
}
)
]
})
}
}
]
const handleEdit = (row: any) => {
variables.showRef = true
variables.row = row
}
const variables = reactive({
columns,
row: {},
tableData: [],
projectCode: ref(Number(router.currentRoute.value.params.projectCode)),
page: ref(1),
pageSize: ref(10),
searchVal: ref(),
totalPage: ref(1),
showRef: ref(false)
})
const getTableData = (params: ISearchParam) => {
const definitionCode = Number(
router.currentRoute.value.params.definitionCode
)
queryScheduleListPaging(
{ ...params, processDefinitionCode: definitionCode },
variables.projectCode
).then((res: any) => {
variables.totalPage = res.totalPage
variables.tableData = res.totalList.map((item: any) => {
return { ...item }
})
})
}
const handleReleaseState = (row: any) => {
let handle = online
if (row.releaseState === 'ONLINE') {
handle = offline
}
handle(variables.projectCode, row.id).then(() => {
window.$message.success(t('project.workflow.success'))
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
})
}
const handleDelete = (id: number) => {
/* after deleting data from the current page, you need to jump forward when the page is empty. */
if (variables.tableData.length === 1 && variables.page > 1) {
variables.page -= 1
}
deleteScheduleById(id, variables.projectCode)
.then(() => {
window.$message.success(t('project.workflow.success'))
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
return {
variables,
getTableData
}
}

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

@ -0,0 +1,72 @@
/*
* 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 IDefinitionParam {
pageSize: number
pageNo: number
searchVal: string | undefined
}
export interface IDefinitionData {
code: number
createTime: string
description: string
executionType: string
flag: 'YES' | 'NO'
globalParamList: any
globalParamMap: Object
globalParams: string
id: number
locations: any
modifyBy: string
name: string
projectCode: number
projectName: any
releaseState: string
scheduleReleaseState: any
tenantCode: any
tenantId: number
timeout: number
updateTime: string
userId: number
userName: string
version: number
warningGroupId: number
}
export interface ICrontabData {
id: number
crontab: string
definitionDescription: string
endTime: string
environmentCode: number
failureStrategy: string
processDefinitionCode: number
processDefinitionName: string
processInstancePriority: string
projectName: string
releaseState: 'ONLINE' | 'OFFLINE'
startTime: string
timezoneId: string
createTime: string
updateTime: string
userId: 1
userName: string
warningGroupId: number
warningType: string
workerGroup: string
}

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

@ -0,0 +1,286 @@
/*
* 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 { h, ref, reactive } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import type { Router } from 'vue-router'
import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
import { useAsyncState } from '@vueuse/core'
import {
batchCopyByCodes,
batchExportByCodes,
deleteByCode,
queryListPaging,
release
} from '@/service/modules/process-definition'
import TableAction from './components/table-action'
import { IDefinitionParam } from './types'
import styles from './index.module.scss'
import { NEllipsis, NTag } from 'naive-ui'
export function useTable() {
const { t } = useI18n()
const router: Router = useRouter()
const columns: TableColumns<any> = [
{
title: t('project.workflow.id'),
key: 'id',
width: 50,
render: (_row, index) => index + 1
},
{
title: t('project.workflow.workflow_name'),
key: 'name',
width: 200,
render: (_row) =>
h(
NEllipsis,
{ style: 'max-width: 200px' },
{
default: () => _row.name
}
)
},
{
title: t('project.workflow.status'),
key: 'releaseState',
render: (_row) =>
_row.releaseState === 'ONLINE'
? t('project.workflow.up_line')
: t('project.workflow.down_line')
},
{
title: t('project.workflow.create_time'),
key: 'createTime',
width: 150
},
{
title: t('project.workflow.update_time'),
key: 'updateTime',
width: 150
},
{
title: t('project.workflow.description'),
key: 'description'
},
{
title: t('project.workflow.create_user'),
key: 'userName'
},
{
title: t('project.workflow.modify_user'),
key: 'modifyBy'
},
{
title: t('project.workflow.schedule_publish_status'),
key: 'scheduleReleaseState',
render: (_row) => {
if (_row.scheduleReleaseState === 'ONLINE') {
return h(
NTag,
{ type: 'success', size: 'small' },
{
default: () => t('project.workflow.up_line')
}
)
} else if (_row.scheduleReleaseState === 'OFFLINE') {
return h(
NTag,
{ type: 'warning', size: 'small' },
{
default: () => t('project.workflow.down_line')
}
)
} else {
return '-'
}
}
},
{
title: t('project.workflow.operation'),
key: 'operation',
width: 300,
fixed: 'right',
className: styles.operation,
render: (row) =>
h(TableAction, {
row,
onStartWorkflow: () => startWorkflow(row),
onTimingWorkflow: () => timingWorkflow(row),
onVersionWorkflow: () => versionWorkflow(row),
onDeleteWorkflow: () => deleteWorkflow(row),
onReleaseWorkflow: () => releaseWorkflow(row),
onCopyWorkflow: () => copyWorkflow(row),
onExportWorkflow: () => exportWorkflow(row),
onGotoTimingManage: () => gotoTimingManage(row)
})
}
]
const startWorkflow = (row: any) => {
variables.startShowRef = true
variables.row = row
}
const timingWorkflow = (row: any) => {
variables.timingShowRef = true
variables.row = row
}
const versionWorkflow = (row: any) => {
variables.versionShowRef = true
variables.row = row
}
const deleteWorkflow = (row: any) => {
deleteByCode(variables.projectCode, row.code)
.then(() => {
window.$message.success(t('project.workflow.success'))
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
const releaseWorkflow = (row: any) => {
const data = {
name: row.name,
releaseState: (row.releaseState === 'ONLINE' ? 'OFFLINE' : 'ONLINE') as
| 'OFFLINE'
| 'ONLINE'
}
release(data, variables.projectCode, row.code)
.then(() => {
window.$message.success(t('project.workflow.success'))
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
const copyWorkflow = (row: any) => {
const data = {
codes: String(row.code),
targetProjectCode: variables.projectCode
}
batchCopyByCodes(data, variables.projectCode)
.then(() => {
window.$message.success(t('project.workflow.success'))
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
const downloadBlob = (data: any, fileNameS = 'json') => {
if (!data) {
return
}
const blob = new Blob([data])
const fileName = `${fileNameS}.json`
if ('download' in document.createElement('a')) {
// Not IE
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) // remove element after downloading is complete.
window.URL.revokeObjectURL(url) // release blob object
} else {
// IE 10+
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, fileName)
}
}
}
const exportWorkflow = (row: any) => {
const fileName = 'workflow_' + new Date().getTime()
const data = {
codes: String(row.code)
}
batchExportByCodes(data, variables.projectCode)
.then((res: any) => {
downloadBlob(res, fileName)
})
.catch((error: any) => {
window.$message.error(error.message)
})
}
const gotoTimingManage = (row: any) => {
router.push({
name: 'workflow-definition-timing',
params: { projectCode: variables.projectCode, definitionCode: row.code }
})
}
const variables = reactive({
columns,
row: {},
tableData: [],
projectCode: ref(Number(router.currentRoute.value.params.projectCode)),
page: ref(1),
pageSize: ref(10),
searchVal: ref(),
totalPage: ref(1),
showRef: ref(false),
startShowRef: ref(false),
timingShowRef: ref(false),
versionShowRef: ref(false)
})
const getTableData = (params: IDefinitionParam) => {
const { state } = useAsyncState(
queryListPaging({ ...params }, variables.projectCode).then((res: any) => {
variables.totalPage = res.totalPage
variables.tableData = res.totalList.map((item: any) => {
return { ...item }
})
}),
{ total: 0, table: [] }
)
return state
}
return {
variables,
getTableData
}
}
Loading…
Cancel
Save