diff --git a/.gitignore b/.gitignore
index 095c630..da8e129 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,4 @@ yarn.lock
src/typings/elegant-router.d.ts
src/typings/components.d.ts
+src/router/elegant/transform.ts
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0c2bf2d..e6d6a37 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,6 +41,9 @@ importers:
'@sa/utils':
specifier: workspace:*
version: link:packages/utils
+ '@sa/workflow':
+ specifier: workspace:*
+ version: link:packages/work-flow
'@vueuse/core':
specifier: 10.9.0
version: 10.9.0(vue@3.4.27(typescript@5.4.5))
@@ -291,6 +294,8 @@ importers:
specifier: 4.2.2
version: 4.2.2
+ packages/work-flow: {}
+
packages:
'@ampproject/remapping@2.3.0':
@@ -4746,6 +4751,17 @@ packages:
'@vue/composition-api':
optional: true
+ vue-demi@0.14.8:
+ resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
vue-draggable-plus@0.4.1:
resolution: {integrity: sha512-KNi+c482OQUZTZ2kXIGc41fEwknkNF+LlngjBr5TVtBLNvpX2dmwRJJ3J7dy5dGcijXb7V1j+mhqce4iHOoi6Q==}
peerDependencies:
@@ -9963,7 +9979,7 @@ snapshots:
dependencies:
codemirror: 6.0.1(@lezer/common@1.2.1)
vue: 3.4.27(typescript@5.4.5)
- vue-demi: 0.14.7(vue@3.4.27(typescript@5.4.5))
+ vue-demi: 0.14.8(vue@3.4.27(typescript@5.4.5))
transitivePeerDependencies:
- '@lezer/common'
- '@vue/composition-api'
@@ -9976,6 +9992,10 @@ snapshots:
dependencies:
vue: 3.4.27(typescript@5.4.5)
+ vue-demi@0.14.8(vue@3.4.27(typescript@5.4.5)):
+ dependencies:
+ vue: 3.4.27(typescript@5.4.5)
+
vue-draggable-plus@0.4.1(@types/sortablejs@1.15.8):
dependencies:
'@types/sortablejs': 1.15.8
diff --git a/public/iconify/cbi.json b/public/iconify/cbi.json
new file mode 100644
index 0000000..54d4521
--- /dev/null
+++ b/public/iconify/cbi.json
@@ -0,0 +1,14 @@
+{
+ "prefix": "cbi",
+ "lastModified": 1715922185,
+ "aliases": {},
+ "width": 24,
+ "height": 24,
+ "icons": {
+ "scene-dynamic": {
+ "body": "",
+ "width": 32,
+ "height": 32
+ }
+ }
+}
diff --git a/public/iconify/fluent.json b/public/iconify/fluent.json
new file mode 100644
index 0000000..ac1b6e4
--- /dev/null
+++ b/public/iconify/fluent.json
@@ -0,0 +1,12 @@
+{
+ "prefix": "fluent",
+ "lastModified": 1716181326,
+ "aliases": {},
+ "width": 20,
+ "height": 20,
+ "icons": {
+ "people-call-20-filled": {
+ "body": ""
+ }
+ }
+}
diff --git a/public/iconify/oui.json b/public/iconify/oui.json
new file mode 100644
index 0000000..f0f1e7e
--- /dev/null
+++ b/public/iconify/oui.json
@@ -0,0 +1,12 @@
+{
+ "prefix": "oui",
+ "lastModified": 1714628006,
+ "aliases": {},
+ "icons": {
+ "app-spaces": {
+ "body": "",
+ "width": 32,
+ "height": 32
+ }
+ }
+}
diff --git a/public/iconify/streamline.json b/public/iconify/streamline.json
new file mode 100644
index 0000000..0aeb1c0
--- /dev/null
+++ b/public/iconify/streamline.json
@@ -0,0 +1,17 @@
+{
+ "prefix": "streamline",
+ "lastModified": 1702314084,
+ "aliases": {},
+ "width": 14,
+ "height": 14,
+ "icons": {
+ "interface-arrows-synchronize-warning-arrow-fail-notification-sync-warning-failure-synchronize-error": {
+ "body": "",
+ "hidden": true
+ },
+ "interface-user-multiple-close-geometric-human-multiple-person-up-user": {
+ "body": "",
+ "hidden": true
+ }
+ }
+}
diff --git a/public/iconify/tabler.json b/public/iconify/tabler.json
new file mode 100644
index 0000000..5a4f022
--- /dev/null
+++ b/public/iconify/tabler.json
@@ -0,0 +1,12 @@
+{
+ "prefix": "tabler",
+ "lastModified": 1716442914,
+ "aliases": {},
+ "width": 24,
+ "height": 24,
+ "icons": {
+ "logs": {
+ "body": ""
+ }
+ }
+}
diff --git a/src/components/common/file-upload.vue b/src/components/common/file-upload.vue
index 853dd63..0b40d64 100644
--- a/src/components/common/file-upload.vue
+++ b/src/components/common/file-upload.vue
@@ -7,6 +7,12 @@ defineOptions({
name: 'FileUpload'
});
+const emit = defineEmits();
+
+interface Emits {
+ (e: 'refresh'): void;
+}
+
interface Props {
accept?: string;
action?: string;
@@ -51,6 +57,7 @@ const handleImport = ({
})
.then(() => {
onFinish();
+ emit('refresh');
})
.catch(() => onError());
};
diff --git a/src/router/elegant/transform.ts b/src/router/elegant/transform.ts
deleted file mode 100644
index f6cb0ee..0000000
--- a/src/router/elegant/transform.ts
+++ /dev/null
@@ -1,250 +0,0 @@
-/* eslint-disable */
-/* prettier-ignore */
-// Generated by elegant-router
-// Read more: https://github.com/soybeanjs/elegant-router
-
-import type { RouteRecordRaw, RouteComponent } from 'vue-router';
-import type { ElegantConstRoute } from '@elegant-router/vue';
-import type { RouteMap, RouteKey, RoutePath } from '@elegant-router/types';
-
-/**
- * transform elegant const routes to vue routes
- * @param routes elegant const routes
- * @param layouts layout components
- * @param views view components
- */
-export function transformElegantRoutesToVueRoutes(
- routes: ElegantConstRoute[],
- layouts: Record Promise)>,
- views: Record Promise)>
-) {
- return routes.flatMap(route => transformElegantRouteToVueRoute(route, layouts, views));
-}
-
-/**
- * transform elegant route to vue route
- * @param route elegant const route
- * @param layouts layout components
- * @param views view components
- */
-function transformElegantRouteToVueRoute(
- route: ElegantConstRoute,
- layouts: Record Promise)>,
- views: Record Promise)>
-) {
- const LAYOUT_PREFIX = 'layout.';
- const VIEW_PREFIX = 'view.';
- const ROUTE_DEGREE_SPLITTER = '_';
- const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
-
- function isLayout(component: string) {
- return component.startsWith(LAYOUT_PREFIX);
- }
-
- function getLayoutName(component: string) {
- const layout = component.replace(LAYOUT_PREFIX, '');
-
- if(!layouts[layout]) {
- throw new Error(`Layout component "${layout}" not found`);
- }
-
- return layout;
- }
-
- function isView(component: string) {
- return component.startsWith(VIEW_PREFIX);
- }
-
- function getViewName(component: string) {
- const view = component.replace(VIEW_PREFIX, '');
-
- if(!views[view]) {
- throw new Error(`View component "${view}" not found`);
- }
-
- return view;
- }
-
- function isFirstLevelRoute(item: ElegantConstRoute) {
- return !item.name.includes(ROUTE_DEGREE_SPLITTER);
- }
-
- function isSingleLevelRoute(item: ElegantConstRoute) {
- return isFirstLevelRoute(item) && !item.children?.length;
- }
-
- function getSingleLevelRouteComponent(component: string) {
- const [layout, view] = component.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT);
-
- return {
- layout: getLayoutName(layout),
- view: getViewName(view)
- };
- }
-
- const vueRoutes: RouteRecordRaw[] = [];
-
- // add props: true to route
- if (route.path.includes(':') && !route.props) {
- route.props = true;
- }
-
- const { name, path, component, children, ...rest } = route;
-
- const vueRoute = { name, path, ...rest } as RouteRecordRaw;
-
- try {
- if (component) {
- if (isSingleLevelRoute(route)) {
- const { layout, view } = getSingleLevelRouteComponent(component);
-
- const singleLevelRoute: RouteRecordRaw = {
- path,
- component: layouts[layout],
- children: [
- {
- name,
- path: '',
- component: views[view],
- ...rest
- } as RouteRecordRaw
- ]
- };
-
- return [singleLevelRoute];
- }
-
- if (isLayout(component)) {
- const layoutName = getLayoutName(component);
-
- vueRoute.component = layouts[layoutName];
- }
-
- if (isView(component)) {
- const viewName = getViewName(component);
-
- vueRoute.component = views[viewName];
- }
-
- }
- } catch (error: any) {
- console.error(`Error transforming route "${route.name}": ${error.toString()}`);
- return [];
- }
-
-
- // add redirect to child
- if (children?.length && !vueRoute.redirect) {
- vueRoute.redirect = {
- name: children[0].name
- };
- }
-
- if (children?.length) {
- const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
-
- if(isFirstLevelRoute(route)) {
- vueRoute.children = childRoutes;
- } else {
- vueRoutes.push(...childRoutes);
- }
- }
-
- vueRoutes.unshift(vueRoute);
-
- return vueRoutes;
-}
-
-/**
- * map of route name and route path
- */
-const routeMap: RouteMap = {
- "root": "/",
- "not-found": "/:pathMatch(.*)*",
- "exception": "/exception",
- "exception_403": "/exception/403",
- "exception_404": "/exception/404",
- "exception_500": "/exception/500",
- "document": "/document",
- "document_project": "/document/project",
- "document_project-link": "/document/project-link",
- "document_vue": "/document/vue",
- "document_vite": "/document/vite",
- "document_unocss": "/document/unocss",
- "document_naive": "/document/naive",
- "document_antd": "/document/antd",
- "403": "/403",
- "404": "/404",
- "500": "/500",
- "about": "/about",
- "function": "/function",
- "function_hide-child": "/function/hide-child",
- "function_hide-child_one": "/function/hide-child/one",
- "function_hide-child_three": "/function/hide-child/three",
- "function_hide-child_two": "/function/hide-child/two",
- "function_multi-tab": "/function/multi-tab",
- "function_request": "/function/request",
- "function_super-page": "/function/super-page",
- "function_tab": "/function/tab",
- "function_toggle-auth": "/function/toggle-auth",
- "group": "/group",
- "home": "/home",
- "iframe-page": "/iframe-page/:url",
- "job": "/job",
- "job_batch": "/job/batch",
- "job_task": "/job/task",
- "login": "/login/:module(pwd-login)?",
- "manage": "/manage",
- "manage_menu": "/manage/menu",
- "manage_role": "/manage/role",
- "manage_user": "/manage/user",
- "manage_user-detail": "/manage/user-detail/:id",
- "multi-menu": "/multi-menu",
- "multi-menu_first": "/multi-menu/first",
- "multi-menu_first_child": "/multi-menu/first/child",
- "multi-menu_second": "/multi-menu/second",
- "multi-menu_second_child": "/multi-menu/second/child",
- "multi-menu_second_child_home": "/multi-menu/second/child/home",
- "namespace": "/namespace",
- "notify": "/notify",
- "notify_recipient": "/notify/recipient",
- "notify_scene": "/notify/scene",
- "pods": "/pods",
- "retry": "/retry",
- "retry_dead-letter": "/retry/dead-letter",
- "retry_log": "/retry/log",
- "retry_scene": "/retry/scene",
- "retry_task": "/retry/task",
- "user": "/user",
- "user_manager": "/user/manager",
- "user-center": "/user-center",
- "workflow": "/workflow",
- "workflow_batch": "/workflow/batch",
- "workflow_form": "/workflow/form",
- "workflow_form_add": "/workflow/form/add",
- "workflow_form_batch": "/workflow/form/batch",
- "workflow_form_copy": "/workflow/form/copy",
- "workflow_form_detail": "/workflow/form/detail",
- "workflow_form_edit": "/workflow/form/edit",
- "workflow_task": "/workflow/task"
-};
-
-/**
- * get route path by route name
- * @param name route name
- */
-export function getRoutePath(name: T) {
- return routeMap[name];
-}
-
-/**
- * get route name by route path
- * @param path route path
- */
-export function getRouteName(path: RoutePath) {
- const routeEntries = Object.entries(routeMap) as [RouteKey, RoutePath][];
-
- const routeName: RouteKey | null = routeEntries.find(([, routePath]) => routePath === path)?.[0] || null;
-
- return routeName;
-}
diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts
index b81cfc4..b65346a 100644
--- a/src/typings/api.d.ts
+++ b/src/typings/api.d.ts
@@ -212,7 +212,7 @@ declare namespace Api {
type DashboardLineJob = {
createDt: string;
total: number;
- failNum: number;
+ fail: number;
stop: number;
cancel: number;
success: number;
@@ -649,8 +649,7 @@ declare namespace Api {
/** notifyRecipient search params */
type NotifyRecipientParams = CommonType.RecordNullable<
- Pick &
- CommonSearchParams
+ Pick & CommonSearchParams
>;
/** notifyRecipient list */
@@ -659,6 +658,11 @@ declare namespace Api {
/** 1: 钉钉通知 2: 邮件通知 3: 企业通知 4: 飞书 5: Webhook */
type AlarmType = 1 | 2 | 3 | 4 | 5;
+ type ExportNotifyRecipient = Common.CommonRecord<{
+ notifyRecipientIds: string[];
+ }> &
+ NotifyRecipientParams;
+
/* 1: application/json 2:application/x-www-form-urlencoded */
type AlarmTypeWebhook = 1 | 2;
}
@@ -894,6 +898,11 @@ declare namespace Api {
Pick & CommonSearchParams
>;
+ type ExportWorkflow = Common.CommonRecord<{
+ workflowIds: String[];
+ }> &
+ WorkflowSearchParams;
+
/** workflow list */
type WorkflowList = Common.PaginatingQueryRecord;
}
@@ -982,6 +991,11 @@ declare namespace Api {
/** JobTask list */
type JobList = Common.PaginatingQueryRecord;
+ type ExportJob = Common.CommonRecord<{
+ jobIds: string[];
+ }> &
+ JobSearchParams;
+
/** 2、固定时间 3、CRON表达式 99、工作流 */
type TriggerType = 2 | 3 | 99;
diff --git a/src/views/group/index.vue b/src/views/group/index.vue
index d4c3142..b59a7dc 100644
--- a/src/views/group/index.vue
+++ b/src/views/group/index.vue
@@ -195,7 +195,7 @@ function handleExport() {
@refresh="getData"
>
-
+
diff --git a/src/views/home/modules/task-line-chart.vue b/src/views/home/modules/task-line-chart.vue
index 1a711e8..a4de18c 100644
--- a/src/views/home/modules/task-line-chart.vue
+++ b/src/views/home/modules/task-line-chart.vue
@@ -193,7 +193,7 @@ const getData = () => {
opts.tabIndex === 'RETRY' ? x.successNum : x.success
);
opts.series[1].data = props.modelValue?.dashboardLineResponseDOList.map(x =>
- opts.tabIndex === 'RETRY' ? x.runningNum : x.failNum
+ opts.tabIndex === 'RETRY' ? x.runningNum : x.fail
);
opts.series[2].data = props.modelValue?.dashboardLineResponseDOList.map(x =>
opts.tabIndex === 'RETRY' ? x.maxCountNum : x.stop
diff --git a/src/views/job/task/index.vue b/src/views/job/task/index.vue
index 821af29..e802b1a 100644
--- a/src/views/job/task/index.vue
+++ b/src/views/job/task/index.vue
@@ -253,8 +253,17 @@ function goToBatch(jobId: string) {
routerPushByKey('job_batch', { query: { jobId } });
}
+function body(): Api.Job.ExportJob {
+ return {
+ jobIds: checkedRowKeys.value,
+ groupName: searchParams.groupName,
+ jobName: searchParams.jobName,
+ jobStatus: searchParams.jobStatus
+ };
+}
+
function handleExport() {
- downloadFetch('/job/export', checkedRowKeys.value, $t('page.jobTask.title'));
+ downloadFetch('/job/export', body(), $t('page.jobTask.title'));
}
@@ -277,7 +286,7 @@ function handleExport() {
@refresh="getData"
>
-
+
diff --git a/src/views/notify/recipient/index.vue b/src/views/notify/recipient/index.vue
index 3fc9375..a34c4b6 100644
--- a/src/views/notify/recipient/index.vue
+++ b/src/views/notify/recipient/index.vue
@@ -9,6 +9,7 @@ import { useTable, useTableOperate } from '@/hooks/common/table';
import { alarmTypeRecord } from '@/constants/business';
import { tagColor } from '@/utils/common';
import { useAuth } from '@/hooks/business/auth';
+import { downloadFetch } from '@/utils/download';
import NotifyRecipientOperateDrawer from './modules/notify-recipient-operate-drawer.vue';
import NotifyRecipientSearch from './modules/notify-recipient-search.vue';
import NotifyRecipientDetailDrawer from './modules/notify-recipient-detail-drawer.vue';
@@ -28,7 +29,7 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
size: 10,
// 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
- recipientName: '',
+ recipientName: null,
notifyType: null
},
columns: () => [
@@ -137,6 +138,18 @@ async function handleDelete(id: string) {
function edit(id: string) {
handleEdit(id);
}
+
+function body(): Api.NotifyRecipient.ExportNotifyRecipient {
+ return {
+ notifyRecipientIds: checkedRowKeys.value,
+ notifyType: searchParams.notifyType,
+ recipientName: searchParams.recipientName
+ };
+}
+
+function handleExport() {
+ downloadFetch('/notify-recipient/export', body(), $t('page.notifyRecipient.title'));
+}
@@ -157,7 +170,28 @@ function edit(id: string) {
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
- />
+ >
+
+
+
+
+
+
+
+
+ {{ $t('common.export') }}
+
+
+
+ {{
+ checkedRowKeys.length === 0
+ ? $t('common.exportAll')
+ : $t('common.exportPar', { num: checkedRowKeys.length })
+ }}
+
+
+
+
-
+
diff --git a/src/views/workflow/task/index.vue b/src/views/workflow/task/index.vue
index 1d0f876..f2a3308 100644
--- a/src/views/workflow/task/index.vue
+++ b/src/views/workflow/task/index.vue
@@ -33,11 +33,11 @@ const { columns, columnChecks, data, getData, loading, mobilePagination, searchP
workflowStatus: null
},
columns: () => [
- // {
- // type: 'selection',
- // align: 'center',
- // width: 48
- // },
+ {
+ type: 'selection',
+ align: 'center',
+ width: 48
+ },
{
key: 'id',
title: $t('common.index'),
@@ -249,8 +249,17 @@ async function execute(id: string) {
}
}
+function body(): Api.Workflow.ExportWorkflow {
+ return {
+ workflowIds: checkedRowKeys.value,
+ groupName: searchParams.groupName,
+ workflowName: searchParams.workflowName,
+ workflowStatus: searchParams.workflowStatus
+ };
+}
+
function handleExport() {
- downloadFetch('/workflow/export', checkedRowKeys.value, $t('page.workflow.title'));
+ downloadFetch('/workflow/export', body(), $t('page.workflow.title'));
}
@@ -275,7 +284,7 @@ function handleExport() {
@refresh="getData"
>
-
+
diff --git a/src/views/workflow/task/modules/workflow-search.vue b/src/views/workflow/task/modules/workflow-search.vue
index d7943b1..622755b 100644
--- a/src/views/workflow/task/modules/workflow-search.vue
+++ b/src/views/workflow/task/modules/workflow-search.vue
@@ -29,7 +29,7 @@ function search() {
-
+