feat(任务中心): 执行交互&任务中心部分操作接口联调&跳转交互

This commit is contained in:
baiqi 2024-10-17 18:05:06 +08:00 committed by Craftsman
parent 3aa53ef5c4
commit bdf9fe47ec
30 changed files with 982 additions and 371 deletions

View File

@ -1,23 +1,34 @@
import MSR from '@/api/http/index';
import {
organizationDeleteTaskUrl,
organizationExecuteTaskDetailListUrl,
organizationExecuteTaskListUrl,
organizationExecuteTaskStatisticsUrl,
organizationScheduleListUrl,
organizationStopTaskUrl,
organizationTaskCenterResourcePoolsUrl,
projectDeleteTaskUrl,
projectExecuteTaskDetailListUrl,
projectExecuteTaskListUrl,
projectExecuteTaskStatisticsUrl,
projectScheduleTaskListUrl,
projectStopTaskUrl,
projectTaskCenterResourcePoolsUrl,
scheduleProCenterListUrl,
systemDeleteTaskUrl,
systemExecuteTaskDetailListUrl,
systemExecuteTaskListUrl,
systemExecuteTaskStatisticsUrl,
systemResourcePoolStatusUrl,
systemScheduleListUrl,
systemStopTaskUrl,
systemTaskCenterResourcePoolsUrl,
} from '@/api/requrls/taskCenter';
import type { CommonList, TableQueryParams } from '@/models/common';
import type { TimingTaskCenterApiCaseItem } from '@/models/projectManagement/taskCenter';
import type {
TaskCenterResourcePoolStatus,
TaskCenterStatisticsItem,
TaskCenterSystemTaskItem,
TaskCenterTaskDetailItem,
@ -45,6 +56,21 @@ export function getProjectExecuteTaskStatistics(data: string[]) {
return MSR.post<TaskCenterStatisticsItem[]>({ url: projectExecuteTaskStatisticsUrl, data });
}
// 项目任务-获取资源池列表
export function getProjectTaskCenterResourcePools() {
return MSR.get<string[]>({ url: projectTaskCenterResourcePoolsUrl });
}
// 项目任务-停止任务
export function projectStopTask(id: string) {
return MSR.get({ url: `${projectStopTaskUrl}/${id}` });
}
// 项目任务-删除任务
export function projectDeleteTask(id: string) {
return MSR.get({ url: `${projectDeleteTaskUrl}/${id}` });
}
// 接口测试-定时任务列表
export function getScheduleProApiCaseList(data: TableQueryParams) {
return MSR.post<CommonList<TimingTaskCenterApiCaseItem>>({ url: scheduleProCenterListUrl, data });
@ -70,6 +96,26 @@ export function getSystemExecuteTaskStatistics(data: string[]) {
return MSR.post<TaskCenterStatisticsItem[]>({ url: systemExecuteTaskStatisticsUrl, data });
}
// 系统任务-获取资源池列表
export function getSystemTaskCenterResourcePools() {
return MSR.get<string[]>({ url: systemTaskCenterResourcePoolsUrl });
}
// 系统任务-停止任务
export function systemStopTask(id: string) {
return MSR.get({ url: `${systemStopTaskUrl}/${id}` });
}
// 系统任务-删除任务
export function systemDeleteTask(id: string) {
return MSR.get({ url: `${systemDeleteTaskUrl}/${id}` });
}
// 任务中心-资源池状态
export function getResourcePoolsStatus(data: string[]) {
return MSR.post<TaskCenterResourcePoolStatus[]>({ url: systemResourcePoolStatusUrl, data });
}
// 组织任务-系统后台任务列表
export function getOrganizationScheduleList(data: TableQueryParams) {
return MSR.post<CommonList<TaskCenterSystemTaskItem>>({ url: organizationScheduleListUrl, data });
@ -89,3 +135,18 @@ export function getOrganizationExecuteTaskList(data: TableQueryParams) {
export function getOrganizationExecuteTaskStatistics(data: string[]) {
return MSR.post<TaskCenterStatisticsItem[]>({ url: organizationExecuteTaskStatisticsUrl, data });
}
// 组织任务-获取资源池列表
export function getOrgTaskCenterResourcePools() {
return MSR.get<string[]>({ url: organizationTaskCenterResourcePoolsUrl });
}
// 组织任务-停止任务
export function organizationStopTask(id: string) {
return MSR.get({ url: `${organizationStopTaskUrl}/${id}` });
}
// 组织任务-删除任务
export function organizationDeleteTask(id: string) {
return MSR.get({ url: `${organizationDeleteTaskUrl}/${id}` });
}

View File

@ -2,15 +2,24 @@ export const projectScheduleTaskListUrl = '/project/task-center/schedule/page';
export const projectExecuteTaskStatisticsUrl = '/project/task-center/exec-task/statistics'; // 项目任务-获取任务统计
export const projectExecuteTaskListUrl = '/project/task-center/exec-task/page'; // 项目任务-任务列表
export const projectExecuteTaskDetailListUrl = '/project/task-center/exec-task/item/page'; // 项目任务-任务详情列表
// 项目-任务中心-定时任务列表
export const scheduleProCenterListUrl = '/task/center/project/schedule/page';
export const scheduleProCenterListUrl = '/task/center/project/schedule/page'; // 项目-任务中心-定时任务列表
export const projectTaskCenterResourcePoolsUrl = '/project/task-center/resource-pool/options'; // 项目-任务中心-获取资源池列表
export const projectStopTaskUrl = '/project/task-center/exec-task/stop'; // 项目-任务中心-停止任务
export const projectDeleteTaskUrl = '/project/task-center/exec-task/delete'; // 项目-任务中心-删除任务
export const systemScheduleListUrl = '/system/task-center/schedule/page'; // 系统任务-系统后台任务列表
export const systemExecuteTaskListUrl = '/system/task-center/exec-task/page'; // 系统任务-执行任务列表
export const systemExecuteTaskDetailListUrl = '/system/task-center/exec-task/item/page'; // 系统任务-执行任务详情列表
export const systemExecuteTaskStatisticsUrl = '/system/task-center/exec-task/statistics'; // 系统任务-获取任务统计
export const systemTaskCenterResourcePoolsUrl = '/system/task-center/resource-pool/options'; // 系统任务-获取资源池列表
export const systemStopTaskUrl = '/system/task-center/exec-task/stop'; // 系统任务-停止任务
export const systemDeleteTaskUrl = '/system/task-center/exec-task/delete'; // 系统任务-删除任务
export const systemResourcePoolStatusUrl = '/system/task-center/resource-pool/status'; // 系统任务-资源池状态
export const organizationScheduleListUrl = '/organization/task-center/schedule/page'; // 系统任务-系统后台任务列表
export const organizationExecuteTaskListUrl = '/organization/task-center/exec-task/page'; // 系统任务-执行任务列表
export const organizationExecuteTaskDetailListUrl = '/organization/task-center/exec-task/item/page'; // 系统任务-执行任务详情列表
export const organizationExecuteTaskStatisticsUrl = '/organization/task-center/exec-task/statistics'; // 系统任务-获取任务统计
export const organizationScheduleListUrl = '/organization/task-center/schedule/page'; // 组织任务-系统后台任务列表
export const organizationExecuteTaskListUrl = '/organization/task-center/exec-task/page'; // 组织任务-执行任务列表
export const organizationExecuteTaskDetailListUrl = '/organization/task-center/exec-task/item/page'; // 组织任务-执行任务详情列表
export const organizationExecuteTaskStatisticsUrl = '/organization/task-center/exec-task/statistics'; // 组织任务-获取任务统计
export const organizationTaskCenterResourcePoolsUrl = '/organization/task-center/resource-pool/options'; // 组织任务-获取资源池列表
export const organizationStopTaskUrl = '/organization/task-center/exec-task/stop'; // 组织任务-停止任务
export const organizationDeleteTaskUrl = '/organization/task-center/exec-task/delete'; // 组织任务-删除任务

View File

@ -0,0 +1,28 @@
<template>
<div class="flex flex-wrap items-center">
<div class="mr-[8px]">{{ props.content }}</div>
<MsButton v-if="props.showDetail" @click="goDetail">{{ t('common.checkDetail') }}</MsButton>
</div>
</template>
<script setup lang="ts">
import MsButton from '@/components/pure/ms-button/index.vue';
import { useI18n } from '@/hooks/useI18n';
const props = withDefaults(
defineProps<{
content: string;
showDetail?: boolean;
}>(),
{
showDetail: true,
}
);
const emit = defineEmits(['goDetail']);
const { t } = useI18n();
function goDetail() {
emit('goDetail');
}
</script>

View File

@ -193,9 +193,12 @@
import { LOCALE_OPTIONS } from '@/locale';
import useLocale from '@/locale/useLocale';
import useAppStore from '@/store/modules/app';
import useGlobalStore from '@/store/modules/global';
import useUserStore from '@/store/modules/user';
import { getFirstRouteNameByPermission, hasAnyPermission } from '@/utils/permission';
import { GlobalEventNameEnum } from '@/enums/commonEnum';
import { IconInfoCircle } from '@arco-design/web-vue/es/icon';
import type { LocaleType } from '#/global';
@ -208,6 +211,7 @@
const appStore = useAppStore();
const userStore = useUserStore();
const globalStore = useGlobalStore();
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
@ -291,6 +295,15 @@
function goTaskCenter() {
taskCenterVisible.value = true;
}
watch(
() => globalStore.getGlobalEvent,
(event) => {
if (event && event.id && event.name === GlobalEventNameEnum.OPEN_TASK_CENTER) {
goTaskCenter();
}
}
);
function goMessageCenter() {
messageCenterVisible.value = true;
}

View File

@ -40,3 +40,7 @@ export enum TagUpdateTypeEnum {
APPEND = 'APPEND',
CLEAR = 'CLEAR',
}
export enum GlobalEventNameEnum {
OPEN_TASK_CENTER = 'openTaskCenter',
}

View File

@ -33,3 +33,18 @@ export enum ExecuteResultEnum {
ERROR = 'ERROR',
FAKE_ERROR = 'FAKE_ERROR',
}
export enum ExecuteTriggerMode {
MANUAL = 'MANUAL',
BATCH = 'BATCH',
API = 'API',
SCHEDULE = 'SCHEDULE',
}
export enum ExecuteTaskType {
API_IMPORT = 'API_IMPORT',
API_SCENARIO = 'API_SCENARIO',
BUG_SYNC = 'BUG_SYNC',
DEMAND_SYNC = 'DEMAND_SYNC',
TEST_PLAN = 'TEST_PLAN',
}

View File

@ -216,4 +216,5 @@ export default {
'common.currentUser': 'Current user',
'common.type': 'Type',
'common.batchUpdate': 'Updated to',
'common.checkDetail': 'View details',
};

View File

@ -216,4 +216,5 @@ export default {
'common.currentUser': '当前用户',
'common.type': '类型',
'common.batchUpdate': '更新为',
'common.checkDetail': '查看详情',
};

View File

@ -1,3 +1,5 @@
import type { ExecuteTriggerMode } from '@/enums/taskCenter';
import type { TableQueryParams } from './common';
export interface TaskCenterSystemTaskItem {
@ -32,7 +34,7 @@ export interface TaskCenterTaskItem {
result: string; // 执行结果
taskType: string; // 任务类型
resourceId: string;
triggerMode: string; // 触发方式
triggerMode: ExecuteTriggerMode; // 执行方式
projectId: string;
organizationId: string;
createTime: number;
@ -42,6 +44,7 @@ export interface TaskCenterTaskItem {
organizationName: string; // 所属组织名称
projectName: string; // 所属项目名称
createUserName: string; // 创建人
[key: string]: any;
}
export interface TaskCenterTaskDetailItem {
@ -77,3 +80,8 @@ export interface TaskCenterStatisticsItem {
pendingCount: number; // 待执行数
caseTotal: number; // 用例总数
}
export interface TaskCenterResourcePoolStatus {
id: string;
status: boolean; // 状态, true: 正常, false: 异常
}

View File

@ -5,11 +5,12 @@ import useTableStore from '@/hooks/useTableStore';
import useAppStore from './modules/app';
import useVisitStore from './modules/app/visit';
import useMinderStore from './modules/components/minder-editor';
import useGlobalStore from './modules/global';
import useUserStore from './modules/user';
import { debouncePlugin } from './plugins';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia().use(debouncePlugin).use(piniaPluginPersistedstate);
export { useAppStore, useMinderStore, useTableStore, useUserStore, useVisitStore };
export { useAppStore, useGlobalStore, useMinderStore, useTableStore, useUserStore, useVisitStore };
export default pinia;

View File

@ -12,10 +12,10 @@ export interface AppState {
navbar: boolean;
menu: boolean;
hideMenu: boolean;
menuCollapse: boolean;
menuCollapse: boolean; // 菜单是否折叠
footer: boolean;
menuWidth: number;
collapsedWidth: number;
collapsedWidth: number; // 菜单折叠宽度
globalSettings: boolean;
device: string;
tabBar: boolean;
@ -45,7 +45,7 @@ export interface AppState {
ordList: { id: string; name: string }[];
envList: EnvironmentItem[];
currentEnvConfig?: EnvConfig; // 当前环境配置信息
fileMaxSize: number;
fileMaxSize: number; // 文件上传最大限制
}
export interface UploadFileTaskState {

View File

@ -0,0 +1,20 @@
import { defineStore } from 'pinia';
import type { GlobalState, GlobalStateEvent } from './types';
const useGlobalStore = defineStore('global', {
state: (): GlobalState => ({
globalEvent: undefined,
}),
getters: {
getGlobalEvent(state: GlobalState) {
return state.globalEvent;
},
},
actions: {
dispatchGlobalEvent(event: GlobalStateEvent) {
this.globalEvent = event;
},
},
});
export default useGlobalStore;

View File

@ -0,0 +1,11 @@
import type { GlobalEventNameEnum } from '@/enums/commonEnum';
export interface GlobalStateEvent {
id: string;
name: GlobalEventNameEnum;
params?: Record<string, any>;
}
export interface GlobalState {
globalEvent?: GlobalStateEvent;
}

View File

@ -214,7 +214,7 @@
:max-length="255"
class="w-[550px]"
></a-input>
<MsButton type="text" @click="taskDrawerVisible = true">
<MsButton type="text" @click="emit('openTaskDrawer')">
{{ t('apiTestManagement.timeTaskList') }}
</MsButton>
</div>
@ -328,52 +328,20 @@
</template>
</a-form>
</MsDrawer>
<MsDrawer v-model:visible="taskDrawerVisible" :width="960" :title="t('apiTestManagement.timeTask')" :footer="false">
<div class="mb-[16px] flex items-center justify-end">
<a-input-search
v-model:model-value="keyword"
:placeholder="t('apiTestManagement.searchTaskPlaceholder')"
allow-clear
class="mr-[8px] w-[240px]"
@search="loadTaskList"
@press-enter="loadTaskList"
@clear="loadTaskList"
/>
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #action="{ record }">
<a-switch
v-model:modelValue="record.enable"
type="line"
size="small"
:before-change="() => handleBeforeEnableChange(record)"
></a-switch>
</template>
</ms-base-table>
</MsDrawer>
</div>
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { FormInstance, Message } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCronSelect from '@/components/pure/ms-cron-select/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsUpload from '@/components/pure/ms-upload/index.vue';
import type { MsFileItem } from '@/components/pure/ms-upload/types';
import {
createDefinitionSchedule,
importDefinition,
switchDefinitionSchedule,
} from '@/api/modules/api-test/management';
import { getScheduleProApiCaseList } from '@/api/modules/taskCenter';
import { createDefinitionSchedule, importDefinition } from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
@ -381,16 +349,14 @@
import type { ImportApiDefinitionParams, ImportApiDefinitionRequest } from '@/models/apiTest/management';
import type { ModuleTreeNode } from '@/models/common';
import { TimingTaskCenterApiCaseItem } from '@/models/projectManagement/taskCenter';
import { RequestImportFormat, RequestImportType } from '@/enums/apiEnum';
import { TaskCenterEnum } from '@/enums/taskCenter';
const props = defineProps<{
visible: boolean;
moduleTree: ModuleTreeNode[];
activeModule: string;
}>();
const emit = defineEmits(['update:visible', 'done']);
const emit = defineEmits(['update:visible', 'done', 'openTaskDrawer']);
const { t } = useI18n();
const appStore = useAppStore();
@ -481,7 +447,6 @@
const cronValue = ref('0 0 0/1 * * ?');
const importLoading = ref(false);
const taskDrawerVisible = ref(false);
function setActiveImportFormat(format: RequestImportFormat) {
importForm.value.platform = format;
@ -583,7 +548,7 @@
importType.value = 'time';
fileList.value = [];
moreSettingActive.value = [];
taskDrawerVisible.value = true;
emit('openTaskDrawer');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -604,107 +569,6 @@
});
}
const keyword = ref('');
const columns: MsTableColumn = [
{
title: 'project.taskCenter.resourceID',
dataIndex: 'resourceNum',
slotName: 'resourceNum',
width: 140,
showInTable: true,
showTooltip: true,
},
{
title: 'project.taskCenter.resourceName',
slotName: 'resourceName',
dataIndex: 'resourceName',
width: 200,
showDrag: true,
showTooltip: true,
},
{
title: 'project.taskCenter.swaggerUrl',
slotName: 'swaggerUrl',
dataIndex: 'swaggerUrl',
width: 300,
showDrag: false,
showTooltip: true,
columnSelectorDisabled: true,
showInTable: true,
},
{
title: 'apiTestManagement.taskRunRule',
dataIndex: 'value',
width: 140,
},
{
title: 'apiTestManagement.taskNextRunTime',
dataIndex: 'nextTime',
showTooltip: true,
width: 180,
},
{
title: 'apiTestManagement.taskOperator',
dataIndex: 'createUserName',
showTooltip: true,
width: 150,
},
{
title: 'apiTestManagement.taskOperationTime',
dataIndex: 'createTime',
width: 180,
},
{
title: 'common.operation',
slotName: 'action',
dataIndex: 'operation',
fixed: 'right',
width: 80,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
getScheduleProApiCaseList,
{
columns,
scroll: { x: '100%' },
},
(item) => ({
...item,
operationTime: dayjs(item.operationTime).format('YYYY-MM-DD HH:mm:ss'),
nextTime: item.nextTime ? dayjs(item.nextTime).format('YYYY-MM-DD HH:mm:ss') : '-',
})
);
function loadTaskList() {
setLoadListParams({
keyword: keyword.value,
moduleType: TaskCenterEnum.API_IMPORT,
});
loadList();
}
watch(
() => taskDrawerVisible.value,
(value) => {
if (value) {
loadTaskList();
}
}
);
async function handleBeforeEnableChange(record: TimingTaskCenterApiCaseItem) {
try {
await switchDefinitionSchedule(record.id);
Message.success(
t(record.enable ? 'apiTestManagement.disableTaskSuccess' : 'apiTestManagement.enableTaskSuccess')
);
return true;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
return false;
}
}
function openLink() {
window.open('https://converter.swagger.io/', '_blank');
}

View File

@ -0,0 +1,165 @@
<template>
<MsDrawer v-model:visible="taskDrawerVisible" :width="960" :title="t('apiTestManagement.timeTask')" :footer="false">
<div class="mb-[16px] flex items-center justify-end">
<a-input-search
v-model:model-value="keyword"
:placeholder="t('apiTestManagement.searchTaskPlaceholder')"
allow-clear
class="mr-[8px] w-[240px]"
@search="loadTaskList"
@press-enter="loadTaskList"
@clear="loadTaskList"
/>
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #action="{ record }">
<div class="flex items-center gap-[12px]">
<a-switch
v-model:modelValue="record.enable"
type="line"
size="small"
:before-change="() => handleBeforeEnableChange(record)"
></a-switch>
<MsButton @click="deleteTask(record)">
{{ t('common.delete') }}
</MsButton>
</div>
</template>
</ms-base-table>
</MsDrawer>
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import { switchDefinitionSchedule } from '@/api/modules/api-test/management';
import { getScheduleProApiCaseList } from '@/api/modules/taskCenter';
import { useI18n } from '@/hooks/useI18n';
import { TimingTaskCenterApiCaseItem } from '@/models/projectManagement/taskCenter';
import { TaskCenterEnum } from '@/enums/taskCenter';
const { t } = useI18n();
const taskDrawerVisible = defineModel<boolean>('visible', { required: true });
const keyword = ref('');
const columns: MsTableColumn = [
{
title: 'apiTestManagement.resourceID',
dataIndex: 'resourceNum',
slotName: 'resourceNum',
width: 140,
showInTable: true,
showTooltip: true,
},
{
title: 'apiTestManagement.resourceName',
slotName: 'resourceName',
dataIndex: 'resourceName',
width: 200,
showDrag: true,
showTooltip: true,
},
{
title: 'apiTestManagement.swaggerUrl',
slotName: 'swaggerUrl',
dataIndex: 'swaggerUrl',
width: 300,
showDrag: false,
showTooltip: true,
columnSelectorDisabled: true,
showInTable: true,
},
{
title: 'apiTestManagement.taskRunRule',
dataIndex: 'value',
width: 140,
},
{
title: 'apiTestManagement.taskNextRunTime',
dataIndex: 'nextTime',
showTooltip: true,
width: 180,
},
{
title: 'apiTestManagement.taskOperator',
dataIndex: 'createUserName',
showTooltip: true,
width: 150,
},
{
title: 'apiTestManagement.taskOperationTime',
dataIndex: 'createTime',
width: 180,
},
{
title: 'common.operation',
slotName: 'action',
dataIndex: 'operation',
fixed: 'right',
width: 110,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
getScheduleProApiCaseList,
{
columns,
scroll: { x: '100%' },
},
(item) => ({
...item,
operationTime: dayjs(item.operationTime).format('YYYY-MM-DD HH:mm:ss'),
nextTime: item.nextTime ? dayjs(item.nextTime).format('YYYY-MM-DD HH:mm:ss') : '-',
})
);
function loadTaskList() {
setLoadListParams({
keyword: keyword.value,
moduleType: TaskCenterEnum.API_IMPORT,
});
loadList();
}
watch(
() => taskDrawerVisible.value,
(value) => {
if (value) {
loadTaskList();
}
}
);
async function handleBeforeEnableChange(record: TimingTaskCenterApiCaseItem) {
try {
await switchDefinitionSchedule(record.id);
Message.success(
t(record.enable ? 'apiTestManagement.disableTaskSuccess' : 'apiTestManagement.enableTaskSuccess')
);
return true;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
return false;
}
}
async function deleteTask(record: TimingTaskCenterApiCaseItem) {
try {
// await switchDefinitionSchedule(record.id);
Message.success(t('common.deleteSuccess'));
loadTaskList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
</script>
<style lang="less" scoped></style>

View File

@ -47,6 +47,7 @@
:active-module="activeModule"
popup-container="#managementContainer"
@done="handleImportDone"
@open-task-drawer="taskDrawerVisible = true"
/>
</div>
<management
@ -116,6 +117,7 @@
is-share
/>
</MsCard>
<importTaskDrawer v-model:visible="taskDrawerVisible" />
</template>
<script lang="ts" setup>
@ -131,6 +133,7 @@
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
import { RequestParam } from '../components/requestComposition/index.vue';
import importApi from './components/import.vue';
import importTaskDrawer from './components/importTaskDrawer.vue';
import management from './components/management/index.vue';
import moduleTree from './components/moduleTree.vue';
import ApiExportModal from '@/views/api-test/management/components/management/api/apiExportModal.vue';
@ -423,10 +426,14 @@
});
}
const taskDrawerVisible = ref(false);
onBeforeMount(() => {
if (docShareId.value) {
getShareDetail();
}
if (route.query.taskDrawer) {
taskDrawerVisible.value = true;
}
});
/** 向子孙组件提供方法和值 */

View File

@ -196,6 +196,8 @@ export default {
'Delete, share a link can lead to user access is to access the link is unusual, please careful operation',
'apiTestManagement.passwordView': 'Password',
'apiTestManagement.publicityView': 'Public',
'apiTestManagement.resourceID': 'Resource ID',
'apiTestManagement.swaggerUrl': 'Swagger URL',
'case.execute.selectEnv': 'Select Environment',
'case.execute.defaultEnv': 'Default Environment',
'case.execute.newEnv': 'New Environment',

View File

@ -188,6 +188,8 @@ export default {
'apiTestManagement.deleteShareTip': '删除后,分享链接会导致正在访问该链接的用户访问异常,请谨慎操作!',
'apiTestManagement.passwordView': '密码',
'apiTestManagement.publicityView': '公开',
'apiTestManagement.resourceID': '资源 ID',
'apiTestManagement.swaggerUrl': 'Swagger URL',
'case.execute.selectEnv': '环境选择',
'case.execute.defaultEnv': '默认环境',
'case.execute.newEnv': '新环境',

View File

@ -104,15 +104,21 @@
import { FormInstance, Message } from '@arco-design/web-vue';
import { BatchActionQueryParams } from '@/components/pure/ms-table/type';
import MsRichMessage from '@/components/business/ms-rich-message/index.vue';
import { getEnvList } from '@/api/modules/api-test/common';
import { getPoolId, getPoolOption } from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useGlobalStore from '@/store/modules/global';
import { getGenerateId } from '@/utils';
import { Environment } from '@/models/apiTest/management';
import { ResourcePoolItem } from '@/models/setting/resourcePool';
import { GlobalEventNameEnum } from '@/enums/commonEnum';
import { TaskCenterEnum } from '@/enums/taskCenter';
const globalStore = useGlobalStore();
const { t } = useI18n();
const batchExecuteFormRef = ref<FormInstance>();
const batchExecuteForm = ref({
@ -213,7 +219,35 @@
versionId: '',
refId: '',
});
Message.success(t('case.detail.execute.success'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.DETAIL,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
cancelBatchExecute();
resetBatchExecuteForm();
emit('finished');

View File

@ -1,8 +1,8 @@
<template>
<MsDrawer v-model:visible="visible" :title="title" :width="800" :footer="false">
<ms-base-table v-bind="propsRes" ref="tableRef" v-on="propsEvent" @filter-change="filterChange">
<template #name="{ record, rowIndex }">
<a-button type="text" class="max-w-full justify-start px-0" @click="showReportDetail(record.id, rowIndex)">
<template #name="{ record }">
<a-button type="text" class="max-w-full justify-start px-0" @click="showReportDetail(record)">
<div class="one-line-text">
{{ record.num }}
</div>
@ -43,6 +43,7 @@
</template>
</ms-base-table>
</MsDrawer>
<ReportDrawer v-model:visible="reportVisible" :report-id="independentReportId" />
</template>
<script setup lang="ts">
@ -55,6 +56,7 @@
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import ExecStatus from '@/views/test-plan/report/component/execStatus.vue';
import ReportDrawer from '@/views/test-plan/testPlan/detail/reportDrawer.vue';
import { useI18n } from '@/hooks/useI18n';
import useTableStore from '@/hooks/useTableStore';
@ -224,8 +226,12 @@
initData(dataIndex, value);
}
function showReportDetail(id: string, rowIndex: number) {
console.log(id, rowIndex);
const reportVisible = ref(false);
const independentReportId = ref<string>('');
function showReportDetail(record: any) {
independentReportId.value = record.id;
reportVisible.value = true;
}
</script>

View File

@ -49,8 +49,8 @@
</template>
<template #resourcePoolNode="{ record }">
<div>{{ record.resourcePoolNode }}</div>
<a-tooltip :content="t('ms.taskCenter.nodeErrorTip')">
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />
<a-tooltip v-if="record.resourcePoolNodeStatus === false" :content="t('ms.taskCenter.nodeErrorTip')">
<icon-exclamation-circle-fill class="ml-[4px] !text-[rgb(var(--warning-6))]" :size="18" />
</a-tooltip>
</template>
<template #action="{ record }">
@ -85,7 +85,7 @@
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue';
import { CascaderOption, Message } from '@arco-design/web-vue';
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
@ -102,8 +102,12 @@
import {
getOrganizationExecuteTaskDetailList,
getOrgTaskCenterResourcePools,
getProjectExecuteTaskDetailList,
getProjectTaskCenterResourcePools,
getResourcePoolsStatus,
getSystemExecuteTaskDetailList,
getSystemTaskCenterResourcePools,
} from '@/api/modules/taskCenter';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
@ -111,6 +115,7 @@
import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { TaskCenterTaskDetailItem } from '@/models/taskCenter';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ExecuteResultEnum, ExecuteStatusEnum } from '@/enums/taskCenter';
@ -128,7 +133,7 @@
const keyword = ref('');
const resourcePool = ref([]);
const resourcePoolOptions = ref([]);
const resourcePoolOptions = ref<CascaderOption[]>([]);
const tableSelected = ref<string[]>([]);
const batchModalParams = ref();
@ -164,6 +169,7 @@
{
title: 'ms.taskCenter.executeMethod',
dataIndex: 'triggerMode',
slotName: 'triggerMode',
width: 100,
filterConfig: {
options: Object.keys(executeMethodMap).map((key) => ({
@ -197,7 +203,7 @@
title: 'ms.taskCenter.node',
dataIndex: 'resourcePoolNode',
slotName: 'resourcePoolNode',
width: 100,
width: 180,
},
{
title: 'ms.taskCenter.queue',
@ -207,7 +213,8 @@
{
title: 'ms.taskCenter.threadID',
dataIndex: 'threadId',
width: 100,
showTooltip: true,
width: 190,
},
{
title: 'ms.taskCenter.startExecuteTime',
@ -229,7 +236,7 @@
},
{
title: 'ms.taskCenter.operationUser',
dataIndex: 'executor',
dataIndex: 'userName',
width: 100,
showTooltip: true,
},
@ -305,8 +312,8 @@
return {
...item,
resourcePoolName: [item.resourcePoolName],
startExecuteTime: dayjs(item.startExecuteTime).format('YYYY-MM-DD HH:mm:ss'),
endExecuteTime: dayjs(item.endExecuteTime).format('YYYY-MM-DD HH:mm:ss'),
startExecuteTime: item.startExecuteTime ? dayjs(item.startExecuteTime).format('YYYY-MM-DD HH:mm:ss') : '-',
endExecuteTime: item.endExecuteTime ? dayjs(item.endExecuteTime).format('YYYY-MM-DD HH:mm:ss') : '-',
};
}
);
@ -319,7 +326,7 @@
/**
* 删除任务
*/
function deleteTask(record?: any, isBatch?: boolean, params?: BatchActionQueryParams) {
function deleteTask(record?: TaskCenterTaskDetailItem, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('ms.taskCenter.deleteTaskTitle', { name: characterLimit(record?.taskName) });
let selectIds = [record?.id || ''];
if (isBatch) {
@ -358,7 +365,7 @@
});
}
function stopTask(record?: any, isBatch?: boolean, params?: BatchActionQueryParams) {
function stopTask(record?: TaskCenterTaskDetailItem, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('ms.taskCenter.stopTaskTitle', { name: characterLimit(record?.taskName) });
let selectIds = [record?.id || ''];
if (isBatch) {
@ -415,23 +422,67 @@
}
}
function rerunTask(record: any) {
function rerunTask(record: TaskCenterTaskDetailItem) {
console.log('rerunTask', record);
}
const executeResultId = ref('');
const caseExecuteResultDrawerVisible = ref(false);
const scenarioExecuteResultDrawerVisible = ref(false);
function checkExecuteResult(record: any) {
function checkExecuteResult(record: TaskCenterTaskDetailItem) {
executeResultId.value = record.id;
scenarioExecuteResultDrawerVisible.value = true;
if (record.resourceType === 'API_SCENARIO') {
scenarioExecuteResultDrawerVisible.value = true;
} else {
caseExecuteResultDrawerVisible.value = true;
}
}
onMounted(() => {
const currentResourcePoolRequest = {
system: getProjectTaskCenterResourcePools,
project: getOrgTaskCenterResourcePools,
org: getSystemTaskCenterResourcePools,
}[props.type];
async function initResourcePools() {
try {
const res = await currentResourcePoolRequest();
resourcePoolOptions.value = res.map((item) => ({
key: item,
value: item,
}));
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
async function initCurrentPageResourcePoolsStatus() {
const ids = propsRes.value.data.map((item) => item.id);
if (ids.length === 0) {
return;
}
try {
const res = await getResourcePoolsStatus(ids);
res.forEach((item) => {
const target = propsRes.value.data.find((task) => task.id === item.id);
if (target) {
target.resourcePoolNodeStatus = item.status;
}
});
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
onMounted(async () => {
if (props.id) {
keyword.value = props.id;
}
searchTask();
initResourcePools();
await loadList();
initCurrentPageResourcePoolsStatus();
});
await tableStore.initColumn(TableKeyEnum.TASK_CENTER_CASE_TASK_DETAIL, columns, 'drawer');

View File

@ -44,10 +44,16 @@
{{ t(executeMethodMap[record.triggerMode]) }}
</template>
<template #executeRate="{ record }">
<a-popover trigger="hover" position="bottom">
<div>{{ record.executeRate }}%</div>
<a-popover
v-model:popup-visible="record.executeRatePopVisible"
trigger="hover"
position="bottom"
:disabled="record.caseTotal === 0 || record.status === ExecuteStatusEnum.PENDING"
@popup-visible-change="($event) => handleExecuteRatePopVisibleChange($event, record)"
>
<div>{{ record.executeRate || '0.00' }}%</div>
<template #content>
<div class="flex w-[130px] flex-col gap-[8px]">
<a-spin :loading="record.loading" class="flex w-[130px] flex-col gap-[8px]">
<div class="ms-taskCenter-execute-rate-item">
<div class="ms-taskCenter-execute-rate-item-label">
{{ t('ms.taskCenter.executeFinishedRate') }}
@ -92,7 +98,7 @@
</div>
<div class="ms-taskCenter-execute-rate-item-value">{{ record.errorCount }}</div>
</div>
</div>
</a-spin>
</template>
</a-popover>
</template>
@ -123,6 +129,7 @@
</MsButton>
</template>
</ms-base-table>
<batchTaskReportDrawer v-model:visible="taskReportDrawerVisible" type="case" :module-type="reportModuleType" />
</template>
<script setup lang="ts">
@ -135,6 +142,7 @@
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import batchTaskReportDrawer from './batchTaskReportDrawer.vue';
import execStatus from './execStatus.vue';
import executionStatus from './executionStatus.vue';
@ -145,16 +153,26 @@
getProjectExecuteTaskStatistics,
getSystemExecuteTaskList,
getSystemExecuteTaskStatistics,
organizationDeleteTask,
organizationStopTask,
projectDeleteTask,
projectStopTask,
systemDeleteTask,
systemStopTask,
} from '@/api/modules/taskCenter';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore';
import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { TaskCenterTaskItem } from '@/models/taskCenter';
import { ReportEnum } from '@/enums/reportEnum';
import { ApiTestRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ExecuteResultEnum, ExecuteStatusEnum } from '@/enums/taskCenter';
import { ExecuteResultEnum, ExecuteStatusEnum, ExecuteTaskType, ExecuteTriggerMode } from '@/enums/taskCenter';
import { executeFinishedRateMap, executeMethodMap, executeResultMap, executeStatusMap } from './config';
@ -167,6 +185,7 @@
const { t } = useI18n();
const { openModal } = useModal();
const { openNewPage } = useOpenNewPage();
const tableStore = useTableStore();
const keyword = ref('');
@ -268,7 +287,7 @@
},
{
title: 'ms.taskCenter.operationUser',
dataIndex: 'createUser',
dataIndex: 'createUserName',
width: 100,
showTooltip: true,
},
@ -319,6 +338,18 @@
org: getOrganizationExecuteTaskStatistics,
}[props.type];
const currentStopTask = {
system: projectStopTask,
project: organizationStopTask,
org: systemStopTask,
}[props.type];
const currentDeleteTask = {
system: projectDeleteTask,
project: organizationDeleteTask,
org: systemDeleteTask,
}[props.type];
const tableBatchActions = {
baseAction: [
{
@ -347,9 +378,11 @@
(item) => {
return {
...item,
startTime: dayjs(item.startTime).format('YYYY-MM-DD HH:mm:ss'),
createTime: dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
endTime: dayjs(item.endTime).format('YYYY-MM-DD HH:mm:ss'),
loading: false,
executeRatePopVisible: false,
startTime: item.startTime ? dayjs(item.startTime).format('YYYY-MM-DD HH:mm:ss') : '-',
createTime: item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') : '-',
endTime: item.endTime ? dayjs(item.endTime).format('YYYY-MM-DD HH:mm:ss') : '-',
};
}
);
@ -359,115 +392,6 @@
loadList();
}
function showTaskDetail(id: string) {
emit('goDetail', id);
}
/**
* 删除任务
*/
function deleteTask(record?: any, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('ms.taskCenter.deleteTaskTitle', { name: characterLimit(record?.taskName) });
let selectIds = [record?.id || ''];
if (isBatch) {
title = t('ms.taskCenter.deleteCaseTaskTitle', {
count: params?.currentSelectCount || tableSelected.value.length,
});
selectIds = tableSelected.value as string[];
}
openModal({
type: 'error',
title,
content: t('ms.taskCenter.deleteCaseTaskTip'),
okText: t('common.confirmDelete'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'danger',
},
maskClosable: false,
onBeforeOk: async () => {
try {
// await deleteUserInfo({
// selectIds,
// selectAll: !!params?.selectAll,
// excludeIds: params?.excludeIds || [],
// condition: { keyword: keyword.value },
// });
Message.success(t('common.deleteSuccess'));
resetSelector();
loadList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
function stopTask(record?: any, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('ms.taskCenter.stopTaskTitle', { name: characterLimit(record?.taskName) });
let selectIds = [record?.id || ''];
if (isBatch) {
title = t('ms.taskCenter.batchStopTaskTitle', {
count: params?.currentSelectCount || tableSelected.value.length,
});
selectIds = tableSelected.value as string[];
}
openModal({
type: 'warning',
title,
content: t('ms.taskCenter.stopTimeTaskTip'),
okText: t('common.stopConfirm'),
cancelText: t('common.cancel'),
maskClosable: false,
onBeforeOk: async () => {
try {
// await deleteUserInfo({
// selectIds,
// selectAll: !!params?.selectAll,
// excludeIds: params?.excludeIds || [],
// condition: { keyword: keyword.value },
// });
Message.success(t('common.stopped'));
resetSelector();
loadList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
/**
* 处理表格选中后批量操作
* @param event 批量操作事件对象
*/
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params.selectedIds || [];
batchModalParams.value = params;
switch (event.eventTag) {
case 'delete':
deleteTask(undefined, true, params);
break;
case 'stop':
stopTask(undefined, true, params);
break;
default:
break;
}
}
function rerunTask(record: any) {
console.log('rerunTask', record);
}
function checkReport(record: any) {
console.log('checkReport', record);
}
async function initTaskStatistics() {
try {
const ids = propsRes.value.data.map((item) => item.id);
@ -496,8 +420,156 @@
initTaskStatistics();
}
async function handleExecuteRatePopVisibleChange(visible: boolean, record: TaskCenterTaskItem) {
if (visible) {
try {
record.loading = true;
const res = await currentExecuteTaskStatistics([record.id]);
record.executeRate = res[0].executeRate;
record.pendingCount = res[0].pendingCount;
record.successCount = res[0].successCount;
record.fakeErrorCount = res[0].fakeErrorCount;
record.errorCount = res[0].errorCount;
record.caseTotal = res[0].caseTotal;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
record.loading = false;
}
}
}
function showTaskDetail(id: string) {
emit('goDetail', id);
}
/**
* 删除任务
*/
function deleteTask(record?: TaskCenterTaskItem, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('ms.taskCenter.deleteTaskTitle', { name: characterLimit(record?.taskName) });
let selectIds = [record?.id || ''];
if (isBatch) {
title = t('ms.taskCenter.deleteCaseTaskTitle', {
count: params?.currentSelectCount || tableSelected.value.length,
});
selectIds = tableSelected.value as string[];
}
openModal({
type: 'error',
title,
content: t('ms.taskCenter.deleteCaseTaskTip'),
okText: t('common.confirmDelete'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'danger',
},
maskClosable: false,
onBeforeOk: async () => {
try {
if (isBatch) {
// await deleteUserInfo({
// selectIds,
// selectAll: !!params?.selectAll,
// excludeIds: params?.excludeIds || [],
// condition: { keyword: keyword.value },
// });
} else {
await currentDeleteTask(record?.id || '');
}
Message.success(t('common.deleteSuccess'));
resetSelector();
refresh();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
function stopTask(record?: TaskCenterTaskItem, isBatch?: boolean, params?: BatchActionQueryParams) {
let title = t('ms.taskCenter.stopTaskTitle', { name: characterLimit(record?.taskName) });
let selectIds = [record?.id || ''];
if (isBatch) {
title = t('ms.taskCenter.batchStopTaskTitle', {
count: params?.currentSelectCount || tableSelected.value.length,
});
selectIds = tableSelected.value as string[];
}
openModal({
type: 'warning',
title,
content: t('ms.taskCenter.stopTimeTaskTip'),
okText: t('common.stopConfirm'),
cancelText: t('common.cancel'),
maskClosable: false,
onBeforeOk: async () => {
try {
if (isBatch) {
// await deleteUserInfo({
// selectIds,
// selectAll: !!params?.selectAll,
// excludeIds: params?.excludeIds || [],
// condition: { keyword: keyword.value },
// });
} else {
await currentStopTask(record?.id || '');
}
Message.success(t('common.stopped'));
resetSelector();
refresh();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
/**
* 处理表格选中后批量操作
* @param event 批量操作事件对象
*/
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params.selectedIds || [];
batchModalParams.value = params;
switch (event.eventTag) {
case 'delete':
deleteTask(undefined, true, params);
break;
case 'stop':
stopTask(undefined, true, params);
break;
default:
break;
}
}
function rerunTask(record: TaskCenterTaskItem) {
console.log('rerunTask', record);
}
const taskReportDrawerVisible = ref(false);
const reportModuleType = ref();
function checkReport(record: TaskCenterTaskItem) {
if (record.taskType.includes('BATCH')) {
reportModuleType.value = record.taskType.includes('CASE')
? ReportEnum.API_REPORT
: ReportEnum.API_SCENARIO_REPORT;
taskReportDrawerVisible.value = true;
} else if (record.taskType === ExecuteTaskType.API_SCENARIO) {
openNewPage(ApiTestRouteEnum.API_TEST_REPORT);
} else if (record.taskType === ExecuteTaskType.TEST_PLAN) {
openNewPage(TestPlanRouteEnum.TEST_PLAN_REPORT);
}
}
onMounted(async () => {
loadList();
searchTask();
});
watch(

View File

@ -1,7 +1,7 @@
<template>
<div class="flex items-center justify-start">
<MsIcon :type="getExecutionResult()?.icon" :class="`text-[${getExecutionResult()?.color}]`" size="14" />
<span class="ml-1">{{ t(getExecutionResult()?.label) }}</span>
<span class="ml-1">{{ t(getExecutionResult()?.label || '-') }}</span>
<!-- <a-tooltip v-if="props.scriptIdentifier" :content="getMsg()">
<MsTag
class="ml-2"

View File

@ -30,15 +30,22 @@
</a-button>
</template>
<template #status="{ record }">
<a-switch v-model:model-value="record.enable" size="small"></a-switch>
<a-switch
v-model:model-value="record.enable"
size="small"
:before-change="() => handleBeforeEnableChange(record)"
></a-switch>
</template>
<template #resourceType="{ record }">
{{ t(scheduleTaskTypeMap[record.resourceType]) }}
</template>
<template #action="{ record }">
<MsButton v-permission="['SYSTEM_USER:READ+DELETE']" @click="deleteTask(record)">
<MsButton v-permission="['SYSTEM_USER:READ+DELETE']" class="!mr-[12px]" @click="deleteTask(record)">
{{ t('common.delete') }}
</MsButton>
<MsButton v-permission="['SYSTEM_USER:READ+DELETE']" class="!mr-0" @click="checkDetail(record)">
{{ t('common.detail') }}
</MsButton>
</template>
</ms-base-table>
</template>
@ -57,10 +64,12 @@
import { getOrganizationScheduleList, getProjectScheduleList, getSystemScheduleList } from '@/api/modules/taskCenter';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore';
import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { ApiTestRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { scheduleTaskTypeMap } from './config';
@ -71,6 +80,7 @@
const { t } = useI18n();
const { openModal } = useModal();
const { openNewPage } = useOpenNewPage();
const tableStore = useTableStore();
const keyword = ref('');
@ -146,7 +156,7 @@
slotName: 'action',
dataIndex: 'operation',
fixed: 'right',
width: 60,
width: 110,
},
];
if (props.type === 'system') {
@ -209,9 +219,9 @@
(item) => {
return {
...item,
operationTime: dayjs(item.operationTime).format('YYYY-MM-DD HH:mm:ss'),
lastFinishTime: dayjs(item.lastFinishTime).format('YYYY-MM-DD HH:mm:ss'),
nextExecuteTime: dayjs(item.nextExecuteTime).format('YYYY-MM-DD HH:mm:ss'),
operationTime: item.operationTime ? dayjs(item.operationTime).format('YYYY-MM-DD HH:mm:ss') : '-',
lastFinishTime: item.lastFinishTime ? dayjs(item.lastFinishTime).format('YYYY-MM-DD HH:mm:ss') : '-',
nextExecuteTime: item.nextExecuteTime ? dayjs(item.nextExecuteTime).format('YYYY-MM-DD HH:mm:ss') : '-',
};
}
);
@ -271,6 +281,23 @@
console.log(record);
}
function checkDetail(record: any) {
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
taskDrawer: true,
});
}
async function handleBeforeEnableChange(record: any) {
try {
Message.success(t(record.enable ? 'ms.taskCenter.closeTaskSuccess' : 'ms.taskCenter.openTaskSuccess'));
return true;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
return false;
}
}
/**
* 处理表格选中后批量操作
* @param event 批量操作事件对象

View File

@ -1,6 +1,6 @@
<template>
<div>
<a-tabs v-model:active-key="activeTab" class="no-content">
<a-tabs v-model:active-key="activeTab" class="no-content" @change="handleTabChange">
<a-tab-pane v-for="item of tabList" :key="item.value" :title="item.label" />
</a-tabs>
<a-divider margin="0"></a-divider>
@ -22,7 +22,10 @@
import systemTaskTable from './component/systemTaskTable.vue';
import { useI18n } from '@/hooks/useI18n';
import useLocalForage from '@/hooks/useLocalForage';
import useGlobalStore from '@/store/modules/global';
import { GlobalEventNameEnum } from '@/enums/commonEnum';
import { TaskCenterEnum } from '@/enums/taskCenter';
const props = defineProps<{
@ -32,6 +35,8 @@
const { t } = useI18n();
const route = useRoute();
const globalStore = useGlobalStore();
const { getItem, setItem } = useLocalForage();
const tabList = ref([
{
@ -55,6 +60,24 @@
activeTaskId.value = id;
activeTab.value = TaskCenterEnum.DETAIL;
}
function handleTabChange(key: string | number) {
setItem('taskCenterActiveTab', key);
}
watch(
() => globalStore.getGlobalEvent,
(event) => {
if (event && event.id && event.name === GlobalEventNameEnum.OPEN_TASK_CENTER) {
activeTab.value = event.params?.tab;
setItem('taskCenterActiveTab', event.params?.tab);
}
}
);
onBeforeMount(async () => {
activeTab.value = (await getItem('taskCenterActiveTab')) || TaskCenterEnum.CASE;
});
</script>
<style scoped lang="less">

View File

@ -8,7 +8,7 @@ export default {
'ms.taskCenter.taskName': 'Task Name',
'ms.taskCenter.executeStatus': 'Execution Status',
'ms.taskCenter.executeMethod': 'Execution Method',
'ms.taskCenter.executeResult': 'Execution Result',
'ms.taskCenter.executeResult': 'Result',
'ms.taskCenter.caseCount': 'Case Count',
'ms.taskCenter.executeFinishedRate': 'Execution Completion Rate',
'ms.taskCenter.createTime': 'Initiation Time',
@ -53,4 +53,6 @@ export default {
'ms.taskCenter.apiImport': 'API Import',
'ms.taskCenter.apiScenario': 'Api Scenario',
'ms.taskCenter.thirdPartSync': 'Third-party sync',
'ms.taskCenter.openTaskSuccess': 'Task started successfully',
'ms.taskCenter.closeTaskSuccess': 'Task closed successfully',
};

View File

@ -53,4 +53,6 @@ export default {
'ms.taskCenter.apiImport': 'API导入',
'ms.taskCenter.apiScenario': '接口场景',
'ms.taskCenter.thirdPartSync': '第三方同步',
'ms.taskCenter.openTaskSuccess': '任务开启成功',
'ms.taskCenter.closeTaskSuccess': '任务关闭成功',
};

View File

@ -362,7 +362,7 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { FormInstance, Message } from '@arco-design/web-vue';
import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
@ -381,6 +381,7 @@
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import MsRichMessage from '@/components/business/ms-rich-message/index.vue';
import MsStatusTag from '@/components/business/ms-status-tag/index.vue';
import executeHistoryTable from '../detail/executeHistory/index.vue';
import ActionModal from './actionModal.vue';
@ -391,7 +392,7 @@
import PlanExpandRow from '@/views/test-plan/testPlan/components/planExpandRow.vue';
import {
addTestPlan,
// addTestPlan,
archivedPlan,
batchArchivedPlan,
batchCopyPlan,
@ -414,12 +415,13 @@
import useModal from '@/hooks/useModal';
import { useAppStore, useTableStore } from '@/store';
import useCacheStore from '@/store/modules/cache/cache';
import { characterLimit } from '@/utils';
import useGlobalStore from '@/store/modules/global';
import { characterLimit, getGenerateId } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { DragSortParams, ModuleTreeNode, TableQueryParams } from '@/models/common';
import type {
AddTestPlanParams,
// AddTestPlanParams,
BatchExecutePlan,
BatchMoveParams,
CreateTask,
@ -429,18 +431,20 @@
TestPlanItem,
} from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { GlobalEventNameEnum } from '@/enums/commonEnum';
import { RouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { TaskCenterEnum } from '@/enums/taskCenter';
import { testPlanTypeEnum } from '@/enums/testPlanEnum';
import { planStatusOptions } from '../config';
import { getModules } from '@/views/case-management/caseManagementFeature/components/utils';
const cacheStore = useCacheStore();
const tableStore = useTableStore();
const appStore = useAppStore();
const globalStore = useGlobalStore();
const router = useRouter();
const { t } = useI18n();
const { openModal } = useModal();
@ -1001,7 +1005,35 @@
try {
await executePlanOrGroup(executeForm.value);
cancelHandler();
Message.success(t('case.detail.execute.success'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.CASE,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
@ -1022,7 +1054,35 @@
executionSource: 'MANUAL',
};
await executeSinglePlan(params);
Message.success(t('case.detail.execute.success'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.CASE,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
fetchData();
cancelHandler();
} catch (error) {
@ -1535,73 +1595,73 @@
emitTableParams();
}
const showQuickCreateForm = ref(false);
const quickCreateFormRef = ref<FormInstance>();
// const showQuickCreateForm = ref(false);
// const quickCreateFormRef = ref<FormInstance>();
const initPlanGroupForm: AddTestPlanParams = {
groupId: 'NONE',
name: '',
projectId: appStore.currentProjectId,
moduleId: '',
cycle: [],
tags: [],
description: '',
testPlanning: false,
automaticStatusUpdate: true,
repeatCase: false,
passThreshold: 100,
type: testPlanTypeEnum.GROUP,
baseAssociateCaseRequest: { selectIds: [], selectAll: false, condition: {} },
};
const quickCreateForm = ref<AddTestPlanParams>(cloneDeep(initPlanGroupForm));
// const initPlanGroupForm: AddTestPlanParams = {
// groupId: 'NONE',
// name: '',
// projectId: appStore.currentProjectId,
// moduleId: '',
// cycle: [],
// tags: [],
// description: '',
// testPlanning: false,
// automaticStatusUpdate: true,
// repeatCase: false,
// passThreshold: 100,
// type: testPlanTypeEnum.GROUP,
// baseAssociateCaseRequest: { selectIds: [], selectAll: false, condition: {} },
// };
// const quickCreateForm = ref<AddTestPlanParams>(cloneDeep(initPlanGroupForm));
const quickCreateLoading = ref(false);
// const quickCreateLoading = ref(false);
function quickCreateCancel() {
showQuickCreateForm.value = false;
quickCreateForm.value = cloneDeep(initPlanGroupForm);
quickCreateFormRef.value?.resetFields();
}
// function quickCreateCancel() {
// showQuickCreateForm.value = false;
// quickCreateForm.value = cloneDeep(initPlanGroupForm);
// quickCreateFormRef.value?.resetFields();
// }
/**
* 快速创建测试计划或者测试计划组
*/
const createType = ref<keyof typeof testPlanTypeEnum>(showType.value);
// const createType = ref<keyof typeof testPlanTypeEnum>(showType.value);
// TODO:
function quickCreateConfirm() {
quickCreateFormRef.value?.validate(async (errors) => {
if (!errors) {
try {
quickCreateLoading.value = true;
const params = {
...cloneDeep(quickCreateForm.value),
groupId: 'NONE',
projectId: appStore.currentProjectId,
moduleId: props.activeFolder === 'all' ? 'root' : props.activeFolder,
testPlanning: false,
automaticStatusUpdate: true,
repeatCase: false,
passThreshold: 100,
type: showType.value === testPlanTypeEnum.ALL ? createType.value : showType.value,
};
await addTestPlan(params);
Message.success(t('common.createSuccess'));
quickCreateCancel();
fetchData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
quickCreateLoading.value = false;
}
}
});
}
// function quickCreateConfirm() {
// quickCreateFormRef.value?.validate(async (errors) => {
// if (!errors) {
// try {
// quickCreateLoading.value = true;
// const params = {
// ...cloneDeep(quickCreateForm.value),
// groupId: 'NONE',
// projectId: appStore.currentProjectId,
// moduleId: props.activeFolder === 'all' ? 'root' : props.activeFolder,
// testPlanning: false,
// automaticStatusUpdate: true,
// repeatCase: false,
// passThreshold: 100,
// type: showType.value === testPlanTypeEnum.ALL ? createType.value : showType.value,
// };
// await addTestPlan(params);
// Message.success(t('common.createSuccess'));
// quickCreateCancel();
// fetchData();
// } catch (error) {
// // eslint-disable-next-line no-console
// console.log(error);
// } finally {
// quickCreateLoading.value = false;
// }
// }
// });
// }
// TODO:
function handleSelect(value: string | number | Record<string, any> | undefined) {
showQuickCreateForm.value = true;
createType.value = value as keyof typeof testPlanTypeEnum;
}
// function handleSelect(value: string | number | Record<string, any> | undefined) {
// showQuickCreateForm.value = true;
// createType.value = value as keyof typeof testPlanTypeEnum;
// }
//
function archivedChangeHandler() {

View File

@ -150,6 +150,7 @@
import useTable from '@/components/pure/ms-table/useTable';
import MsBugOperation from '@/components/business/ms-bug-operation/index.vue';
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import MsRichMessage from '@/components/business/ms-rich-message/index.vue';
import ApiMethodName from '@/views/api-test/components/apiMethodName.vue';
import apiStatus from '@/views/api-test/components/apiStatus.vue';
import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
@ -178,7 +179,8 @@
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app';
import { characterLimit } from '@/utils';
import useGlobalStore from '@/store/modules/global';
import { characterLimit, getGenerateId } from '@/utils';
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
import { DragSortParams, ModuleTreeNode, TableQueryParams } from '@/models/common';
@ -187,10 +189,12 @@
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
import { AssociatedBugApiTypeEnum } from '@/enums/associateBugEnum';
import { CaseLinkEnum } from '@/enums/caseEnum';
import { GlobalEventNameEnum } from '@/enums/commonEnum';
import { ReportEnum } from '@/enums/reportEnum';
import { ApiTestRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { TaskCenterEnum } from '@/enums/taskCenter';
import {
casePriorityOptions,
@ -222,6 +226,7 @@
const { openModal } = useModal();
const { openNewPage } = useOpenNewPage();
const appStore = useAppStore();
const globalStore = useGlobalStore();
const keyword = ref('');
@ -823,7 +828,35 @@
try {
tableLoading.value = true;
await runApiCase(record.id);
Message.success(t('common.executionSuccess'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.DETAIL,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
resetSelectorAndCaseList();
emit('refresh');
} catch (error) {
@ -845,7 +878,35 @@
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
});
Message.success(t('common.operationSuccess'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.DETAIL,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
resetSelectorAndCaseList();
emit('refresh');
} catch (error) {

View File

@ -150,6 +150,7 @@
import useTable from '@/components/pure/ms-table/useTable';
import MsBugOperation from '@/components/business/ms-bug-operation/index.vue';
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import MsRichMessage from '@/components/business/ms-rich-message/index.vue';
import apiStatus from '@/views/api-test/components/apiStatus.vue';
import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
@ -177,7 +178,8 @@
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app';
import { characterLimit } from '@/utils';
import useGlobalStore from '@/store/modules/global';
import { characterLimit, getGenerateId } from '@/utils';
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
import { DragSortParams, ModuleTreeNode, TableQueryParams } from '@/models/common';
@ -186,10 +188,12 @@
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
import { AssociatedBugApiTypeEnum } from '@/enums/associateBugEnum';
import { CaseLinkEnum } from '@/enums/caseEnum';
import { GlobalEventNameEnum } from '@/enums/commonEnum';
import { ReportEnum } from '@/enums/reportEnum';
import { ApiTestRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { TaskCenterEnum } from '@/enums/taskCenter';
import { casePriorityOptions, lastReportStatusListOptions } from '@/views/api-test/components/config';
import { scenarioStatusOptions } from '@/views/api-test/scenario/components/config';
@ -216,6 +220,7 @@
const { openModal } = useModal();
const { openNewPage } = useOpenNewPage();
const appStore = useAppStore();
const globalStore = useGlobalStore();
const keyword = ref('');
@ -793,7 +798,35 @@
try {
tableLoading.value = true;
await runApiScenario(record.id);
Message.success(t('common.executionSuccess'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.DETAIL,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
resetSelectorAndCaseList();
emit('refresh');
} catch (error) {
@ -815,7 +848,35 @@
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
});
Message.success(t('common.operationSuccess'));
Message.success({
content: () =>
h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
gap: '4px',
},
},
[
h(MsRichMessage, {
content: t('case.detail.execute.success'),
onGoDetail() {
globalStore.dispatchGlobalEvent({
id: getGenerateId(),
name: GlobalEventNameEnum.OPEN_TASK_CENTER,
params: {
tab: TaskCenterEnum.DETAIL,
},
});
},
}),
]
),
duration: 5000,
closable: true,
});
resetSelectorAndCaseList();
emit('refresh');
} catch (error) {