Browse Source

[Feature][UI Next] Workflow editing and cells deletion capabilities (#8435)

* [Feature][UI Next] Workflow editing and cells deletion capabilities

* [Feature][UI Next] Code format
3.0.0/version-upgrade
wangyizhi 3 years ago committed by GitHub
parent
commit
e49ce611d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      dolphinscheduler-ui-next/src/components/modal/index.tsx
  2. 4
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  3. 4
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  4. 10
      dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts
  5. 7
      dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts
  6. 15
      dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx
  7. 7
      dolphinscheduler-ui-next/src/views/projects/task/components/node/detail.tsx
  8. 2
      dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts
  9. 41
      dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-save-modal.tsx
  10. 49
      dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-toolbar.tsx
  11. 14
      dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx
  12. 3
      dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts
  13. 54
      dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts
  14. 62
      dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx
  15. 40
      dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.module.scss
  16. 47
      dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.tsx
  17. 13
      dolphinscheduler-ui-next/src/views/security/yarn-queue-manage/index.tsx

19
dolphinscheduler-ui-next/src/components/modal/index.tsx

@ -89,8 +89,15 @@ const Modal = defineComponent({
return { t, onCancel, onConfirm, onJumpLink }
},
render() {
const { $slots, t, onCancel, onConfirm, confirmDisabled, confirmLoading, onJumpLink } =
this
const {
$slots,
t,
onCancel,
onConfirm,
confirmDisabled,
confirmLoading,
onJumpLink
} = this
return (
<NModal
@ -109,16 +116,12 @@ const Modal = defineComponent({
'header-extra': () => (
<NSpace justify='end'>
{this.linkEventShow && (
<NButton
text
onClick={onJumpLink}
>
<NButton text onClick={onJumpLink}>
{this.linkEventText}
</NButton>
)}
</NSpace>
)
,
),
footer: () => (
<NSpace justify='end'>
{this.cancelShow && (

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

@ -547,7 +547,9 @@ const project = {
minute: 'Minute',
key: 'Key',
value: 'Value',
success: 'Success'
success: 'Success',
delete_cell: 'Delete selected edges and nodes',
online_directly: 'Whether to go online the process definition'
},
node: {
current_node_settings: 'Current node settings',

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

@ -545,7 +545,9 @@ const project = {
minute: '分',
key: '键',
value: '值',
success: '成功'
success: '成功',
delete_cell: '删除选中的线或节点',
online_directly: '是否上线流程定义'
},
node: {
current_node_settings: '当前节点设置',

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

@ -157,13 +157,13 @@ export function queryProcessDefinitionByCode(
})
}
export function update(
data: ProcessDefinitionReq & NameReq & ReleaseStateReq,
code: CodeReq,
processCode: CodeReq
export function updateProcessDefinition(
data: ProcessDefinitionReq & ReleaseStateReq,
code: number,
projectCode: number
): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}`,
url: `/projects/${projectCode}/process-definition/${code}`,
method: 'put',
data
})

7
dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts

@ -82,9 +82,12 @@ export function queryTopNLongestRunningProcessInstance(
})
}
export function queryProcessInstanceById(id: IdReq, code: CodeReq): any {
export function queryProcessInstanceById(
instanceId: number,
projectCode: number
): any {
return axios({
url: `/projects/${code}/process-instances/${id}`,
url: `/projects/${projectCode}/process-instances/${instanceId}`,
method: 'get'
})
}

15
dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx

@ -64,7 +64,7 @@ const NodeDetailModal = defineComponent({
detailRef: ref(),
linkEventShowRef: ref(),
linkEventTextRef: ref(),
linkUrlRef: ref(),
linkUrlRef: ref()
})
const onConfirm = async () => {
@ -103,8 +103,17 @@ const NodeDetailModal = defineComponent({
}
},
render() {
const { t, show, onConfirm, onCancel, projectCode, data, readonly, from, onJumpLink } =
this
const {
t,
show,
onConfirm,
onCancel,
projectCode,
data,
readonly,
from,
onJumpLink
} = this
return (
<Modal
show={show}

7
dolphinscheduler-ui-next/src/views/projects/task/components/node/detail.tsx

@ -74,7 +74,12 @@ const NodeDetail = defineComponent({
// TODO: Change task type
if (taskType === 'SUB_PROCESS') {
// TODO: add linkUrl
emit('linkEventText', true, `${t('project.node.enter_child_node')}`, '')
emit(
'linkEventText',
true,
`${t('project.node.enter_child_node')}`,
''
)
} else {
emit('linkEventText', false, '', '')
}

2
dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts

@ -68,7 +68,7 @@ interface ITaskData
> {
name?: string
processName?: number
taskPriority?: number
taskPriority?: string
timeoutFlag: 'OPEN' | 'CLOSE'
timeoutNotifyStrategy?: string | []
taskParams: {

41
dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-save-modal.tsx

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { defineComponent, PropType, ref, computed, onMounted } from 'vue'
import { defineComponent, PropType, ref, computed, onMounted, watch } from 'vue'
import Modal from '@/components/modal'
import { useI18n } from 'vue-i18n'
import {
@ -25,16 +25,22 @@ import {
NSelect,
NSwitch,
NInputNumber,
NDynamicInput
NDynamicInput,
NCheckbox
} from 'naive-ui'
import { queryTenantList } from '@/service/modules/tenants'
import { SaveForm } from './types'
import { SaveForm, WorkflowDefinition } from './types'
import './x6-style.scss'
const props = {
visible: {
type: Boolean as PropType<boolean>,
default: false
},
// If this prop is passed, it means from definition detail
definition: {
type: Object as PropType<WorkflowDefinition>,
default: undefined
}
}
@ -74,7 +80,8 @@ export default defineComponent({
tenantCode: 'default',
timeoutFlag: false,
timeout: 0,
globalParams: []
globalParams: [],
release: false
})
const formRef = ref()
const rule = {
@ -89,6 +96,25 @@ export default defineComponent({
context.emit('update:show', false)
}
watch(
() => props.definition,
() => {
const process = props.definition?.processDefinition
if (process) {
formValue.value.name = process.name
formValue.value.description = process.description
formValue.value.tenantCode = process.tenantCode
if (process.timeout && process.timeout > 0) {
formValue.value.timeoutFlag = true
formValue.value.timeout = process.timeout
}
formValue.value.globalParams = process.globalParamList.map(
(param) => ({ key: param.prop, value: param.value })
)
}
}
)
return () => (
<Modal
show={props.visible}
@ -146,6 +172,13 @@ export default defineComponent({
value-placeholder={t('project.dag.value')}
/>
</NFormItem>
{props.definition && (
<NFormItem label=' ' path='timeoutFlag'>
<NCheckbox v-model:checked={formValue.value.release}>
{t('project.dag.online_directly')}
</NCheckbox>
</NFormItem>
)}
</NForm>
</Modal>
)

49
dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-toolbar.tsx

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { defineComponent, ref, inject, PropType } from 'vue'
import { defineComponent, ref, inject, PropType, Ref } from 'vue'
import { useI18n } from 'vue-i18n'
import Styles from './dag.module.scss'
import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui'
@ -26,13 +26,15 @@ import {
FullscreenExitOutlined,
InfoCircleOutlined,
FormatPainterOutlined,
CopyOutlined
CopyOutlined,
DeleteOutlined
} from '@vicons/antd'
import { useNodeSearch, useTextCopy } from './dag-hooks'
import { DataUri } from '@antv/x6'
import { useFullscreen } from '@vueuse/core'
import { useRouter } from 'vue-router'
import { useThemeStore } from '@/store/theme/theme'
import type { Graph } from '@antv/x6'
const props = {
layoutToggle: {
@ -50,13 +52,13 @@ const props = {
export default defineComponent({
name: 'workflow-dag-toolbar',
props,
emits: ['versionToggle', 'saveModelToggle'],
emits: ['versionToggle', 'saveModelToggle', 'removeTasks'],
setup(props, context) {
const { t } = useI18n()
const themeStore = useThemeStore()
const graph = inject('graph', ref())
const graph = inject<Ref<Graph | undefined>>('graph', ref())
const router = useRouter()
/**
@ -124,6 +126,22 @@ export default defineComponent({
*/
const { copy } = useTextCopy()
/**
* Delete selected edges and nodes
*/
const removeCells = () => {
if (graph.value) {
const cells = graph.value.getSelectedCells()
if (cells) {
graph.value?.removeCells(cells)
const codes = cells
.filter((cell) => cell.isNode())
.map((cell) => +cell.id)
context.emit('removeTasks', codes)
}
}
}
return () => (
<div
class={[
@ -209,6 +227,29 @@ export default defineComponent({
default: () => t('project.dag.download_png')
}}
></NTooltip>
{/* Delete */}
<NTooltip
v-slots={{
trigger: () => (
<NButton
class={Styles['toolbar-right-item']}
strong
secondary
circle
type='info'
onClick={() => removeCells()}
v-slots={{
icon: () => (
<NIcon>
<DeleteOutlined />
</NIcon>
)
}}
/>
),
default: () => t('project.dag.delete_cell')
}}
></NTooltip>
{/* Toggle fullscreen */}
<NTooltip
v-slots={{

14
dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx

@ -82,8 +82,9 @@ export default defineComponent({
currTask,
taskCancel,
appendTask,
taskDefinitions
} = useTaskEdit({ graph })
taskDefinitions,
removeTasks
} = useTaskEdit({ graph, definition: toRef(props, 'definition') })
const { onDragStart, onDrop } = useDagDragAndDrop({
graph,
@ -144,6 +145,7 @@ export default defineComponent({
definition={props.definition}
onVersionToggle={versionToggle}
onSaveModelToggle={saveModelToggle}
onRemoveTasks={removeTasks}
/>
<div class={Styles.content}>
<DagSidebar onDragStart={onDragStart} />
@ -163,11 +165,15 @@ export default defineComponent({
onUpdateList={refreshDetail}
/>
)}
<DagSaveModal v-model:show={saveModalShow.value} onSave={onSave} />
<DagSaveModal
v-model:show={saveModalShow.value}
onSave={onSave}
definition={props.definition}
/>
<TaskModal
show={taskModalVisible.value}
projectCode={props.projectCode}
data={currTask.value}
data={currTask.value as any}
onSubmit={taskConfirm}
onCancel={taskCancel}
/>

3
dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts

@ -79,7 +79,7 @@ export interface TaskDefinition {
environmentCode: number
failRetryTimes: number
failRetryInterval: number
timeoutFlag: string
timeoutFlag: 'OPEN' | 'CLOSE'
timeoutNotifyStrategy: string
timeout: number
delayTime: number
@ -125,6 +125,7 @@ export interface SaveForm {
timeoutFlag: boolean
timeout: number
globalParams: GlobalParam[]
release: boolean
}
export interface Location {

54
dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts

@ -15,15 +15,18 @@
* limitations under the License.
*/
import { ref, onMounted } from 'vue'
import { ref, onMounted, watch } from 'vue'
import type { Ref } from 'vue'
import type { Graph } from '@antv/x6'
import type { Coordinate, NodeData } from './types'
import { TaskType } from '@/views/projects/task/constants/task-type'
import { formatParams } from '@/views/projects/task/components/node/format-data'
import { useCellUpdate } from './dag-hooks'
import { WorkflowDefinition } from './types'
interface Options {
graph: Ref<Graph | undefined>
definition: Ref<WorkflowDefinition | undefined>
}
/**
@ -32,7 +35,7 @@ interface Options {
* @returns
*/
export function useTaskEdit(options: Options) {
const { graph } = options
const { graph, definition } = options
const { addNode, setNodeName } = useCellUpdate({ graph })
@ -57,6 +60,16 @@ export function useTaskEdit(options: Options) {
openTaskModal({ code, taskType: type, name: '' })
}
/**
* Remove task
* @param {number} code
*/
function removeTasks(codes: number[]) {
taskDefinitions.value = taskDefinitions.value.filter(
(task) => !codes.includes(task.code)
)
}
function openTaskModal(task: NodeData) {
currTask.value = task
taskModalVisible.value = true
@ -67,25 +80,21 @@ export function useTaskEdit(options: Options) {
* @param formRef
* @param from
*/
function taskConfirm({ formRef, form }: any) {
formRef.validate((errors: any) => {
if (!errors) {
// override target config
taskDefinitions.value = taskDefinitions.value.map((task) => {
if (task.code === currTask.value?.code) {
setNodeName(task.code + '', form.name)
console.log(form)
console.log(JSON.stringify(form))
return {
code: task.code,
...form
}
}
return task
})
taskModalVisible.value = false
function taskConfirm({ data }: any) {
const taskDef = formatParams(data).taskDefinitionJsonObj as NodeData
// override target config
taskDefinitions.value = taskDefinitions.value.map((task) => {
if (task.code === currTask.value?.code) {
setNodeName(task.code + '', taskDef.name)
return {
...taskDef,
code: task.code,
taskType: currTask.value.taskType
}
}
return task
})
taskModalVisible.value = false
}
/**
@ -108,12 +117,17 @@ export function useTaskEdit(options: Options) {
}
})
watch(definition, () => {
taskDefinitions.value = definition.value?.taskDefinitionList || []
})
return {
currTask,
taskModalVisible,
taskConfirm,
taskCancel,
appendTask,
taskDefinitions
taskDefinitions,
removeTasks
}
}

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

@ -16,18 +16,39 @@
*/
import { defineComponent, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import { useThemeStore } from '@/store/theme/theme'
import { useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import Dag from '../../components/dag'
import { queryProcessDefinitionByCode } from '@/service/modules/process-definition'
import { WorkflowDefinition } from '../../components/dag/types'
import {
queryProcessDefinitionByCode,
updateProcessDefinition
} from '@/service/modules/process-definition'
import {
WorkflowDefinition,
SaveForm,
TaskDefinition,
Connect,
Location
} from '../../components/dag/types'
import Styles from './index.module.scss'
interface SaveData {
saveForm: SaveForm
taskDefinitions: TaskDefinition[]
connects: Connect[]
locations: Location[]
}
export default defineComponent({
name: 'WorkflowDefinitionDetails',
setup() {
const theme = useThemeStore()
const route = useRoute()
const router = useRouter()
const message = useMessage()
const { t } = useI18n()
const projectCode = Number(route.params.projectCode)
const code = Number(route.params.code)
@ -39,7 +60,40 @@ export default defineComponent({
})
}
const save = () => {}
const save = ({
taskDefinitions,
saveForm,
connects,
locations
}: SaveData) => {
const globalParams = saveForm.globalParams.map((p) => {
return {
prop: p.key,
value: p.value,
direct: 'IN',
type: 'VARCHAR'
}
})
updateProcessDefinition(
{
taskDefinitionJson: JSON.stringify(taskDefinitions),
taskRelationJson: JSON.stringify(connects),
locations: JSON.stringify(locations),
name: saveForm.name,
tenantCode: saveForm.tenantCode,
description: saveForm.description,
globalParams: JSON.stringify(globalParams),
timeout: saveForm.timeoutFlag ? saveForm.timeout : 0,
releaseState: saveForm.release ? 'ONLINE' : 'OFFLINE'
},
code,
projectCode
).then((res: any) => {
message.success(t('project.dag.success'))
router.push({ path: `/projects/${projectCode}/workflow-definition` })
})
}
onMounted(() => {
if (!code || !projectCode) return

40
dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.module.scss

@ -0,0 +1,40 @@
/*
* 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.
*/
$borderDark: rgba(255, 255, 255, 0.09);
$borderLight: rgb(239, 239, 245);
$bgDark: rgb(24, 24, 28);
$bgLight: #ffffff;
.container {
width: 100%;
padding: 20px;
box-sizing: border-box;
height: calc(100vh - 100px);
overflow: hidden;
display: block;
}
.dark {
border: solid 1px $borderDark;
background-color: $bgDark;
}
.light {
border: solid 1px $borderLight;
background-color: $bgLight;
}

47
dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.tsx

@ -15,11 +15,54 @@
* limitations under the License.
*/
import { defineComponent } from 'vue'
import { defineComponent, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { useThemeStore } from '@/store/theme/theme'
import Dag from '../../components/dag'
// import { queryProcessDefinitionByCode } from '@/service/modules/process-definition'
import { queryProcessInstanceById } from '@/service/modules/process-instances'
import { WorkflowDefinition } from '../../components/dag/types'
import Styles from './index.module.scss'
export default defineComponent({
name: 'WorkflowInstanceDetails',
setup() {
return () => <div>WorkflowInstanceDetails</div>
const theme = useThemeStore()
const route = useRoute()
const projectCode = Number(route.params.projectCode)
const id = Number(route.params.id)
const definition = ref<WorkflowDefinition>()
const refresh = () => {
queryProcessInstanceById(id, projectCode).then((res: any) => {
if (res.dagData) {
definition.value = res.dagData
}
})
}
const save = () => {}
onMounted(() => {
if (!id || !projectCode) return
refresh()
})
return () => (
<div
class={[
Styles.container,
theme.darkTheme ? Styles['dark'] : Styles['light']
]}
>
<Dag
definition={definition.value}
onRefresh={refresh}
projectCode={projectCode}
onSave={save}
/>
</div>
)
}
})

13
dolphinscheduler-ui-next/src/views/security/yarn-queue-manage/index.tsx

@ -106,7 +106,12 @@ const yarnQueueManage = defineComponent({
<NCard>
<div class={styles['search-card']}>
<div>
<NButton size='small' type='primary' onClick={handleModalChange} class='btn-create-queue'>
<NButton
size='small'
type='primary'
onClick={handleModalChange}
class='btn-create-queue'
>
{t('security.yarn_queue.create_queue')}
</NButton>
</div>
@ -130,7 +135,11 @@ const yarnQueueManage = defineComponent({
</div>
</NCard>
<Card class={styles['table-card']}>
<NDataTable row-class-name='items' columns={this.columns} data={this.tableData} />
<NDataTable
row-class-name='items'
columns={this.columns}
data={this.tableData}
/>
<div class={styles.pagination}>
<NPagination
v-model:page={this.page}

Loading…
Cancel
Save