[Feature][UI Next] DAG backfill (#8325)
* [Feature][UI Next] Dag backfill * [Feature][UI Next] Add license header
This commit is contained in:
parent
222506ae35
commit
480492db73
|
|
@ -495,7 +495,8 @@ const project = {
|
|||
grid_layout: 'Grid',
|
||||
dagre_layout: 'Dagre',
|
||||
rows: 'Rows',
|
||||
cols: 'Cols'
|
||||
cols: 'Cols',
|
||||
copy_success: 'Copy Success'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -494,7 +494,8 @@ const project = {
|
|||
grid_layout: '网格布局',
|
||||
dagre_layout: '层次布局',
|
||||
rows: '行数',
|
||||
cols: '列数'
|
||||
cols: '列数',
|
||||
copy_success: '复制成功'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,11 +148,11 @@ export function verifyName(params: NameReq, code: CodeReq): any {
|
|||
}
|
||||
|
||||
export function queryProcessDefinitionByCode(
|
||||
code: CodeReq,
|
||||
processCode: CodeReq
|
||||
code: number,
|
||||
projectCode: number
|
||||
): any {
|
||||
return axios({
|
||||
url: `/projects/${code}/process-definition/${processCode}`,
|
||||
url: `/projects/${projectCode}/process-definition/${code}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const ALL_TASK_TYPES: any = {
|
||||
export const TASK_TYPES_MAP = {
|
||||
SHELL: {
|
||||
alias: 'SHELL'
|
||||
},
|
||||
|
|
@ -65,3 +65,5 @@ export const ALL_TASK_TYPES: any = {
|
|||
alias: 'WATERDROP'
|
||||
}
|
||||
}
|
||||
|
||||
export type TaskType = keyof typeof TASK_TYPES_MAP
|
||||
|
|
|
|||
|
|
@ -17,53 +17,33 @@
|
|||
|
||||
import { defineComponent, ref, inject } from 'vue'
|
||||
import Styles from './dag.module.scss'
|
||||
import type { PropType, Ref } from 'vue'
|
||||
import type { Dragged } from './index'
|
||||
import { useCanvasInit, useCellActive, useCanvasDrop } from './dag-hooks'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const props = {
|
||||
dragged: {
|
||||
type: Object as PropType<Ref<Dragged>>,
|
||||
default: ref({
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: ''
|
||||
})
|
||||
}
|
||||
}
|
||||
import { useCanvasInit, useCellActive } from './dag-hooks'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'workflow-dag-canvas',
|
||||
props,
|
||||
emits: ['drop'],
|
||||
setup(props, context) {
|
||||
const readonly = inject('readonly', ref(false))
|
||||
const graph = inject('graph', ref())
|
||||
const route = useRoute()
|
||||
const projectCode = route.params.projectCode as string
|
||||
|
||||
const { paper, minimap, container } = useCanvasInit({ readonly, graph })
|
||||
|
||||
// Change the style on cell hover and select
|
||||
useCellActive({ graph })
|
||||
|
||||
// Drop sidebar item in canvas
|
||||
const { onDrop, onDragenter, onDragover, onDragleave } = useCanvasDrop({
|
||||
readonly,
|
||||
dragged: props.dragged,
|
||||
graph,
|
||||
container,
|
||||
projectCode
|
||||
})
|
||||
const preventDefault = (e: DragEvent) => {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
ref={container}
|
||||
class={Styles.canvas}
|
||||
onDrop={onDrop}
|
||||
onDragenter={onDragenter}
|
||||
onDragover={onDragover}
|
||||
onDragleave={onDragleave}
|
||||
onDrop={(e) => {
|
||||
context.emit('drop', e)
|
||||
}}
|
||||
onDragenter={preventDefault}
|
||||
onDragover={preventDefault}
|
||||
onDragleave={preventDefault}
|
||||
>
|
||||
<div ref={paper} class={Styles.paper}></div>
|
||||
<div ref={minimap} class={Styles.minimap}></div>
|
||||
|
|
|
|||
|
|
@ -16,19 +16,25 @@
|
|||
*/
|
||||
|
||||
import { useCanvasInit } from './use-canvas-init'
|
||||
import { useGraphOperations } from './use-graph-operations'
|
||||
import { useCellQuery } from './use-cell-query'
|
||||
import { useCellActive } from './use-cell-active'
|
||||
import { useSidebarDrag } from './use-sidebar-drag'
|
||||
import { useCanvasDrop } from './use-canvas-drop'
|
||||
import { useCellUpdate } from './use-cell-update'
|
||||
import { useNodeSearch } from './use-node-search'
|
||||
import { useGraphAutoLayout } from './use-graph-auto-layout'
|
||||
import { useTextCopy } from './use-text-copy'
|
||||
import { useCustomCellBuilder } from './use-custom-cell-builder'
|
||||
import { useGraphBackfill } from './use-graph-backfill'
|
||||
import { useDagDragAndDrop } from './use-dag-drag-drop'
|
||||
|
||||
export {
|
||||
useCanvasInit,
|
||||
useGraphOperations,
|
||||
useCellQuery,
|
||||
useCellActive,
|
||||
useSidebarDrag,
|
||||
useCanvasDrop,
|
||||
useNodeSearch,
|
||||
useGraphAutoLayout
|
||||
useGraphAutoLayout,
|
||||
useTextCopy,
|
||||
useCustomCellBuilder,
|
||||
useGraphBackfill,
|
||||
useCellUpdate,
|
||||
useDagDragAndDrop
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,37 +15,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { PropType, Ref } from 'vue'
|
||||
import type { Dragged } from './index'
|
||||
import { defineComponent, ref, inject } from 'vue'
|
||||
import { ALL_TASK_TYPES } from '../../../task/constants/task-type'
|
||||
import { useSidebarDrag } from './dag-hooks'
|
||||
import { defineComponent } from 'vue'
|
||||
import { TASK_TYPES_MAP, TaskType } from '../../../task/constants/task-type'
|
||||
import Styles from './dag.module.scss'
|
||||
|
||||
const props = {
|
||||
dragged: {
|
||||
type: Object as PropType<Ref<Dragged>>,
|
||||
default: ref({
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: ''
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'workflow-dag-sidebar',
|
||||
props,
|
||||
setup(props) {
|
||||
const readonly = inject('readonly', ref(false))
|
||||
const dragged = props.dragged
|
||||
const { onDragStart } = useSidebarDrag({
|
||||
readonly,
|
||||
dragged
|
||||
})
|
||||
const allTaskTypes = Object.keys(ALL_TASK_TYPES).map((type) => ({
|
||||
emits: ['dragStart'],
|
||||
setup(props, context) {
|
||||
const allTaskTypes = Object.keys(TASK_TYPES_MAP).map((type) => ({
|
||||
type,
|
||||
...ALL_TASK_TYPES[type]
|
||||
...TASK_TYPES_MAP[type as TaskType]
|
||||
}))
|
||||
|
||||
return () => (
|
||||
|
|
@ -54,7 +34,9 @@ export default defineComponent({
|
|||
<div
|
||||
class={Styles.draggable}
|
||||
draggable='true'
|
||||
onDragstart={(e) => onDragStart(e, task.type)}
|
||||
onDragstart={(e) => {
|
||||
context.emit('dragStart', e, task.type)
|
||||
}}
|
||||
>
|
||||
<em
|
||||
class={[
|
||||
|
|
|
|||
|
|
@ -15,19 +15,29 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, ref, inject, PropType } from 'vue'
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
inject,
|
||||
PropType,
|
||||
onMounted,
|
||||
watch,
|
||||
computed
|
||||
} from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Styles from './dag.module.scss'
|
||||
import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui'
|
||||
import { NTooltip, NIcon, NButton, NSelect, useMessage } from 'naive-ui'
|
||||
import {
|
||||
SearchOutlined,
|
||||
DownloadOutlined,
|
||||
FullscreenOutlined,
|
||||
FullscreenExitOutlined,
|
||||
InfoCircleOutlined,
|
||||
FormatPainterOutlined
|
||||
FormatPainterOutlined,
|
||||
CopyOutlined
|
||||
} from '@vicons/antd'
|
||||
import { useNodeSearch } from './dag-hooks'
|
||||
import { useNodeSearch, useTextCopy, useCellQuery } from './dag-hooks'
|
||||
import { DataUri } from '@antv/x6'
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
|
@ -37,12 +47,19 @@ const props = {
|
|||
layoutToggle: {
|
||||
type: Function as PropType<(bool?: boolean) => void>,
|
||||
default: () => {}
|
||||
},
|
||||
// If this prop is passed, it means from definition detail
|
||||
definition: {
|
||||
// The same as the structure responsed by the queryProcessDefinitionByCode api
|
||||
type: Object as PropType<any>,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'workflow-dag-toolbar',
|
||||
props,
|
||||
emits: ['versionToggle'],
|
||||
setup(props, context) {
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
@ -55,11 +72,11 @@ export default defineComponent({
|
|||
* Node search and navigate
|
||||
*/
|
||||
const {
|
||||
searchNode,
|
||||
getAllNodes,
|
||||
allNodes,
|
||||
navigateTo,
|
||||
toggleSearchInput,
|
||||
searchInputVisible
|
||||
searchInputVisible,
|
||||
reQueryNodes,
|
||||
nodesDropdown
|
||||
} = useNodeSearch({ graph })
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +111,7 @@ export default defineComponent({
|
|||
* Open workflow version modal
|
||||
*/
|
||||
const openVersionModal = () => {
|
||||
//TODO, same as the version popup in the workflow list page
|
||||
context.emit('versionToggle', true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -111,6 +128,11 @@ export default defineComponent({
|
|||
router.go(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy workflow name
|
||||
*/
|
||||
const { copy } = useTextCopy()
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
|
|
@ -118,7 +140,24 @@ export default defineComponent({
|
|||
Styles[themeStore.darkTheme ? 'toolbar-dark' : 'toolbar-light']
|
||||
]}
|
||||
>
|
||||
<span class={Styles['workflow-name']}>{t('project.dag.create')}</span>
|
||||
<div>
|
||||
<span class={Styles['workflow-name']}>
|
||||
{props.definition?.processDefinition?.name ||
|
||||
t('project.dag.create')}
|
||||
</span>
|
||||
{props.definition?.processDefinition?.name && (
|
||||
<NButton
|
||||
quaternary
|
||||
circle
|
||||
onClick={() => copy(props.definition?.processDefinition?.name)}
|
||||
class={Styles['copy-btn']}
|
||||
>
|
||||
<NIcon>
|
||||
<CopyOutlined />
|
||||
</NIcon>
|
||||
</NButton>
|
||||
)}
|
||||
</div>
|
||||
<div class={Styles['toolbar-right-part']}>
|
||||
{/* Search node */}
|
||||
<NTooltip
|
||||
|
|
@ -150,9 +189,9 @@ export default defineComponent({
|
|||
>
|
||||
<NSelect
|
||||
size='small'
|
||||
options={allNodes.value}
|
||||
onFocus={getAllNodes}
|
||||
onUpdateValue={searchNode}
|
||||
options={nodesDropdown.value}
|
||||
onFocus={reQueryNodes}
|
||||
onUpdateValue={navigateTo}
|
||||
filterable
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -233,28 +272,30 @@ export default defineComponent({
|
|||
}}
|
||||
></NTooltip>
|
||||
{/* Version info */}
|
||||
<NTooltip
|
||||
v-slots={{
|
||||
trigger: () => (
|
||||
<NButton
|
||||
class={Styles['toolbar-right-item']}
|
||||
strong
|
||||
secondary
|
||||
circle
|
||||
type='info'
|
||||
onClick={openVersionModal}
|
||||
v-slots={{
|
||||
icon: () => (
|
||||
<NIcon>
|
||||
<InfoCircleOutlined />
|
||||
</NIcon>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
),
|
||||
default: () => t('project.workflow.version_info')
|
||||
}}
|
||||
></NTooltip>
|
||||
{!!props.definition && (
|
||||
<NTooltip
|
||||
v-slots={{
|
||||
trigger: () => (
|
||||
<NButton
|
||||
class={Styles['toolbar-right-item']}
|
||||
strong
|
||||
secondary
|
||||
circle
|
||||
type='info'
|
||||
onClick={openVersionModal}
|
||||
v-slots={{
|
||||
icon: () => (
|
||||
<NIcon>
|
||||
<InfoCircleOutlined />
|
||||
</NIcon>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
),
|
||||
default: () => t('project.workflow.version_info')
|
||||
}}
|
||||
></NTooltip>
|
||||
)}
|
||||
{/* Save workflow */}
|
||||
<NButton
|
||||
class={Styles['toolbar-right-item']}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ $bgLight: #ffffff;
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -16,48 +16,48 @@
|
|||
*/
|
||||
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { defineComponent, ref, provide } from 'vue'
|
||||
import { defineComponent, ref, provide, PropType, toRef } from 'vue'
|
||||
import DagToolbar from './dag-toolbar'
|
||||
import DagCanvas from './dag-canvas'
|
||||
import DagSidebar from './dag-sidebar'
|
||||
import Styles from './dag.module.scss'
|
||||
import DagAutoLayoutModal from './dag-auto-layout-modal'
|
||||
import { useGraphAutoLayout } from './dag-hooks'
|
||||
import {
|
||||
useGraphAutoLayout,
|
||||
useGraphBackfill,
|
||||
useDagDragAndDrop
|
||||
} from './dag-hooks'
|
||||
import { useThemeStore } from '@/store/theme/theme'
|
||||
import VersionModal from '../../definition/components/version-modal'
|
||||
import { WorkflowDefinition } from './types'
|
||||
import './x6-style.scss'
|
||||
|
||||
export interface Dragged {
|
||||
x: number
|
||||
y: number
|
||||
type: string
|
||||
const props = {
|
||||
// If this prop is passed, it means from definition detail
|
||||
definition: {
|
||||
type: Object as PropType<WorkflowDefinition>,
|
||||
default: undefined
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'workflow-dag',
|
||||
props,
|
||||
emits: ['refresh'],
|
||||
setup(props, context) {
|
||||
const theme = useThemeStore()
|
||||
|
||||
// Whether the graph can be operated
|
||||
const readonly = ref(false)
|
||||
provide('readonly', readonly)
|
||||
provide('readonly', toRef(props, 'readonly'))
|
||||
|
||||
const graph = ref<Graph>()
|
||||
provide('graph', graph)
|
||||
|
||||
// The sidebar slots
|
||||
const toolbarSlots = {
|
||||
left: context.slots.toolbarLeft,
|
||||
right: context.slots.toolbarRight
|
||||
}
|
||||
|
||||
// The element currently being dragged up
|
||||
const dragged = ref<Dragged>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: ''
|
||||
})
|
||||
|
||||
// Auto layout
|
||||
// Auto layout modal
|
||||
const {
|
||||
visible: layoutVisible,
|
||||
toggle: layoutToggle,
|
||||
|
|
@ -67,6 +67,28 @@ export default defineComponent({
|
|||
cancel
|
||||
} = useGraphAutoLayout({ graph })
|
||||
|
||||
const { onDragStart, onDrop } = useDagDragAndDrop({
|
||||
graph,
|
||||
readonly: toRef(props, 'readonly')
|
||||
})
|
||||
|
||||
// backfill
|
||||
useGraphBackfill({ graph, definition: toRef(props, 'definition') })
|
||||
|
||||
// version modal
|
||||
const versionModalShow = ref(false)
|
||||
const versionToggle = (bool: boolean) => {
|
||||
if (typeof bool === 'boolean') {
|
||||
versionModalShow.value = bool
|
||||
} else {
|
||||
versionModalShow.value = !versionModalShow.value
|
||||
}
|
||||
}
|
||||
const refreshDetail = () => {
|
||||
context.emit('refresh')
|
||||
versionModalShow.value = false
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
|
|
@ -74,10 +96,14 @@ export default defineComponent({
|
|||
Styles[`dag-${theme.darkTheme ? 'dark' : 'light'}`]
|
||||
]}
|
||||
>
|
||||
<DagToolbar v-slots={toolbarSlots} layoutToggle={layoutToggle} />
|
||||
<DagToolbar
|
||||
layoutToggle={layoutToggle}
|
||||
definition={props.definition}
|
||||
onVersionToggle={versionToggle}
|
||||
/>
|
||||
<div class={Styles.content}>
|
||||
<DagSidebar dragged={dragged} />
|
||||
<DagCanvas dragged={dragged} />
|
||||
<DagSidebar onDragStart={onDragStart} />
|
||||
<DagCanvas onDrop={onDrop} />
|
||||
</div>
|
||||
<DagAutoLayoutModal
|
||||
visible={layoutVisible.value}
|
||||
|
|
@ -86,6 +112,13 @@ export default defineComponent({
|
|||
formValue={formValue}
|
||||
formRef={formRef}
|
||||
/>
|
||||
{!!props.definition && (
|
||||
<VersionModal
|
||||
v-model:row={props.definition.processDefinition}
|
||||
v-model:show={versionModalShow.value}
|
||||
onUpdateList={refreshDetail}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
export interface ProcessDefinition {
|
||||
id: number
|
||||
code: number
|
||||
name: string
|
||||
version: number
|
||||
releaseState: string
|
||||
projectCode: number
|
||||
description: string
|
||||
globalParams: string
|
||||
globalParamList: any[]
|
||||
globalParamMap: any
|
||||
createTime: string
|
||||
updateTime: string
|
||||
flag: string
|
||||
userId: number
|
||||
userName?: any
|
||||
projectName?: any
|
||||
locations: string
|
||||
scheduleReleaseState?: any
|
||||
timeout: number
|
||||
tenantId: number
|
||||
tenantCode: string
|
||||
modifyBy?: any
|
||||
warningGroupId: number
|
||||
}
|
||||
|
||||
export interface ProcessTaskRelationList {
|
||||
id: number
|
||||
name: string
|
||||
processDefinitionVersion: number
|
||||
projectCode: any
|
||||
processDefinitionCode: any
|
||||
preTaskCode: number
|
||||
preTaskVersion: number
|
||||
postTaskCode: any
|
||||
postTaskVersion: number
|
||||
conditionType: string
|
||||
conditionParams: any
|
||||
createTime: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
export interface TaskDefinitionList {
|
||||
id: number
|
||||
code: any
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
projectCode: any
|
||||
userId: number
|
||||
taskType: string
|
||||
taskParams: any
|
||||
taskParamList: any[]
|
||||
taskParamMap: any
|
||||
flag: string
|
||||
taskPriority: string
|
||||
userName?: any
|
||||
projectName?: any
|
||||
workerGroup: string
|
||||
environmentCode: number
|
||||
failRetryTimes: number
|
||||
failRetryInterval: number
|
||||
timeoutFlag: string
|
||||
timeoutNotifyStrategy: string
|
||||
timeout: number
|
||||
delayTime: number
|
||||
resourceIds: string
|
||||
createTime: string
|
||||
updateTime: string
|
||||
modifyBy?: any
|
||||
dependence: string
|
||||
}
|
||||
|
||||
export interface WorkflowDefinition {
|
||||
processDefinition: ProcessDefinition
|
||||
processTaskRelationList: ProcessTaskRelationList[]
|
||||
taskDefinitionList: TaskDefinitionList[]
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
|
||||
import type { Node } from '@antv/x6'
|
||||
import { ref, onMounted, Ref, onUnmounted } from 'vue'
|
||||
import { ref, onMounted, Ref } from 'vue'
|
||||
import { Graph } from '@antv/x6'
|
||||
import { NODE, EDGE, X6_NODE_NAME, X6_EDGE_NAME } from './dag-config'
|
||||
import { debounce } from 'lodash'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { TaskType } from '../../../task/constants/task-type'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some cell-related query methods and refs
|
||||
* @param {Options} options
|
||||
*/
|
||||
export function useCellQuery(options: Options) {
|
||||
const { graph } = options
|
||||
|
||||
/**
|
||||
* Get all nodes
|
||||
*/
|
||||
function getNodes() {
|
||||
const nodes = graph.value?.getNodes()
|
||||
if (!nodes) return []
|
||||
return nodes.map((node) => {
|
||||
const position = node.getPosition()
|
||||
const data = node.getData()
|
||||
return {
|
||||
code: node.id,
|
||||
position: position,
|
||||
name: data.taskName as string,
|
||||
type: data.taskType as TaskType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
getNodes
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import type { TaskType } from '@/views/projects/task/constants/task-type'
|
||||
import { TASK_TYPES_MAP } from '@/views/projects/task/constants/task-type'
|
||||
import { useCustomCellBuilder } from './dag-hooks'
|
||||
import type { Coordinate } from './use-custom-cell-builder'
|
||||
import utils from '@/utils'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some cell query
|
||||
* @param {Options} options
|
||||
*/
|
||||
export function useCellUpdate(options: Options) {
|
||||
const { graph } = options
|
||||
|
||||
const { buildNode } = useCustomCellBuilder()
|
||||
|
||||
/**
|
||||
* Set node name by id
|
||||
* @param {string} id
|
||||
* @param {string} name
|
||||
*/
|
||||
function setNodeName(id: string, newName: string) {
|
||||
const node = graph.value?.getCellById(id)
|
||||
if (node) {
|
||||
const truncation = utils.truncateText(newName, 18)
|
||||
node.attr('title/text', truncation)
|
||||
node.setData({ taskName: newName })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a node to the graph
|
||||
* @param {string} id
|
||||
* @param {string} taskType
|
||||
* @param {Coordinate} coordinate Default is { x: 100, y: 100 }
|
||||
*/
|
||||
function addNode(
|
||||
id: string,
|
||||
type: string,
|
||||
coordinate: Coordinate = { x: 100, y: 100 }
|
||||
) {
|
||||
if (!TASK_TYPES_MAP[type as TaskType]) {
|
||||
console.warn(`taskType:${type} is invalid!`)
|
||||
return
|
||||
}
|
||||
const node = buildNode(id, type, '', coordinate)
|
||||
graph.value?.addNode(node)
|
||||
}
|
||||
|
||||
return {
|
||||
setNodeName,
|
||||
addNode
|
||||
}
|
||||
}
|
||||
|
|
@ -15,24 +15,27 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
import type { Node, Graph, Edge } from '@antv/x6'
|
||||
import type { Node, Edge } from '@antv/x6'
|
||||
import { X6_NODE_NAME, X6_EDGE_NAME } from './dag-config'
|
||||
import { ALL_TASK_TYPES } from '../../../task/constants/task-type'
|
||||
import utils from '@/utils'
|
||||
import { WorkflowDefinition } from './types'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
}
|
||||
export type Coordinate = { x: number; y: number }
|
||||
|
||||
type Coordinate = { x: number; y: number }
|
||||
|
||||
/**
|
||||
* Expose some graph operation methods
|
||||
* @param {Options} options
|
||||
*/
|
||||
export function useGraphOperations(options: Options) {
|
||||
const { graph } = options
|
||||
export function useCustomCellBuilder() {
|
||||
/**
|
||||
* Convert locationStr to JSON
|
||||
* @param {string} locationStr
|
||||
* @returns
|
||||
*/
|
||||
function parseLocationStr(locationStr: string) {
|
||||
let locations = null
|
||||
if (!locationStr) return locations
|
||||
try {
|
||||
locations = JSON.parse(locationStr)
|
||||
} catch (error) {}
|
||||
return Array.isArray(locations) ? locations : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Build edge metadata
|
||||
|
|
@ -40,7 +43,7 @@ export function useGraphOperations(options: Options) {
|
|||
* @param {string} targetId
|
||||
* @param {string} label
|
||||
*/
|
||||
function buildEdgeMetadata(
|
||||
function buildEdge(
|
||||
sourceId: string,
|
||||
targetId: string,
|
||||
label: string = ''
|
||||
|
|
@ -63,7 +66,7 @@ export function useGraphOperations(options: Options) {
|
|||
* @param {string} taskType
|
||||
* @param {Coordinate} coordinate Default is { x: 100, y: 100 }
|
||||
*/
|
||||
function buildNodeMetadata(
|
||||
function buildNode(
|
||||
id: string,
|
||||
type: string,
|
||||
taskName: string,
|
||||
|
|
@ -92,74 +95,43 @@ export function useGraphOperations(options: Options) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a node to the graph
|
||||
* @param {string} id
|
||||
* @param {string} taskType
|
||||
* @param {Coordinate} coordinate Default is { x: 100, y: 100 }
|
||||
* Build graph JSON
|
||||
* @param {WorkflowDefinition} definition
|
||||
* @returns
|
||||
*/
|
||||
function addNode(
|
||||
id: string,
|
||||
type: string,
|
||||
coordinate: Coordinate = { x: 100, y: 100 }
|
||||
) {
|
||||
if (!ALL_TASK_TYPES[type]) {
|
||||
console.warn(`taskType:${type} is invalid!`)
|
||||
return
|
||||
}
|
||||
const node = buildNodeMetadata(id, type, '', coordinate)
|
||||
graph.value?.addNode(node)
|
||||
}
|
||||
function buildGraph(definition: WorkflowDefinition) {
|
||||
const nodes: Node.Metadata[] = []
|
||||
const edges: Edge.Metadata[] = []
|
||||
|
||||
/**
|
||||
* Set node name by id
|
||||
* @param {string} id
|
||||
* @param {string} name
|
||||
*/
|
||||
function setNodeName(id: string, newName: string) {
|
||||
const node = graph.value?.getCellById(id)
|
||||
if (node) {
|
||||
const truncation = utils.truncateText(newName, 18)
|
||||
node.attr('title/text', truncation)
|
||||
node.setData({ taskName: newName })
|
||||
}
|
||||
}
|
||||
const locations =
|
||||
parseLocationStr(definition.processDefinition.locations) || []
|
||||
const tasks = definition.taskDefinitionList
|
||||
const connects = definition.processTaskRelationList
|
||||
|
||||
/**
|
||||
* Get nodes
|
||||
*/
|
||||
function getNodes() {
|
||||
const nodes = graph.value?.getNodes()
|
||||
if (!nodes) return []
|
||||
return nodes.map((node) => {
|
||||
const position = node.getPosition()
|
||||
const data = node.getData()
|
||||
return {
|
||||
code: node.id,
|
||||
position: position,
|
||||
name: data.taskName,
|
||||
type: data.taskType
|
||||
}
|
||||
tasks.forEach((task) => {
|
||||
const location = locations.find((l) => l.taskCode === task.code) || {}
|
||||
const node = buildNode(task.code, task.taskType, task.name, {
|
||||
x: location.x,
|
||||
y: location.y
|
||||
})
|
||||
nodes.push(node)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to cell
|
||||
* @param {string} code
|
||||
*/
|
||||
function navigateTo(code: string) {
|
||||
if (!graph.value) return
|
||||
const cell = graph.value.getCellById(code)
|
||||
graph.value.scrollToCell(cell, { animation: { duration: 600 } })
|
||||
graph.value.cleanSelection()
|
||||
graph.value.select(cell)
|
||||
connects
|
||||
.filter((r) => !!r.preTaskCode)
|
||||
.forEach((c) => {
|
||||
const edge = buildEdge(c.preTaskCode + '', c.postTaskCode, c.name)
|
||||
edges.push(edge)
|
||||
})
|
||||
return {
|
||||
nodes,
|
||||
edges
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
buildEdgeMetadata,
|
||||
buildNodeMetadata,
|
||||
addNode,
|
||||
setNodeName,
|
||||
getNodes,
|
||||
navigateTo
|
||||
buildNode,
|
||||
buildEdge,
|
||||
buildGraph
|
||||
}
|
||||
}
|
||||
|
|
@ -15,39 +15,60 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ref } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import type { Dragged } from './dag'
|
||||
import type { Dragged } from '.'
|
||||
import { genTaskCodeList } from '@/service/modules/task-definition'
|
||||
import { useGraphOperations } from './dag-hooks'
|
||||
import { useCellUpdate } from './dag-hooks'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
interface Options {
|
||||
readonly: Ref<boolean>
|
||||
graph: Ref<Graph | undefined>
|
||||
container: Ref<HTMLElement | undefined>
|
||||
dragged: Ref<Dragged>
|
||||
projectCode: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop sidebar item in canvas
|
||||
* Sidebar item drag && drop in canvas
|
||||
*/
|
||||
export function useCanvasDrop(options: Options) {
|
||||
const { readonly, graph, container, dragged, projectCode } = options
|
||||
export function useDagDragAndDrop(options: Options) {
|
||||
const { readonly, graph } = options
|
||||
|
||||
const { addNode } = useGraphOperations({ graph })
|
||||
const route = useRoute()
|
||||
const projectCode = Number(route.params.projectCode)
|
||||
|
||||
const onDrop = (e: DragEvent) => {
|
||||
const { addNode } = useCellUpdate({ graph })
|
||||
|
||||
// The element currently being dragged up
|
||||
const dragged = ref<Dragged>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
type: ''
|
||||
})
|
||||
|
||||
function onDragStart(e: DragEvent, type: string) {
|
||||
if (readonly.value) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
dragged.value = {
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
type: type
|
||||
}
|
||||
}
|
||||
|
||||
function onDrop(e: DragEvent) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (readonly.value) {
|
||||
return
|
||||
}
|
||||
if (dragged.value && graph.value && container.value && projectCode) {
|
||||
if (dragged.value && graph.value && projectCode) {
|
||||
const { type, x: eX, y: eY } = dragged.value
|
||||
const { x, y } = graph.value.clientToLocal(e.clientX, e.clientY)
|
||||
const genNums = 1
|
||||
genTaskCodeList(genNums, Number(projectCode)).then((res) => {
|
||||
genTaskCodeList(genNums, projectCode).then((res) => {
|
||||
const [code] = res
|
||||
addNode(code + '', type, { x: x - eX, y: y - eY })
|
||||
// openTaskConfigModel(code, type)
|
||||
|
|
@ -60,6 +81,7 @@ export function useCanvasDrop(options: Options) {
|
|||
}
|
||||
|
||||
return {
|
||||
onDragStart,
|
||||
onDrop,
|
||||
onDragenter: preventDefault,
|
||||
onDragover: preventDefault,
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 { Ref, watch } from 'vue'
|
||||
import { useCustomCellBuilder } from './dag-hooks'
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { WorkflowDefinition } from './types'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
definition: Ref<WorkflowDefinition | undefined>
|
||||
}
|
||||
|
||||
/**
|
||||
* Backfill workflow into graph
|
||||
*/
|
||||
export function useGraphBackfill(options: Options) {
|
||||
const { graph, definition } = options
|
||||
|
||||
const { buildGraph } = useCustomCellBuilder()
|
||||
|
||||
watch([graph, definition], () => {
|
||||
if (graph.value && definition.value) {
|
||||
graph.value.fromJSON(buildGraph(definition.value))
|
||||
}
|
||||
})
|
||||
|
||||
return {}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import type { Graph } from '@antv/x6'
|
||||
import { ref, Ref } from 'vue'
|
||||
import { useGraphOperations } from './dag-hooks'
|
||||
import { useCellQuery } from './dag-hooks'
|
||||
|
||||
interface Options {
|
||||
graph: Ref<Graph | undefined>
|
||||
|
|
@ -29,30 +29,43 @@ interface Options {
|
|||
export function useNodeSearch(options: Options) {
|
||||
const { graph } = options
|
||||
|
||||
/**
|
||||
* Search input visible control
|
||||
*/
|
||||
const searchInputVisible = ref(false)
|
||||
const allNodes = ref<any>([])
|
||||
const toggleSearchInput = () => {
|
||||
searchInputVisible.value = !searchInputVisible.value
|
||||
}
|
||||
const { getNodes, navigateTo } = useGraphOperations({ graph })
|
||||
const searchNode = (val: string) => {
|
||||
navigateTo(val)
|
||||
|
||||
/**
|
||||
* Search dropdown control
|
||||
*/
|
||||
const { getNodes } = useCellQuery({ graph })
|
||||
const nodesDropdown = ref<{ label: string; value: string }[]>([])
|
||||
const reQueryNodes = () => {
|
||||
nodesDropdown.value = getNodes().map((node) => ({
|
||||
label: node.name,
|
||||
value: node.code
|
||||
}))
|
||||
}
|
||||
const getAllNodes = () => {
|
||||
const nodes = getNodes()
|
||||
allNodes.value = nodes.map((node) => {
|
||||
return {
|
||||
label: node.name,
|
||||
value: node.code
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Navigate to cell
|
||||
* @param {string} code
|
||||
*/
|
||||
function navigateTo(code: string) {
|
||||
if (!graph.value) return
|
||||
const cell = graph.value.getCellById(code)
|
||||
graph.value.scrollToCell(cell, { animation: { duration: 600 } })
|
||||
graph.value.cleanSelection()
|
||||
graph.value.select(cell)
|
||||
}
|
||||
|
||||
return {
|
||||
searchNode,
|
||||
getAllNodes,
|
||||
allNodes,
|
||||
navigateTo,
|
||||
toggleSearchInput,
|
||||
searchInputVisible
|
||||
searchInputVisible,
|
||||
reQueryNodes,
|
||||
nodesDropdown
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,33 +15,23 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
import type { Dragged } from './dag'
|
||||
|
||||
interface Options {
|
||||
readonly: Ref<boolean>
|
||||
dragged: Ref<Dragged>
|
||||
}
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
/**
|
||||
* Sidebar drag
|
||||
* Text copy with success message
|
||||
*/
|
||||
export function useSidebarDrag(options: Options) {
|
||||
const { readonly, dragged } = options
|
||||
|
||||
const onDragStart = (e: DragEvent, type: string) => {
|
||||
if (readonly.value) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
dragged.value = {
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
type: type
|
||||
}
|
||||
export function useTextCopy() {
|
||||
const { t } = useI18n()
|
||||
const { copy } = useClipboard()
|
||||
const message = useMessage()
|
||||
const copyText = (text: string) => {
|
||||
copy(text).then((res) => {
|
||||
message.success(t('project.dag.copy_success'))
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
onDragStart
|
||||
copy: copyText
|
||||
}
|
||||
}
|
||||
|
|
@ -58,9 +58,11 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
watch(
|
||||
() => props.row.code,
|
||||
() => props.show,
|
||||
() => {
|
||||
getTableData(props.row)
|
||||
if (props.show && props.row?.code) {
|
||||
getTableData(props.row)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,11 +25,6 @@ export default defineComponent({
|
|||
setup() {
|
||||
const theme = useThemeStore()
|
||||
|
||||
const slots = {
|
||||
toolbarLeft: () => <span>left-operations</span>,
|
||||
toolbarRight: () => <span>right-operations</span>
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
|
|
@ -37,7 +32,7 @@ export default defineComponent({
|
|||
theme.darkTheme ? Styles['dark'] : Styles['light']
|
||||
]}
|
||||
>
|
||||
<Dag v-slots={slots} />
|
||||
<Dag />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
|
@ -15,11 +15,44 @@
|
|||
* 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 { WorkflowDefinition } from '../../components/dag/types'
|
||||
import Styles from './index.module.scss'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WorkflowDefinitionDetails',
|
||||
setup() {
|
||||
return () => <div>WorkflowDefinitionDetails</div>
|
||||
const theme = useThemeStore()
|
||||
const route = useRoute()
|
||||
const projectCode = Number(route.params.projectCode)
|
||||
const code = Number(route.params.code)
|
||||
|
||||
const definition = ref<WorkflowDefinition>()
|
||||
|
||||
const refresh = () => {
|
||||
queryProcessDefinitionByCode(code, projectCode).then((res: any) => {
|
||||
definition.value = res
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!code || !projectCode) return
|
||||
refresh()
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
Styles.container,
|
||||
theme.darkTheme ? Styles['dark'] : Styles['light']
|
||||
]}
|
||||
>
|
||||
<Dag definition={definition.value} onRefresh={refresh} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -32,11 +32,17 @@ 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 { useRouter, useRoute } from 'vue-router'
|
||||
import type { Router } from 'vue-router'
|
||||
import styles from './index.module.scss'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WorkflowDefinitionList',
|
||||
setup() {
|
||||
const router: Router = useRouter()
|
||||
const route = useRoute()
|
||||
const projectCode = Number(route.params.projectCode)
|
||||
|
||||
const { variables, getTableData } = useTable()
|
||||
|
||||
const requestData = () => {
|
||||
|
|
@ -61,6 +67,12 @@ export default defineComponent({
|
|||
requestData()
|
||||
}
|
||||
|
||||
const createDefinition = () => {
|
||||
router.push({
|
||||
path: `/projects/${projectCode}/workflow/definitions/create`
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
requestData()
|
||||
})
|
||||
|
|
@ -69,6 +81,7 @@ export default defineComponent({
|
|||
requestData,
|
||||
handleSearch,
|
||||
handleUpdateList,
|
||||
createDefinition,
|
||||
handleChangePageSize,
|
||||
...toRefs(variables)
|
||||
}
|
||||
|
|
@ -81,7 +94,7 @@ export default defineComponent({
|
|||
<Card class={styles.card}>
|
||||
<div class={styles.header}>
|
||||
<NSpace>
|
||||
<NButton type='primary' /* TODO: Create workflow */>
|
||||
<NButton type='primary' onClick={this.createDefinition}>
|
||||
{t('project.workflow.create_workflow')}
|
||||
</NButton>
|
||||
<NButton strong secondary onClick={() => (this.showRef = true)}>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { h, ref, reactive } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRouter, RouterLink } 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'
|
||||
|
|
@ -49,14 +49,21 @@ export function useTable() {
|
|||
title: t('project.workflow.workflow_name'),
|
||||
key: 'name',
|
||||
width: 200,
|
||||
render: (_row) =>
|
||||
h(
|
||||
NEllipsis,
|
||||
{ style: 'max-width: 200px' },
|
||||
{
|
||||
default: () => _row.name
|
||||
}
|
||||
)
|
||||
render: (_row) => (
|
||||
<NEllipsis
|
||||
style={{
|
||||
maxWidth: '200px'
|
||||
}}
|
||||
>
|
||||
<RouterLink
|
||||
to={{
|
||||
path: `/projects/${_row.projectCode}/workflow/definitions/${_row.code}`
|
||||
}}
|
||||
>
|
||||
{_row.name}
|
||||
</RouterLink>
|
||||
</NEllipsis>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t('project.workflow.status'),
|
||||
Loading…
Reference in New Issue