merge(sj_preview): 合并 dev_1.1.0-beta3

This commit is contained in:
xlsea 2024-07-11 09:23:20 +08:00
commit 495cbd5fb0
37 changed files with 511 additions and 250 deletions

2
.env
View File

@ -2,7 +2,7 @@ VITE_APP_TITLE=Snail Job
VITE_APP_DESC=A flexible, reliable, and fast platform for distributed task retry and distributed task scheduling.
VITE_APP_VERSION=1.1.0-beta2
VITE_APP_VERSION=1.1.0-beta3
VITE_APP_DEFAULT_TOKEN=SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj

View File

@ -4,9 +4,9 @@ import { NButton, NCode, NTag } from 'naive-ui';
import hljs from 'highlight.js/lib/core';
import json from 'highlight.js/lib/languages/json';
import { ref, render } from 'vue';
import { taskStatusRecord } from '@/constants/business';
import { taskStatusRecord, taskStatusRecordOptions } from '@/constants/business';
import { $t } from '@/locales';
import { parseArgsJson } from '@/utils/common';
import { isNotNull, parseArgsJson, translateOptions } from '@/utils/common';
import { useTable } from '@/hooks/common/table';
import { fetchGetJobTaskList, fetchGetJobTaskTree } from '@/service/api';
@ -19,11 +19,15 @@ hljs.registerLanguage('json', json);
interface Props {
/** row data */
rowData?: Api.JobBatch.JobBatch | null;
isRetry?: boolean;
}
const props = defineProps<Props>();
const props = withDefaults(defineProps<Props>(), {
rowData: null
});
interface Emits {
(e: 'retry'): void;
(e: 'showLog', rowData: Api.Job.JobTask): void;
}
@ -32,8 +36,9 @@ const emit = defineEmits<Emits>();
const expandedRowKeys = ref<DataTableRowKey[]>([]);
const argsDomMap = ref<Map<string, boolean>>(new Map());
const resultDomMap = ref<Map<string, boolean>>(new Map());
const { columns, columnChecks, data, loading, mobilePagination } = useTable({
const { columns, searchParams, columnChecks, data, getData, loading, mobilePagination } = useTable({
apiFn: fetchGetJobTaskList,
apiParams: {
page: 1,
@ -41,7 +46,8 @@ const { columns, columnChecks, data, loading, mobilePagination } = useTable({
groupName: props.rowData?.groupName,
taskBatchId: props.rowData?.id,
startId: 0,
fromIndex: 0
fromIndex: 0,
taskStatus: undefined
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
// the value can not be undefined, otherwise the property in Form will not be reactive
},
@ -133,6 +139,11 @@ const { columns, columnChecks, data, loading, mobilePagination } = useTable({
);
const handleView = () => {
if (resultDomMap.value.get(row.id)) {
const tr = document.querySelector(`#job-task-result-${row.id}`);
tr?.remove();
resultDomMap.value.set(row.id, false);
}
if (argsDomMap.value.get(row.id)) {
return;
}
@ -161,7 +172,7 @@ const { columns, columnChecks, data, loading, mobilePagination } = useTable({
<span class="w-28px ws-break-spaces">{`收起`}</span>
</NButton>
) : (
<NButton type="primary" text onClick={() => handleView()}>
<NButton type="primary" text disabled={!isNotNull(row.argsStr)} onClick={() => handleView()}>
<span class="w-28px ws-break-spaces">{`查看\n参数`}</span>
</NButton>
)}
@ -173,7 +184,61 @@ const { columns, columnChecks, data, loading, mobilePagination } = useTable({
key: 'resultMessage',
title: $t('page.jobBatch.jobTask.resultMessage'),
align: 'left',
minWidth: 120
minWidth: 120,
render: row => {
const argsDom = () => (
<td class="n-data-table-td n-data-table-td--last-col" colspan={columns.value.length || 9}>
<NCode
class={`max-h-300px overflow-auto ${String(row.parentId) !== '0' ? 'pl-36px' : ''}`}
hljs={hljs}
code={row.resultMessage}
language="json"
show-line-numbers
/>
</td>
);
const handleView = () => {
if (argsDomMap.value.get(row.id)) {
const tr = document.querySelector(`#job-task-args-${row.id}`);
tr?.remove();
argsDomMap.value.set(row.id, false);
}
if (resultDomMap.value.get(row.id)) {
return;
}
const tr = document.querySelector(`#job-task-${row.id}`);
const argsTr = document.createElement('tr');
argsTr.setAttribute('id', `job-task-result-${row.id}`);
argsTr.setAttribute('class', 'n-data-table-tr n-data-table-tr--expanded');
tr?.after(argsTr);
render(argsDom(), argsTr!);
resultDomMap.value.set(row.id, true);
};
const handleClose = () => {
if (!resultDomMap.value.get(row.id)) {
return;
}
const tr = document.querySelector(`#job-task-result-${row.id}`);
tr?.remove();
resultDomMap.value.set(row.id, false);
};
return (
<>
{resultDomMap.value.get(row.id) ? (
<NButton type="primary" text onClick={() => handleClose()}>
<span class="w-28px ws-break-spaces">{`收起`}</span>
</NButton>
) : (
<NButton type="primary" text disabled={!isNotNull(row.resultMessage)} onClick={() => handleView()}>
<span class="w-28px ws-break-spaces">{`查看\n结果`}</span>
</NButton>
)}
</>
);
}
},
{
key: 'retryCount',
@ -217,6 +282,21 @@ const onUpdatePage = (_: number) => {
expandedRowKeys.value = [];
};
async function flushed() {
searchParams.taskStatus = undefined;
await getData();
}
const retry = async () => {
emit('retry');
};
const isRetry = () => {
return (
props.rowData?.taskBatchStatus === 4 || props.rowData?.taskBatchStatus === 5 || props.rowData?.taskBatchStatus === 6
);
};
const init = () => {
columnChecks.value = columnChecks.value.filter(column => {
if (!['4', '5'].includes(String(props.rowData?.taskType) || '-1')) {
@ -231,24 +311,56 @@ init();
</script>
<template>
<NDataTable
:columns="columns"
:data="data"
:loading="loading"
remote
:scroll-x="1000"
:row-key="row => row.id"
:pagination="mobilePagination"
:indent="16"
:cascade="false"
allow-checking-not-loaded
:expanded-row-keys="expandedRowKeys"
class="sm:h-full"
:row-props="row => ({ id: `job-task-${row.id}` })"
@update:expanded-row-keys="onExpandedRowKeys"
@update:page="onUpdatePage"
@load="onLoad"
/>
<NCard
:bordered="false"
size="small"
class="sm:flex-1-hidden card-wrapper pt-16px"
:content-style="{ padding: 0 }"
:header-style="{ padding: 0 }"
>
<template #header>
<NSelect
v-model:value="searchParams.taskStatus"
clearable
class="max-w-180px"
:options="translateOptions(taskStatusRecordOptions)"
placeholder="请选择状态"
@update:value="getData"
/>
</template>
<template #header-extra>
<NButton class="mr-16px" @click="flushed">
<template #icon>
<icon-ant-design:sync-outlined class="text-icon" />
</template>
刷新
</NButton>
<NButton v-if="isRetry()" @click="retry">
<template #icon>
<icon-ant-design:redo-outlined class="text-icon" />
</template>
重试
</NButton>
</template>
<NDataTable
:columns="columns"
:data="data"
:loading="loading"
remote
:scroll-x="1000"
:row-key="row => row.id"
:pagination="mobilePagination"
:indent="16"
:cascade="false"
allow-checking-not-loaded
:expanded-row-keys="expandedRowKeys"
class="mt-16px sm:h-full"
:row-props="row => ({ id: `job-task-${row.id}` })"
@update:expanded-row-keys="onExpandedRowKeys"
@update:page="onUpdatePage"
@load="onLoad"
/>
</NCard>
</template>
<style scoped></style>

View File

@ -41,8 +41,8 @@ async function search() {
}
const btnSpan = computed(() => {
const keyNum = Object.keys(props.model).length - 1;
return props.btnSpan || (keyNum % 4 !== 0 ? `24 m:12 m:${(4 - ((keyNum - 1) % 4)) * 6}` : '24');
const keyNum = Object.keys(props.model).length - 2;
return props.btnSpan || (keyNum % 4 !== 0 ? `24 m:12 m:${(4 - (keyNum % 4)) * 6}` : '24');
});
</script>

View File

@ -1,19 +1,13 @@
<script setup lang="tsx">
import { nextTick, ref, useSlots, watch } from 'vue';
import { NButton, NTag } from 'naive-ui';
import { NTag } from 'naive-ui';
import hljs from 'highlight.js/lib/core';
import json from 'highlight.js/lib/languages/json';
import { isNotNull, translateOptions } from '@/utils/common';
import {
jobExecutorEnum,
jobOperationReasonEnum,
jobStatusEnum,
taskBatchStatusEnum,
taskStatusRecordOptions
} from '@/constants/business';
import { isNotNull } from '@/utils/common';
import { jobExecutorEnum, jobOperationReasonEnum, jobStatusEnum, taskBatchStatusEnum } from '@/constants/business';
import { useWorkflowStore } from '@/store/modules/workflow';
import { $t } from '@/locales';
import { fetchGetJobBatchDetail, fetchGetJobDetail, fetchGetJobTaskList, fetchWorkflowNodeRetry } from '@/service/api';
import { fetchGetJobBatchDetail, fetchGetJobDetail, fetchWorkflowNodeRetry } from '@/service/api';
defineOptions({
name: 'DetailCard'
@ -45,11 +39,8 @@ const store = useWorkflowStore();
const visible = ref(false);
const logOpen = ref(false);
const spinning = ref(false);
const loading = ref(false);
const currentIndex = ref(1);
const taskStatusSearch = ref();
const jobData = ref<Workflow.JobTaskType>({});
const dataSource = ref<Workflow.JobBatchType[]>([]);
const pagination = ref({
page: 1,
@ -61,13 +52,11 @@ const pagination = ref({
pagination.value.page = page;
const id = props.ids[currentIndex.value - 1];
getBatchDetail(id);
getRows(id, page);
},
onUpdatePageSize: async (pageSize: number) => {
pagination.value.pageSize = pageSize;
const id = props.ids[currentIndex.value - 1];
getBatchDetail(id);
getRows(id, pagination.value.page);
}
});
@ -104,27 +93,6 @@ async function getBatchDetail(id: string) {
}
}
async function getRows(id: string, page: number = 1) {
loading.value = true;
const { data, error } = await fetchGetJobTaskList({
groupName: store.groupName!,
taskBatchId: id ?? '0',
taskStatus: taskStatusSearch.value,
page,
size: pagination.value.pageSize
});
if (!error) {
pagination.value.pageCount = data.total;
dataSource.value = data.data;
loading.value = false;
}
}
async function flushed(id: string) {
taskStatusSearch.value = null;
await getRows(id);
}
const idList = ref<string[]>([]);
function onLoad() {
@ -133,11 +101,9 @@ function onLoad() {
nextTick(() => {
if (props.ids.length > 0) {
getBatchDetail(props.ids[0]);
getRows(props.ids[0]);
} else if (props.id) {
idList.value = [jobData.value.taskBatchId!];
getDetail(props.id);
getRows(jobData.value.taskBatchId!);
}
});
}
@ -149,17 +115,13 @@ const getLogRows = (task: Workflow.JobTaskType) => {
logOpen.value = true;
};
const retry = async (item: Workflow.JobTaskType) => {
const { error } = await fetchWorkflowNodeRetry(store.id!, item.workflowNodeId!);
const retry = async () => {
const { error } = await fetchWorkflowNodeRetry(store.id!, jobData.value.workflowNodeId!);
if (!error) {
window.$message?.success('执行重试成功');
}
};
const isRetry = (taskBatchStatus: number) => {
return taskBatchStatus === 4 || taskBatchStatus === 5 || taskBatchStatus === 6;
};
function getTagColor(color: string) {
return {
color: `${color}18`,
@ -172,7 +134,6 @@ const onUpdatePage = (page: number) => {
currentIndex.value = page;
const id = props.ids[page - 1];
getBatchDetail(id);
getRows(id, page);
};
</script>
@ -240,39 +201,7 @@ const onUpdatePage = (page: number) => {
<template #tab>
<span>任务项列表</span>
</template>
<NCard
:bordered="false"
size="small"
class="sm:flex-1-hidden card-wrapper pt-16px"
:content-style="{ padding: 0 }"
:header-style="{ padding: 0 }"
>
<template #header>
<NSelect
v-model:value="taskStatusSearch"
clearable
class="max-w-180px"
:options="translateOptions(taskStatusRecordOptions)"
placeholder="请选择状态"
@update:value="getRows(item)"
/>
</template>
<template #header-extra>
<NButton class="mr-16px" @click="flushed(item)">
<template #icon>
<icon-ant-design:sync-outlined class="text-icon" />
</template>
刷新
</NButton>
<NButton v-if="isRetry(jobData.taskBatchStatus!)" @click="retry(jobData)">
<template #icon>
<icon-ant-design:redo-outlined class="text-icon" />
</template>
重试
</NButton>
</template>
<JobTaskListTable class="mt-16px" :row-data="jobData as any" @show-log="getLogRows" />
</NCard>
<JobTaskListTable :row-data="jobData as any" @show-log="getLogRows" @retry="retry" />
</NTabPane>
</NTabs>
</NTabPane>

View File

@ -117,6 +117,7 @@ const jobTaskChange = (_: string, option: { label: string; value: number }) => {
<NFormItem path="jobTask.jobId" label="所属任务" placeholder="请选择任务">
<NSelect
v-model:value="form.jobTask!.jobId"
filterable
:options="
jobList.map(job => {
return {

View File

@ -160,7 +160,8 @@ export const contentTypeOptions = transformRecordToNumberOption(contentTypeRecor
/** 执行器类型 */
export const executorTypeRecord: Record<Api.Common.ExecutorType, App.I18n.I18nKey> = {
1: 'common.executorType.items.java'
1: 'common.executorType.items.java',
2: 'common.executorType.items.python'
};
export const executorTypeRecordOptions = transformRecordToNumberOption(executorTypeRecord);

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, nextTick, onUnmounted, reactive } from 'vue';
import { computed } from 'vue';
import { useFullscreen } from '@vueuse/core';
import { useAppStore } from '@/store/modules/app';
import { useThemeStore } from '@/store/modules/theme';
@ -49,21 +49,6 @@ const headerMenus = computed(() => {
const href = (url: string) => {
window.open(url, '_blank');
};
const state = reactive({ width: 0 });
const getState = () => {
state.width = document.documentElement.clientWidth;
};
nextTick(() => {
getState();
window.addEventListener('resize', getState);
});
onUnmounted(() => {
window.removeEventListener('resize', getState);
});
</script>
<template>
@ -72,40 +57,40 @@ onUnmounted(() => {
<HorizontalMenu v-if="showMenu" mode="horizontal" :menus="headerMenus" class="px-12px" />
<div v-else class="h-full flex-y-center flex-1-hidden">
<MenuToggler v-if="showMenuToggler" :collapsed="appStore.siderCollapse" @click="appStore.toggleSiderCollapse" />
<GlobalBreadcrumb v-if="!appStore.isMobile && state.width > 900" class="ml-12px" />
<GlobalBreadcrumb v-if="!appStore.isMobile" class="ml-12px" />
</div>
<div class="h-full flex-y-center justify-end">
<NamespaceSelect :is-mobile="appStore.isMobile || state.width < 970" />
<NamespaceSelect />
<GlobalSearch />
<ButtonIcon
v-if="!appStore.isMobile && state.width > 760"
class="color-#c71d23"
v-if="!appStore.isMobile"
class="color-#c71d23 xl:block sm:hidden"
tooltip-content="Gitee"
icon="simple-icons:gitee"
@click="href('https://gitee.com/aizuda/snail-job')"
/>
<ButtonIcon
v-if="!appStore.isMobile && state.width > 760"
v-if="!appStore.isMobile"
tooltip-content="Github"
class="color-#010409 dark:color-#e6edf3"
class="color-#010409 xl:block sm:hidden dark:color-#e6edf3"
icon="simple-icons:github"
@click="href('https://github.com/aizuda/snail-job')"
/>
<ButtonIcon
v-if="!appStore.isMobile && state.width > 760"
v-if="!appStore.isMobile"
tooltip-content="Document"
class="color-#272636 dark:color-#f0f2f5"
icon="material-symbols:unknown-document-outline"
@click="href('https://snailjob.opensnail.com/')"
/>
<FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" />
<FullScreen v-if="!appStore.isMobile" class="xl:block sm:hidden" :full="isFullscreen" @click="toggle" />
<LangSwitch :lang="appStore.locale" :lang-options="appStore.localeOptions" @change-lang="appStore.changeLocale" />
<ThemeSchemaSwitch
:theme-schema="themeStore.themeScheme"
:is-dark="themeStore.darkMode"
@switch="themeStore.toggleThemeScheme"
/>
<ThemeButton v-if="!appStore.isMobile && state.width > 970" />
<ThemeButton v-if="!appStore.isMobile" class="xl:block sm:hidden" />
<UserAvatar />
</div>
</DarkModeContainer>

View File

@ -106,7 +106,7 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra
<SettingItem v-if="isDev" key="8" :label="$t('theme.watermark.visible')">
<NSwitch v-model:value="themeStore.watermark.visible" />
</SettingItem>
<SettingItem v-if="isDev" key="8-1" :label="$t('theme.watermark.text')">
<SettingItem v-if="false" key="8-1" :label="$t('theme.watermark.text')">
<NInput v-model:value="themeStore.watermark.text" size="small" :step="1" class="max-w-180px" />
</SettingItem>
</TransitionGroup>

View File

@ -135,7 +135,8 @@ const local: App.I18n.Schema = {
label: 'Executor Type',
form: 'Please enter executor type',
items: {
java: 'Java'
java: 'Java',
python: 'Python'
}
},
taskType: {

View File

@ -135,7 +135,8 @@ const local: App.I18n.Schema = {
label: '执行器类型',
form: '请选择执行器类型',
items: {
java: 'Java'
java: 'Java',
python: 'Python'
}
},
taskType: {
@ -303,7 +304,7 @@ const local: App.I18n.Schema = {
resetSuccessMsg: '重置成功'
},
watermark: {
visible: '开启',
visible: '开启水印',
text: '水印文字'
}
},

View File

@ -59,3 +59,11 @@ export function fetchGetAllGroupConfigList(data: string[]) {
data
});
}
/** delete group by id */
export function fetchDeleteGroup(groupName: string) {
return request<boolean>({
url: `/group/${groupName}`,
method: 'delete'
});
}

View File

@ -31,3 +31,21 @@ export function fetchJobBatchRetry(jobId: string) {
method: 'post'
});
}
/** delete job */
export function fetchDeleteJobBatch(id: string) {
return request<boolean>({
url: `/job/batch/ids`,
method: 'delete',
data: [id]
});
}
/** delete job */
export function fetchBatchDeleteJobBatch(data: string[]) {
return request<boolean>({
url: '/job/batch/ids',
method: 'delete',
data
});
}

View File

@ -71,11 +71,12 @@ export function fetchUpdateJobStatus(data: Api.Job.JobUpdateJobStatusRequestVO)
});
}
/** delete Job by id */
export function fetchDeleteJob(id: string) {
/** batch delete Job by id */
export function fetchBatchDeleteJob(data: string[]) {
return request<boolean>({
url: `/job/${id}`,
method: 'delete'
url: '/job/ids',
method: 'delete',
data
});
}

View File

@ -26,3 +26,11 @@ export function fetchEditNamespace(data: Api.Namespace.Namespace) {
data
});
}
/** delete namespace by id */
export function fetchDeleteNamespace(uniqueId: string) {
return request<boolean>({
url: `/namespace/${uniqueId}`,
method: 'delete'
});
}

View File

@ -43,3 +43,21 @@ export function fetchUpdateSceneStatus(id: string, status: Api.Common.EnableStat
method: 'put'
});
}
/** delete retry scene status */
export function fetchDeleteRetryScene(id: string) {
return request({
url: '/scene-config/ids',
method: 'delete',
data: [id]
});
}
/** batch delete retry scene status */
export function fetchBatchDeleteRetryScene(data: string[]) {
return request({
url: '/scene-config/ids',
method: 'delete',
data
});
}

View File

@ -35,6 +35,15 @@ export function fetchDelUser(id: number) {
});
}
/** batch delete user */
export function fetchBatchDelteUser(data: string[]) {
return request<boolean>({
url: `/user/ids`,
method: 'delete',
data
});
}
/** update user password */
export function fetchUpdateUserPassword(data: Api.UserManager.UpdateUserPassword) {
return request<boolean>({

View File

@ -43,10 +43,11 @@ export function fetchUpdateWorkflowStatus(id: string) {
});
}
export function fetchDelWorkflow(id: string) {
export function fetchBatchDeleteWorkflow(data: string[]) {
return request({
url: `/workflow/${id}`,
method: 'delete'
url: '/workflow/ids',
method: 'delete',
data
});
}
@ -60,7 +61,7 @@ export function fetchStopWorkflowBatch(id: string) {
export function fetchWorkflowNodeRetry(id: string, workflowNodeId: string) {
return request<null>({
url: `/workflow/node/retry/${workflowNodeId}/${id}`,
method: 'get'
method: 'post'
});
}
@ -118,3 +119,19 @@ export function fetchNodeStop(nodeId: string, taskBatchId: string) {
method: 'post'
});
}
export function fetchDeleteWorkflowBatch(id: string) {
return request({
url: '/workflow/batch/ids',
method: 'delete',
data: [id]
});
}
export function fetchBatchDeleteWorkflowBatch(data: string[]) {
return request({
url: '/workflow/batch/ids',
method: 'delete',
data
});
}

View File

@ -10,11 +10,14 @@ import { $t } from '@/locales';
import { roleTypeRecord } from '@/constants/business';
import { useRouteStore } from '../route';
import { useTabStore } from '../tab';
import { useThemeStore } from '../theme';
import { clearAuthStorage, getToken } from './shared';
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
const appTitle = import.meta.env.VITE_APP_TITLE || 'Snail Job';
const route = useRoute();
const routeStore = useRouteStore();
const themeStore = useThemeStore();
const tabStore = useTabStore();
const { toLogin, redirectFromLogin } = useRouterPush(false);
const { loading: loginLoading, startLoading, endLoading } = useLoading();
@ -54,6 +57,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
authStore.$reset();
themeStore.setWatermarkText(appTitle);
if (!route.meta.constant) {
await toLogin();
}
@ -136,6 +141,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
info!.roles = [roleTypeRecord[info.role]];
localStg.set('userInfo', info);
Object.assign(userInfo, info);
themeStore.setWatermarkText(`${userInfo.userName}@${appTitle}`);
return true;
}

View File

@ -59,7 +59,7 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
const { setWatermark, clearWatermark } = useWatermark({ id: 'global_watermark_id' });
/** 开启水印 */
function toggleWatermark(visible: boolean) {
function toggleWatermark(visible: boolean = false) {
visible ? setWatermark(settings.value.watermark.text) : clearWatermark();
}

View File

@ -49,7 +49,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
},
watermark: {
visible: true,
text: 'Snail Job'
text: import.meta.env.VITE_APP_TITLE || 'Snail Job'
}
};

View File

@ -91,8 +91,8 @@ declare namespace Api {
/** 工作流节点状态 */
type WorkFlowNodeStatus = 0 | 1;
/** 执行器类型 1:Java */
type ExecutorType = 1;
/** 执行器类型 1:Java 2:Python */
type ExecutorType = 1 | 2;
/** 触发类型 2:固定时间 3:CRON 表达式 99:工作流 */
type TriggerType = 2 | 3 | 99;

View File

@ -395,6 +395,7 @@ declare namespace App {
form: string;
items: {
java: string;
python: string;
};
};
taskType: {

View File

@ -2,7 +2,7 @@
import { NButton, NPopconfirm, NTag } from 'naive-ui';
import { ref } from 'vue';
import { useBoolean } from '@sa/hooks';
import { fetchGetGroupConfigList, fetchUpdateGroupStatus } from '@/service/api';
import { fetchDeleteGroup, fetchGetGroupConfigList, fetchUpdateGroupStatus } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
@ -31,11 +31,6 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
groupStatus: null
},
columns: () => [
{
type: 'selection',
align: 'center',
width: 48
},
{
key: 'id',
title: $t('common.index'),
@ -142,6 +137,17 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
<NButton type="primary" text ghost size="small" onClick={() => edit(row.id!)}>
{$t('common.edit')}
</NButton>
<n-divider vertical />
<NPopconfirm onPositiveClick={() => handleDelete(row.groupName!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</div>
);
}
@ -155,7 +161,8 @@ const {
editingData,
handleAdd,
handleEdit,
checkedRowKeys
checkedRowKeys,
onDeleted
// closeDrawer
} = useTableOperate(data, getData);
@ -163,6 +170,12 @@ function edit(id: string) {
handleEdit(id);
}
async function handleDelete(groupName: string) {
const { error } = await fetchDeleteGroup(groupName);
if (error) return;
onDeleted();
}
function body(): Api.GroupConfig.ExportGroupConfig {
return {
groupName: searchParams.groupName,
@ -189,10 +202,9 @@ function handleExport() {
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-delete="false"
:show-add="hasAuth('R_ADMIN')"
:show-delete="false"
@add="handleAdd"
@refresh="getData"
>

View File

@ -2,10 +2,16 @@
import { NButton, NPopconfirm, NTag, NTooltip } from 'naive-ui';
import { useBoolean } from '@sa/hooks';
import { ref } from 'vue';
import { fetchGetJobBatchList, fetchJobBatchRetry, fetchJobBatchStop } from '@/service/api';
import {
fetchBatchDeleteJobBatch,
fetchDeleteJobBatch,
fetchGetJobBatchList,
fetchJobBatchRetry,
fetchJobBatchStop
} from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable } from '@/hooks/common/table';
import { useTable, useTableOperate } from '@/hooks/common/table';
import { operationReasonRecord, taskBatchStatusRecord, taskTypeRecord } from '@/constants/business';
import { monthRangeISO8601, tagColor } from '@/utils/common';
import SvgIcon from '@/components/custom/svg-icon.vue';
@ -39,6 +45,9 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
taskBatchStatus
},
columns: () => [
{
type: 'selection'
},
{
key: 'id',
align: 'center',
@ -158,7 +167,7 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 130,
width: 170,
render: row => {
const stopBtn = () => {
if (row.taskBatchStatus === 1 || row.taskBatchStatus === 2) {
@ -208,6 +217,17 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
</NButton>
{stopBtn()}
{retryBtn()}
<n-divider vertical />
<NPopconfirm onPositiveClick={() => handleDelete(row.id!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</div>
);
}
@ -215,6 +235,25 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
]
});
const {
checkedRowKeys,
onDeleted,
onBatchDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleDelete(id: string) {
const { error } = await fetchDeleteJobBatch(id);
if (error) return;
onDeleted();
}
async function handleBatchDelete() {
const { error } = await fetchBatchDeleteJobBatch(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
function handleLog(row: Api.JobBatch.JobBatch) {
detailData.value = row;
setDetailLog(true);
@ -251,13 +290,15 @@ async function handleStopJob(id: string) {
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-delete="false"
:show-add="false"
@delete="handleBatchDelete"
@refresh="getData"
/>
</template>
<NDataTable
v-model:checked-row-keys="checkedRowKeys"
:columns="columns"
:data="data"
:flex-height="!appStore.isMobile"

View File

@ -3,6 +3,7 @@ import { ref } from 'vue';
import { executorTypeRecord, operationReasonRecord, taskBatchStatusRecord } from '@/constants/business';
import { $t } from '@/locales';
import { tagColor } from '@/utils/common';
import { fetchJobBatchRetry } from '@/service/api';
defineOptions({
name: 'JobBatchDetailDrawer'
@ -14,7 +15,7 @@ interface Props {
log?: boolean;
}
withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
log: false,
rowData: null
});
@ -30,13 +31,20 @@ async function openLog(row: Api.Job.JobTask) {
logShow.value = true;
taskData.value = row;
}
async function retry() {
const { error } = await fetchJobBatchRetry(props.rowData!.id!);
if (!error) {
window.$message?.success($t('common.operateSuccess'));
}
}
</script>
<template>
<DetailDrawer v-model="visible" :title="$t('page.jobBatch.detail')" :width="['50%', '90%']">
<NTabs type="segment" animated :default-value="log ? 1 : 0">
<NTabPane :name="0" :tab="$t('page.log.info')">
<NDescriptions label-placement="top" bordered :column="2">
<NDescriptions class="pt-16px" label-placement="top" bordered :column="2">
<NDescriptionsItem :label="$t('page.jobBatch.groupName')">{{ rowData?.groupName }}</NDescriptionsItem>
<NDescriptionsItem :label="$t('page.jobBatch.jobName')">{{ rowData?.jobName }}</NDescriptionsItem>
<NDescriptionsItem :label="$t('page.jobBatch.taskBatchStatus')">
@ -62,11 +70,15 @@ async function openLog(row: Api.Job.JobTask) {
</NDescriptions>
</NTabPane>
<NTabPane :name="1" :tab="$t('page.log.title')" display-directive="if">
<JobTaskListTable :row-data="rowData" @show-log="openLog" />
<JobTaskListTable :row-data="rowData" @show-log="openLog" @retry="retry" />
</NTabPane>
</NTabs>
</DetailDrawer>
<LogDrawer v-model:show="logShow" :title="$t('page.log.title')" :task-data="taskData" />
</template>
<style scoped></style>
<style scoped>
:deep(.n-tab-pane) {
padding-top: 0 !important;
}
</style>

View File

@ -2,7 +2,7 @@
import { NButton, NPopconfirm, NTag } from 'naive-ui';
import { useBoolean } from '@sa/hooks';
import { ref } from 'vue';
import { fetchDeleteJob, fetchGetJobPage, fetchTriggerJob, fetchUpdateJobStatus } from '@/service/api';
import { fetchBatchDeleteJob, fetchGetJobPage, fetchTriggerJob, fetchUpdateJobStatus } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
@ -229,16 +229,23 @@ const {
handleAdd,
handleEdit,
checkedRowKeys,
onDeleted
onDeleted,
onBatchDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleDelete(id: string) {
const { error } = await fetchDeleteJob(id);
const { error } = await fetchBatchDeleteJob([id]);
if (error) return;
onDeleted();
}
async function handleBatchDelete() {
const { error } = await fetchBatchDeleteJob(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
function edit(id: string) {
handleEdit(id);
}
@ -285,8 +292,9 @@ function handleExport() {
<TableHeaderOperation
v-model:columns="columnChecks"
:loading="loading"
:show-delete="false"
:disabled-delete="checkedRowKeys.length === 0"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
>
<template #addAfter>

View File

@ -26,7 +26,7 @@ const visible = defineModel<boolean>('visible', {
</script>
<template>
<OperateDrawer v-model="visible" :title="$t('page.jobTask.detail')">
<DetailDrawer v-model="visible" :title="$t('page.jobTask.detail')" :width="['50%', '90%']">
<NDescriptions label-placement="top" bordered :column="2">
<NDescriptionsItem :label="$t('page.jobTask.groupName')">{{ rowData?.groupName }}</NDescriptionsItem>
<NDescriptionsItem :label="$t('page.jobTask.jobName')">{{ rowData?.jobName }}</NDescriptionsItem>
@ -74,7 +74,7 @@ const visible = defineModel<boolean>('visible', {
{{ rowData?.description }}
</NDescriptionsItem>
</NDescriptions>
</OperateDrawer>
</DetailDrawer>
</template>
<style scoped></style>

View File

@ -1,7 +1,7 @@
<script setup lang="tsx">
import { NButton } from 'naive-ui';
import { NButton, NPopconfirm } from 'naive-ui';
import { ref } from 'vue';
import { fetchGetNamespaceList } from '@/service/api';
import { fetchDeleteNamespace, fetchGetNamespaceList } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
@ -28,12 +28,12 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
keyword: null
},
columns: () => [
// {
// key: 'id',
// title: $t('common.index'),
// align: 'center',
// width: 64
// },
{
key: 'id',
title: $t('common.index'),
align: 'center',
width: 64
},
{
key: 'name',
title: $t('page.namespace.name'),
@ -77,7 +77,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 64,
width: 80,
render: row => (
<div class="flex-center gap-8px">
<NButton type="primary" text ghost size="small" onClick={() => edit(row.id!)}>
@ -89,6 +89,17 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
<NButton type="warning" text ghost size="small" onClick={() => handleChange(row.uniqueId!)}>
{$t('common.switch')}
</NButton>
<n-divider vertical />
<NPopconfirm onPositiveClick={() => handleDelete(row.uniqueId!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</>
) : (
''
@ -105,13 +116,20 @@ const {
editingData,
handleAdd,
handleEdit,
checkedRowKeys
checkedRowKeys,
onDeleted
// closeDrawer
} = useTableOperate(data, getData);
function edit(id: string) {
handleEdit(id);
}
async function handleDelete(uniqueId: string) {
const { error } = await fetchDeleteNamespace(uniqueId);
if (error) return;
onDeleted();
}
</script>
<template>

View File

@ -190,16 +190,15 @@ const {
editingData,
handleAdd,
handleEdit,
checkedRowKeys
checkedRowKeys,
onBatchDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleBatchDelete() {
const { error } = await fetchBatchDeleteNotify(checkedRowKeys.value);
if (!error) {
window.$message?.success($t('common.deleteSuccess'));
getData();
}
if (error) return;
onBatchDeleted();
}
async function handleDelete(id: string) {

View File

@ -114,16 +114,15 @@ const {
editingData,
handleAdd,
handleEdit,
checkedRowKeys
checkedRowKeys,
onBatchDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleBatchDelete() {
const { error } = await fetchDeleteNotifyRecipient(checkedRowKeys.value);
if (!error) {
window.$message?.success($t('common.deleteSuccess'));
getData();
}
if (error) return;
onBatchDeleted();
}
async function handleDelete(id: string) {

View File

@ -136,7 +136,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
]
});
const { handleAdd, checkedRowKeys } = useTableOperate(data, getData);
const { handleAdd, checkedRowKeys, onDeleted, onBatchDeleted } = useTableOperate(data, getData);
async function handleBatchDelete() {
// request
@ -145,8 +145,8 @@ async function handleBatchDelete() {
groupName: searchParams.groupName!
});
if (error) return;
window.$message?.success($t('common.deleteSuccess'));
getData();
if (error) return;
onBatchDeleted();
}
async function handleBatchRollback() {
@ -163,8 +163,7 @@ async function handleBatchRollback() {
async function handleDelete(row: Api.RetryDeadLetter.DeadLetter) {
const { error } = await fetchDeleteRetryDeadLetter({ ids: [row.id!], groupName: row.groupName! });
if (error) return;
window.$message?.success($t('common.deleteSuccess'));
getData();
onDeleted();
}
async function loadRetryInfo(row: Api.RetryDeadLetter.DeadLetter) {

View File

@ -42,7 +42,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
type: 'selection',
align: 'center',
width: 48,
disabled: row => row.retryStatus !== 1
disabled: row => row.retryStatus === 0
},
{
key: 'id',
@ -130,7 +130,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 130,
width: 80,
render: row => (
<div class="flex-center gap-8px">
{row.retryStatus === 1 ? (
@ -153,20 +153,18 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
]
});
const { checkedRowKeys } = useTableOperate(data, getData);
const { checkedRowKeys, onDeleted, onBatchDeleted } = useTableOperate(data, getData);
async function handleBatchDelete() {
const { error } = await fetchBatchDeleteRetryLog(checkedRowKeys.value as any[]);
if (!error) {
window.$message?.success($t('common.deleteSuccess'));
getData();
}
if (error) return;
onBatchDeleted();
}
async function handleDelete(id: any) {
await fetchDeleteRetryLog(id);
window.$message?.success($t('common.deleteSuccess'));
getData();
const { error } = await fetchDeleteRetryLog(id);
if (error) return;
onDeleted();
}
async function loadRetryInfo(row: Api.RetryLog.RetryLog) {

View File

@ -2,7 +2,12 @@
import { NButton, NPopconfirm, NTag } from 'naive-ui';
import { ref } from 'vue';
import { useBoolean } from '@sa/hooks';
import { fetchGetRetryScenePageList, fetchUpdateSceneStatus } from '@/service/api';
import {
fetchBatchDeleteRetryScene,
fetchDeleteRetryScene,
fetchGetRetryScenePageList,
fetchUpdateSceneStatus
} from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
@ -47,6 +52,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
},
{
key: 'sceneName',
align: 'center',
title: $t('page.retryScene.sceneName'),
fixed: 'left',
width: 120,
@ -161,27 +167,48 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
title: $t('common.operate'),
align: 'center',
fixed: 'right',
width: 120,
width: 100,
render: row => (
<div class="flex-center gap-8px">
<NButton type="primary" text ghost size="small" onClick={() => edit(row.id!)}>
{$t('common.edit')}
</NButton>
<n-divider vertical />
<NPopconfirm onPositiveClick={() => handleDelete(row.id!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</div>
)
}
]
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys } = useTableOperate(
data,
getData
);
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onDeleted, onBatchDeleted } =
useTableOperate(data, getData);
function edit(id: string) {
handleEdit(id);
}
async function handleDelete(id: string) {
const { error } = await fetchDeleteRetryScene(id);
if (error) return;
onDeleted();
}
async function handleBatchDelete() {
const { error } = await fetchBatchDeleteRetryScene(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
function triggerInterval(backOff: number, maxRetryCount: number) {
if (backOff !== 1) {
return '';
@ -223,8 +250,8 @@ function handleExport() {
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-delete="false"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
>
<template #addAfter>

View File

@ -43,7 +43,8 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
{
type: 'selection',
align: 'center',
width: 48
width: 48,
disabled: row => row.retryStatus === 0
},
{
key: 'id',
@ -54,7 +55,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
{
key: 'uniqueId',
title: $t('page.retryTask.uniqueId'),
align: 'left',
align: 'center',
fixed: 'left',
minWidth: 120,
render: row => {
@ -149,7 +150,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
{
key: 'operate',
title: $t('common.operate'),
align: 'left',
align: 'center',
width: 260,
fixed: 'right',
render: row => (
@ -259,7 +260,6 @@ const { bool: batchAddDrawerVisible, setTrue: openBatchAddDrawer } = useBoolean(
async function handleDelete(groupName: string, id: string) {
const { error } = await fetchBatchDeleteRetryTask({ groupName, ids: [id] });
if (error) return;
onDeleted();
}
@ -274,7 +274,6 @@ async function handleBatchDelete() {
const groupName = data.value[0].groupName;
const { error } = await fetchBatchDeleteRetryTask({ groupName, ids });
if (error) return;
onBatchDeleted();
}

View File

@ -2,7 +2,7 @@
import { NButton, NPopconfirm, NTag } from 'naive-ui';
import { ref } from 'vue';
import { useBoolean } from '@sa/hooks';
import { fetchDelUser, fetchGetUserPageList } from '@/service/api';
import { fetchBatchDelteUser, fetchDelUser, fetchGetUserPageList } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
@ -30,9 +30,9 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
columns: () => [
{
key: 'permissions',
align: 'left',
align: 'center',
type: 'expand',
minWidth: 10,
minWidth: 36,
renderExpand: row => {
return (
<div>
@ -57,6 +57,9 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
);
}
},
{
type: 'selection'
},
{
key: 'id',
title: $t('common.index'),
@ -144,16 +147,19 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
]
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys } = useTableOperate(
data,
getData
);
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onDeleted, onBatchDeleted } =
useTableOperate(data, getData);
async function handleDelete(id: string) {
const { error } = await fetchDelUser(id as any);
if (error) return;
getData();
window.$message?.success($t('common.deleteSuccess'));
onDeleted();
}
async function handleBatchDelete() {
const { error } = await fetchBatchDelteUser(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
function edit(id: string) {
@ -176,8 +182,8 @@ function edit(id: string) {
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-delete="false"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
/>
</template>

View File

@ -1,7 +1,12 @@
<script setup lang="tsx">
import { NButton, NPopconfirm, NTag } from 'naive-ui';
import { useRouter } from 'vue-router';
import { fetchGetWorkflowBatchList, fetchStopWorkflowBatch } from '@/service/api';
import {
fetchBatchDeleteWorkflowBatch,
fetchDeleteWorkflowBatch,
fetchGetWorkflowBatchList,
fetchStopWorkflowBatch
} from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
@ -35,6 +40,9 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
taskBatchStatus
},
columns: () => [
{
type: 'selection'
},
{
key: 'id',
title: $t('common.index'),
@ -121,19 +129,32 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
render: row => (
<div class="flex-center gap-8px">
{row?.taskBatchStatus === 1 || row?.taskBatchStatus === 2 ? (
<NPopconfirm onPositiveClick={() => handleStop(row.id!)}>
{{
default: () => $t('common.confirmStop'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.stop')}
</NButton>
)
}}
</NPopconfirm>
<>
<NPopconfirm onPositiveClick={() => handleStop(row.id!)}>
{{
default: () => $t('common.confirmStop'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.stop')}
</NButton>
)
}}
</NPopconfirm>
<n-divider vertical />
</>
) : (
''
)}
<NPopconfirm onPositiveClick={() => handleDelete(row.id!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" text ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</div>
)
}
@ -142,12 +163,20 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
const {
checkedRowKeys,
onDeleted,
onBatchDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleDelete(id: string) {
const { error } = await fetchDeleteWorkflowBatch(id);
if (error) return;
onDeleted();
}
async function handleBatchDelete() {
// requestd
const { error } = await fetchBatchDeleteWorkflowBatch(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
@ -180,7 +209,6 @@ function detail(id: string) {
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-add="false"
:show-delete="false"
@delete="handleBatchDelete"
@refresh="getData"
/>

View File

@ -2,7 +2,7 @@
import { NButton, NDropdown, NPopconfirm, NTag } from 'naive-ui';
import { useRouter } from 'vue-router';
import {
fetchDelWorkflow,
fetchBatchDeleteWorkflow,
fetchGetWorkflowPageList,
fetchTriggerWorkflow,
fetchUpdateWorkflowStatus
@ -216,22 +216,22 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
const {
checkedRowKeys,
onBatchDeleted
onBatchDeleted,
onDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleBatchDelete() {
// request
const { error } = await fetchBatchDeleteWorkflow(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
async function handleDelete(id: string) {
// request
const { error } = await fetchDelWorkflow(id!);
if (!error) {
window.$message?.success($t('common.deleteSuccess'));
getData();
}
const { error } = await fetchBatchDeleteWorkflow([id!]);
if (error) return;
onDeleted();
}
function edit(id: string) {
@ -295,7 +295,6 @@ function goToBatch(workflowId: string) {
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-delete="false"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"