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

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

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

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

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

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

@ -157,13 +157,13 @@ export function queryProcessDefinitionByCode(
}) })
} }
export function update( export function updateProcessDefinition(
data: ProcessDefinitionReq & NameReq & ReleaseStateReq, data: ProcessDefinitionReq & ReleaseStateReq,
code: CodeReq, code: number,
processCode: CodeReq projectCode: number
): any { ): any {
return axios({ return axios({
url: `/projects/${code}/process-definition/${processCode}`, url: `/projects/${projectCode}/process-definition/${code}`,
method: 'put', method: 'put',
data 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({ return axios({
url: `/projects/${code}/process-instances/${id}`, url: `/projects/${projectCode}/process-instances/${instanceId}`,
method: 'get' method: 'get'
}) })
} }

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

@ -64,7 +64,7 @@ const NodeDetailModal = defineComponent({
detailRef: ref(), detailRef: ref(),
linkEventShowRef: ref(), linkEventShowRef: ref(),
linkEventTextRef: ref(), linkEventTextRef: ref(),
linkUrlRef: ref(), linkUrlRef: ref()
}) })
const onConfirm = async () => { const onConfirm = async () => {
@ -103,8 +103,17 @@ const NodeDetailModal = defineComponent({
} }
}, },
render() { render() {
const { t, show, onConfirm, onCancel, projectCode, data, readonly, from, onJumpLink } = const {
this t,
show,
onConfirm,
onCancel,
projectCode,
data,
readonly,
from,
onJumpLink
} = this
return ( return (
<Modal <Modal
show={show} 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 // TODO: Change task type
if (taskType === 'SUB_PROCESS') { if (taskType === 'SUB_PROCESS') {
// TODO: add linkUrl // TODO: add linkUrl
emit('linkEventText', true, `${t('project.node.enter_child_node')}`, '') emit(
'linkEventText',
true,
`${t('project.node.enter_child_node')}`,
''
)
} else { } else {
emit('linkEventText', false, '', '') emit('linkEventText', false, '', '')
} }

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

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

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

@ -15,7 +15,7 @@
* limitations under the License. * 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 Modal from '@/components/modal'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { import {
@ -25,16 +25,22 @@ import {
NSelect, NSelect,
NSwitch, NSwitch,
NInputNumber, NInputNumber,
NDynamicInput NDynamicInput,
NCheckbox
} from 'naive-ui' } from 'naive-ui'
import { queryTenantList } from '@/service/modules/tenants' import { queryTenantList } from '@/service/modules/tenants'
import { SaveForm } from './types' import { SaveForm, WorkflowDefinition } from './types'
import './x6-style.scss' import './x6-style.scss'
const props = { const props = {
visible: { visible: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false 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', tenantCode: 'default',
timeoutFlag: false, timeoutFlag: false,
timeout: 0, timeout: 0,
globalParams: [] globalParams: [],
release: false
}) })
const formRef = ref() const formRef = ref()
const rule = { const rule = {
@ -89,6 +96,25 @@ export default defineComponent({
context.emit('update:show', false) 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 () => ( return () => (
<Modal <Modal
show={props.visible} show={props.visible}
@ -146,6 +172,13 @@ export default defineComponent({
value-placeholder={t('project.dag.value')} value-placeholder={t('project.dag.value')}
/> />
</NFormItem> </NFormItem>
{props.definition && (
<NFormItem label=' ' path='timeoutFlag'>
<NCheckbox v-model:checked={formValue.value.release}>
{t('project.dag.online_directly')}
</NCheckbox>
</NFormItem>
)}
</NForm> </NForm>
</Modal> </Modal>
) )

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

@ -15,7 +15,7 @@
* limitations under the License. * 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 { useI18n } from 'vue-i18n'
import Styles from './dag.module.scss' import Styles from './dag.module.scss'
import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui' import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui'
@ -26,13 +26,15 @@ import {
FullscreenExitOutlined, FullscreenExitOutlined,
InfoCircleOutlined, InfoCircleOutlined,
FormatPainterOutlined, FormatPainterOutlined,
CopyOutlined CopyOutlined,
DeleteOutlined
} from '@vicons/antd' } from '@vicons/antd'
import { useNodeSearch, useTextCopy } from './dag-hooks' import { useNodeSearch, useTextCopy } from './dag-hooks'
import { DataUri } from '@antv/x6' import { DataUri } from '@antv/x6'
import { useFullscreen } from '@vueuse/core' import { useFullscreen } from '@vueuse/core'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useThemeStore } from '@/store/theme/theme' import { useThemeStore } from '@/store/theme/theme'
import type { Graph } from '@antv/x6'
const props = { const props = {
layoutToggle: { layoutToggle: {
@ -50,13 +52,13 @@ const props = {
export default defineComponent({ export default defineComponent({
name: 'workflow-dag-toolbar', name: 'workflow-dag-toolbar',
props, props,
emits: ['versionToggle', 'saveModelToggle'], emits: ['versionToggle', 'saveModelToggle', 'removeTasks'],
setup(props, context) { setup(props, context) {
const { t } = useI18n() const { t } = useI18n()
const themeStore = useThemeStore() const themeStore = useThemeStore()
const graph = inject('graph', ref()) const graph = inject<Ref<Graph | undefined>>('graph', ref())
const router = useRouter() const router = useRouter()
/** /**
@ -124,6 +126,22 @@ export default defineComponent({
*/ */
const { copy } = useTextCopy() 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 () => ( return () => (
<div <div
class={[ class={[
@ -209,6 +227,29 @@ export default defineComponent({
default: () => t('project.dag.download_png') default: () => t('project.dag.download_png')
}} }}
></NTooltip> ></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 */} {/* Toggle fullscreen */}
<NTooltip <NTooltip
v-slots={{ v-slots={{

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

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

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

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

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

@ -15,15 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { ref, onMounted } from 'vue' import { ref, onMounted, watch } from 'vue'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import type { Graph } from '@antv/x6' import type { Graph } from '@antv/x6'
import type { Coordinate, NodeData } from './types' import type { Coordinate, NodeData } from './types'
import { TaskType } from '@/views/projects/task/constants/task-type' 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 { useCellUpdate } from './dag-hooks'
import { WorkflowDefinition } from './types'
interface Options { interface Options {
graph: Ref<Graph | undefined> graph: Ref<Graph | undefined>
definition: Ref<WorkflowDefinition | undefined>
} }
/** /**
@ -32,7 +35,7 @@ interface Options {
* @returns * @returns
*/ */
export function useTaskEdit(options: Options) { export function useTaskEdit(options: Options) {
const { graph } = options const { graph, definition } = options
const { addNode, setNodeName } = useCellUpdate({ graph }) const { addNode, setNodeName } = useCellUpdate({ graph })
@ -57,6 +60,16 @@ export function useTaskEdit(options: Options) {
openTaskModal({ code, taskType: type, name: '' }) 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) { function openTaskModal(task: NodeData) {
currTask.value = task currTask.value = task
taskModalVisible.value = true taskModalVisible.value = true
@ -67,26 +80,22 @@ export function useTaskEdit(options: Options) {
* @param formRef * @param formRef
* @param from * @param from
*/ */
function taskConfirm({ formRef, form }: any) { function taskConfirm({ data }: any) {
formRef.validate((errors: any) => { const taskDef = formatParams(data).taskDefinitionJsonObj as NodeData
if (!errors) {
// override target config // override target config
taskDefinitions.value = taskDefinitions.value.map((task) => { taskDefinitions.value = taskDefinitions.value.map((task) => {
if (task.code === currTask.value?.code) { if (task.code === currTask.value?.code) {
setNodeName(task.code + '', form.name) setNodeName(task.code + '', taskDef.name)
console.log(form)
console.log(JSON.stringify(form))
return { return {
...taskDef,
code: task.code, code: task.code,
...form taskType: currTask.value.taskType
} }
} }
return task return task
}) })
taskModalVisible.value = false taskModalVisible.value = false
} }
})
}
/** /**
* The cancel event in task config modal * The cancel event in task config modal
@ -108,12 +117,17 @@ export function useTaskEdit(options: Options) {
} }
}) })
watch(definition, () => {
taskDefinitions.value = definition.value?.taskDefinitionList || []
})
return { return {
currTask, currTask,
taskModalVisible, taskModalVisible,
taskConfirm, taskConfirm,
taskCancel, taskCancel,
appendTask, 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 { defineComponent, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useThemeStore } from '@/store/theme/theme' import { useThemeStore } from '@/store/theme/theme'
import { useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import Dag from '../../components/dag' import Dag from '../../components/dag'
import { queryProcessDefinitionByCode } from '@/service/modules/process-definition' import {
import { WorkflowDefinition } from '../../components/dag/types' queryProcessDefinitionByCode,
updateProcessDefinition
} from '@/service/modules/process-definition'
import {
WorkflowDefinition,
SaveForm,
TaskDefinition,
Connect,
Location
} from '../../components/dag/types'
import Styles from './index.module.scss' import Styles from './index.module.scss'
interface SaveData {
saveForm: SaveForm
taskDefinitions: TaskDefinition[]
connects: Connect[]
locations: Location[]
}
export default defineComponent({ export default defineComponent({
name: 'WorkflowDefinitionDetails', name: 'WorkflowDefinitionDetails',
setup() { setup() {
const theme = useThemeStore() const theme = useThemeStore()
const route = useRoute() const route = useRoute()
const router = useRouter()
const message = useMessage()
const { t } = useI18n()
const projectCode = Number(route.params.projectCode) const projectCode = Number(route.params.projectCode)
const code = Number(route.params.code) 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(() => { onMounted(() => {
if (!code || !projectCode) return 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. * 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({ export default defineComponent({
name: 'WorkflowInstanceDetails', name: 'WorkflowInstanceDetails',
setup() { 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> <NCard>
<div class={styles['search-card']}> <div class={styles['search-card']}>
<div> <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')} {t('security.yarn_queue.create_queue')}
</NButton> </NButton>
</div> </div>
@ -130,7 +135,11 @@ const yarnQueueManage = defineComponent({
</div> </div>
</NCard> </NCard>
<Card class={styles['table-card']}> <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}> <div class={styles.pagination}>
<NPagination <NPagination
v-model:page={this.page} v-model:page={this.page}

Loading…
Cancel
Save