feat(全局): 任务中心&部分bug修复&需求调整
This commit is contained in:
parent
fa5aa54071
commit
50139a84ac
|
@ -12,6 +12,7 @@
|
|||
|
||||
import { getProjectInfo } from '@/api/modules/project-management/basicInfo';
|
||||
import { saveBaseUrl } from '@/api/modules/setting/config';
|
||||
import { getUserHasProjectPermission } from '@/api/modules/system';
|
||||
import { GetPlatformIconUrl } from '@/api/requrls/setting/config';
|
||||
// import GlobalSetting from '@/components/pure/global-setting/index.vue';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
|
@ -70,21 +71,32 @@
|
|||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
const checkIsLogin = async () => {
|
||||
const isLogin = await userStore.isLogin();
|
||||
const isLoginPage = route.name === 'login';
|
||||
if (isLogin && appStore.currentProjectId && appStore.currentProjectId !== 'no_such_project') {
|
||||
// 当前为登陆状态,且已经选择了项目,初始化当前项目配置
|
||||
try {
|
||||
const res = await getProjectInfo(appStore.currentProjectId);
|
||||
if (res && (res.deleted || !res.enable)) {
|
||||
// 如果项目被删除或者被禁用,跳转到无项目页面
|
||||
const HasProjectPermission = await getUserHasProjectPermission(appStore.currentProjectId);
|
||||
if (!HasProjectPermission) {
|
||||
// 没有项目权限(用户所在的当前项目被禁用&用户被移除出去该项目)
|
||||
router.push({
|
||||
name: NO_PROJECT_ROUTE_NAME,
|
||||
});
|
||||
return;
|
||||
}
|
||||
appStore.setCurrentMenuConfig(res?.moduleIds || []);
|
||||
const res = await getProjectInfo(appStore.currentProjectId);
|
||||
if (res.deleted) {
|
||||
// 如果项目被删除或者被禁用,跳转到无项目页面
|
||||
router.push({
|
||||
name: NO_PROJECT_ROUTE_NAME,
|
||||
});
|
||||
}
|
||||
|
||||
if (res) {
|
||||
appStore.setCurrentMenuConfig(res?.moduleIds || []);
|
||||
}
|
||||
} catch (err) {
|
||||
appStore.setCurrentMenuConfig([]);
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import MSR from '@/api/http';
|
||||
import {
|
||||
deleteScheduleSysTaskUrl,
|
||||
scheduleOrgCenterListUrl,
|
||||
scheduleProCenterListUrl,
|
||||
scheduleSysCenterListUrl,
|
||||
taskOrgRealCenterListUrl,
|
||||
taskProRealCenterListUrl,
|
||||
taskSysRealCenterListUrl,
|
||||
} from '@/api/requrls/project-management/taskCenter';
|
||||
|
||||
import type { CommonList, TableQueryParams } from '@/models/common';
|
||||
import type { RealTaskCenterApiCaseItem, TimingTaskCenterApiCaseItem } from '@/models/projectManagement/taskCenter';
|
||||
|
||||
// 实时任务
|
||||
export function getRealSysApiCaseList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<RealTaskCenterApiCaseItem>>({ url: taskSysRealCenterListUrl, data });
|
||||
}
|
||||
|
||||
export function getRealOrdApiCaseList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<RealTaskCenterApiCaseItem>>({ url: taskOrgRealCenterListUrl, data });
|
||||
}
|
||||
|
||||
export function getRealProApiCaseList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<RealTaskCenterApiCaseItem>>({ url: taskProRealCenterListUrl, data });
|
||||
}
|
||||
|
||||
// 定时任务
|
||||
|
||||
export function getScheduleSysApiCaseList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<TimingTaskCenterApiCaseItem>>({ url: scheduleSysCenterListUrl, data });
|
||||
}
|
||||
|
||||
export function getScheduleOrgApiCaseList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<TimingTaskCenterApiCaseItem>>({ url: scheduleOrgCenterListUrl, data });
|
||||
}
|
||||
|
||||
export function getScheduleProApiCaseList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<TimingTaskCenterApiCaseItem>>({ url: scheduleProCenterListUrl, data });
|
||||
}
|
||||
|
||||
export function deleteScheduleSysTask(id: string) {
|
||||
return MSR.get({ url: `${deleteScheduleSysTaskUrl}/${id}` });
|
||||
}
|
||||
export default {};
|
|
@ -1,6 +1,12 @@
|
|||
// 系统全局类的接口
|
||||
import MSR from '@/api/http/index';
|
||||
import { GetVersionUrl, OrgOptionsUrl, PackageTypeUrl, SwitchOrgUrl } from '@/api/requrls/system';
|
||||
import {
|
||||
GetVersionUrl,
|
||||
OrgOptionsUrl,
|
||||
PackageTypeUrl,
|
||||
SwitchOrgUrl,
|
||||
userHasProjectPermissionUrl,
|
||||
} from '@/api/requrls/system';
|
||||
|
||||
// 获取系统版本
|
||||
export function getSystemVersion() {
|
||||
|
@ -21,3 +27,8 @@ export function switchUserOrg(organizationId: string, userId: string) {
|
|||
export function getPackageType() {
|
||||
return MSR.get<string>({ url: PackageTypeUrl });
|
||||
}
|
||||
|
||||
// 获取当前用户是否具备项目权限
|
||||
export function getUserHasProjectPermission(userId: string) {
|
||||
return MSR.get({ url: `${userHasProjectPermissionUrl}/${userId}` });
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// 系统管理
|
||||
// 任务中心-实时任务-接口用例/场景
|
||||
export const taskSysRealCenterListUrl = '/task/center/api/system/real-time/page';
|
||||
// 系统-任务中心-定时任务列表
|
||||
export const scheduleSysCenterListUrl = '/task/center/system/schedule/page';
|
||||
// 系统-任务中心-删除定时任务
|
||||
export const deleteScheduleSysTaskUrl = '/task/center/schedule/delete';
|
||||
|
||||
// 组织管理
|
||||
// 任务中心-实时任务-接口用例/场景
|
||||
export const taskOrgRealCenterListUrl = '/task/center/api/org/real-time/page';
|
||||
|
||||
// 系统-任务中心-定时任务列表
|
||||
export const scheduleOrgCenterListUrl = '/task/center/org/schedule/page';
|
||||
|
||||
// 项目管理
|
||||
// 任务中心-实时任务-接口用例/场景
|
||||
export const taskProRealCenterListUrl = '/task/center/api/project/real-time/page';
|
||||
|
||||
// 系统-任务中心-定时任务列表
|
||||
export const scheduleProCenterListUrl = '/task/center/project/schedule/page';
|
||||
|
||||
export default {
|
||||
taskSysRealCenterListUrl,
|
||||
scheduleSysCenterListUrl,
|
||||
deleteScheduleSysTaskUrl,
|
||||
taskOrgRealCenterListUrl,
|
||||
scheduleOrgCenterListUrl,
|
||||
taskProRealCenterListUrl,
|
||||
scheduleProCenterListUrl,
|
||||
};
|
|
@ -4,3 +4,4 @@ export const GetVersionUrl = '/system/version/current';
|
|||
export const OrgOptionsUrl = '/system/organization/switch-option';
|
||||
export const SwitchOrgUrl = '/system/organization/switch';
|
||||
export const PackageTypeUrl = '/system/version/package-type';
|
||||
export const userHasProjectPermissionUrl = '/project/has-permission'; // 判断当前用户是否还在项目里或者项目禁止有权限操作
|
||||
|
|
|
@ -626,9 +626,6 @@
|
|||
}
|
||||
|
||||
/** 开关 **/
|
||||
.arco-switch[disabled] {
|
||||
background: var(--color-text-n8);
|
||||
}
|
||||
.arco-switch-type-line.arco-switch-small {
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
|
|
|
@ -202,6 +202,7 @@
|
|||
orgListLoading.value = true;
|
||||
const res = await getOrgOptions();
|
||||
originOrgList.value = res || [];
|
||||
appStore.setOrdList(originOrgList.value);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
<slot name="title">
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
{{ props.title }}
|
||||
<a-tooltip :content="props.title">
|
||||
<span> {{ props.title }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<slot name="headerLeft"></slot>
|
||||
<a-tag v-if="titleTag" :color="props.titleTagColor" class="ml-[8px] mr-auto">
|
||||
{{ props.titleTag }}
|
||||
|
|
|
@ -10,7 +10,8 @@ export const INPUT = {
|
|||
field: 'fieldName',
|
||||
value: '',
|
||||
props: {
|
||||
placeholder: t('formCreate.PleaseEnter'),
|
||||
'placeholder': t('formCreate.PleaseEnter'),
|
||||
'max-length': 255,
|
||||
},
|
||||
};
|
||||
export const SELECT = {
|
||||
|
@ -150,6 +151,7 @@ export const TEXTAREA = {
|
|||
minRows: 1,
|
||||
maxRows: 3,
|
||||
},
|
||||
'max-length': 1000,
|
||||
},
|
||||
};
|
||||
export const JIRAKEY = {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<FormCreate v-model:api="fApi" :rule="formRuleList" :option="props.options || option"> </FormCreate>
|
||||
<FormCreate v-model:api="fApi" :rule="formRuleList" :option="props.options || option" @change="changeHandler">
|
||||
</FormCreate>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -268,12 +269,16 @@
|
|||
},
|
||||
};
|
||||
|
||||
function changeHandler(value: any) {
|
||||
fApi.value.validateField(value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formRuleList.value,
|
||||
(val) => {
|
||||
if (val) emit('update:form-item', formRuleList.value);
|
||||
fApi.value.refreshValidate();
|
||||
fApi.value.clearValidateState();
|
||||
if (val) {
|
||||
emit('update:form-item', formRuleList.value);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
|
@ -291,13 +296,4 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.arco-form-item-status-success .arco-select-view:not(.arco-select-view-disabled).arco-select-view-focus) {
|
||||
border-color: var(--color-text-input-border);
|
||||
background: none;
|
||||
}
|
||||
:deep(.arco-form-item-status-success .arco-select-view:not(.arco-select-view-disabled)) {
|
||||
border-color: var(--color-text-input-border);
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
hide-button
|
||||
:formatter="handleFormatter"
|
||||
@change="handleChange"
|
||||
@enter="handleChange"
|
||||
/>
|
||||
<span v-if="$slots['jumper-append']" :class="`${prefixCls}-append`"><slot name="jumper-append" /></span>
|
||||
<span :class="`${prefixCls}-total-page`" :style="{ 'min-width': totalPageWidth }">{{
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
</li>
|
||||
<li>
|
||||
<a-tooltip :content="t('settings.navbar.task')">
|
||||
<a-button type="secondary">
|
||||
<a-button type="secondary" @click="goTaskCenter">
|
||||
<template #icon>
|
||||
<icon-calendar-clock />
|
||||
</template>
|
||||
|
@ -135,6 +135,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<TaskCenterModal v-model:visible="taskCenterVisible" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -145,6 +146,7 @@
|
|||
|
||||
import TopMenu from '@/components/business/ms-top-menu/index.vue';
|
||||
import MessageBox from '../message-box/index.vue';
|
||||
import TaskCenterModal from './taskCenterModal.vue';
|
||||
|
||||
import { switchProject } from '@/api/modules/project-management/project';
|
||||
import { MENU_LEVEL, type PathMapRoute } from '@/config/pathMap';
|
||||
|
@ -234,6 +236,11 @@
|
|||
});
|
||||
refBtn.value.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const taskCenterVisible = ref<boolean>(false);
|
||||
function goTaskCenter() {
|
||||
taskCenterVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="innerVisible"
|
||||
class="ms-modal-no-padding"
|
||||
width="100%"
|
||||
title-align="start"
|
||||
unmount-on-close
|
||||
:footer="false"
|
||||
modal-class="modalSelf"
|
||||
:body-style="{
|
||||
height: '100%',
|
||||
}"
|
||||
:modal-style="{
|
||||
padding: '16px 16px 0',
|
||||
}"
|
||||
esc-to-close
|
||||
fullscreen
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template #title> {{ t('settings.navbar.task') }}</template>
|
||||
|
||||
<div class="divider h-full">
|
||||
<TaskCenter group="system" mode="modal"></TaskCenter>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import TaskCenter from '@/views/project-management/taskCenter/component/taskCom.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const innerVisible = useVModel(props, 'visible', emit);
|
||||
|
||||
function handleOk() {}
|
||||
function handleCancel() {
|
||||
innerVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.divider {
|
||||
border-top: 1px solid var(--color-text-n8);
|
||||
}
|
||||
</style>
|
|
@ -330,12 +330,19 @@ export const pathMap: PathMapItem[] = [
|
|||
level: MENU_LEVEL[1],
|
||||
},
|
||||
{
|
||||
key: 'SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL', // 系统设置-模板管理-详情
|
||||
key: 'SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_CREATE', // 系统设置-模板管理-创建
|
||||
locale: 'menu.settings.organization.templateManagementDetail',
|
||||
route: RouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[1],
|
||||
},
|
||||
{
|
||||
key: 'SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_UPDATE', // 系统设置-模板管理-更新模版
|
||||
locale: 'menu.settings.organization.templateManagementEdit',
|
||||
route: RouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[1],
|
||||
},
|
||||
{
|
||||
key: 'SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_WORKFLOW', // 系统设置-模板管理-工作流
|
||||
locale: 'menu.settings.organization.templateManagementWorkFlow',
|
||||
|
@ -375,6 +382,13 @@ export const pathMap: PathMapItem[] = [
|
|||
permission: [],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
// {
|
||||
// key: 'PROJECT_MANAGEMENT_PERMISSION_VERSION', // 项目管理-项目与权限-项目版本
|
||||
// locale: 'project.permission.projectVersion',
|
||||
// route: RouteEnum.PROJECT_MANAGEMENT_PERMISSION_VERSION,
|
||||
// permission: [],
|
||||
// level: MENU_LEVEL[2],
|
||||
// },
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_PERMISSION_MEMBER', // 项目管理-项目与权限-成员
|
||||
locale: 'project.permission.member',
|
||||
|
@ -413,12 +427,19 @@ export const pathMap: PathMapItem[] = [
|
|||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL', // 项目管理-模板管理-详情
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_CREATE', // 项目管理-模板管理-创建模版
|
||||
locale: 'menu.settings.organization.templateManagementDetail',
|
||||
route: RouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_UPDATE', // 项目管理-模板管理-更新模版
|
||||
locale: 'menu.settings.organization.templateManagementEdit',
|
||||
route: RouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_WORKFLOW', // 项目管理-模板管理-工作流
|
||||
locale: 'menu.settings.organization.templateManagementWorkFlow',
|
||||
|
@ -490,7 +511,7 @@ export const pathMap: PathMapItem[] = [
|
|||
],
|
||||
},
|
||||
// 测试计划
|
||||
/* {
|
||||
{
|
||||
key: 'TEST_PLAN', // 测试计划
|
||||
locale: 'menu.testPlan',
|
||||
route: RouteEnum.TEST_PLAN,
|
||||
|
@ -505,7 +526,7 @@ export const pathMap: PathMapItem[] = [
|
|||
level: MENU_LEVEL[2],
|
||||
},
|
||||
],
|
||||
}, */
|
||||
},
|
||||
{
|
||||
key: 'PERSONAL_INFORMATION', // 个人信息
|
||||
locale: 'ms.personal',
|
||||
|
@ -550,4 +571,12 @@ export const pathMap: PathMapItem[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
// 系统
|
||||
{
|
||||
key: 'SYSTEM',
|
||||
locale: 'menu.settings.system', // 系统
|
||||
route: '',
|
||||
permission: [],
|
||||
level: MENU_LEVEL[0],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -34,6 +34,7 @@ export enum ProjectManagementRouteEnum {
|
|||
PROJECT_MANAGEMENT_MESSAGE_MANAGEMENT = 'projectManagementMessageManagement',
|
||||
PROJECT_MANAGEMENT_COMMON_SCRIPT = 'projectManagementCommonScript',
|
||||
PROJECT_MANAGEMENT_MESSAGE_MANAGEMENT_EDIT = 'projectManagementMessageManagementEdit',
|
||||
PROJECT_MANAGEMENT_TASK_CENTER = 'projectManagementTaskCenter',
|
||||
PROJECT_MANAGEMENT_LOG = 'projectManagementLog',
|
||||
PROJECT_MANAGEMENT_PERMISSION = 'projectManagementPermission',
|
||||
PROJECT_MANAGEMENT_PERMISSION_BASIC_INFO = 'projectManagementPermissionBasicInfo',
|
||||
|
@ -86,6 +87,7 @@ export enum SettingRouteEnum {
|
|||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_WORKFLOW = 'settingOrganizationTemplateWorkFlow',
|
||||
SETTING_ORGANIZATION_SERVICE = 'settingOrganizationService',
|
||||
SETTING_ORGANIZATION_LOG = 'settingOrganizationLog',
|
||||
SETTING_ORGANIZATION_TASK_CENTER = 'settingOrganizationTaskCenter',
|
||||
}
|
||||
|
||||
export const RouteEnum = {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// 模板展示字段icon
|
||||
export enum TaskCenterEnum {
|
||||
API_CASE = 'API_CASE', // 接口用例
|
||||
API_SCENARIO = 'API_SCENARIO', // 接口场景
|
||||
UI_TEST = 'UI_TEST', // ui测试
|
||||
LOAD_TEST = 'LOAD_TEST', // 性能测试
|
||||
TEST_PLAN = 'TEST_PLAN', // 测试计划
|
||||
TEST_RESOURCE = 'TEST_RESOURCE', // 测试资源
|
||||
API_IMPORT = 'API_IMPORT', // API导入
|
||||
}
|
||||
|
||||
// 执行方式
|
||||
export enum ExecutionMethods {
|
||||
SCHEDULE = 'SCHEDULE', // 定时任务
|
||||
MANUAL = 'MANUAL', // 手动执行
|
||||
API = 'API', // 接口调用
|
||||
BATCH = 'API', // 批量执行
|
||||
}
|
||||
|
||||
export enum ExecutionMethodsLabel {
|
||||
SCHEDULE = 'project.taskCenter.scheduledTask', // 定时任务
|
||||
MANUAL = 'project.taskCenter.manualExecution', // 手动执行
|
||||
API = 'project.taskCenter.interfaceCall', // 接口调用
|
||||
BATCH = 'project.taskCenter.batchExecution', // 批量执行
|
||||
}
|
||||
export default {};
|
|
@ -49,6 +49,7 @@ export default {
|
|||
'menu.loadTest': 'Performance Test',
|
||||
'menu.projectManagement.projectPermission': 'Project Permission',
|
||||
'menu.projectManagement.log': 'Log',
|
||||
'menu.projectManagement.taskCenter': 'The task center',
|
||||
'menu.projectManagement.environmentManagement': 'EnvironmentManagement',
|
||||
'menu.settings': 'Settings',
|
||||
'menu.settings.system': 'System',
|
||||
|
|
|
@ -33,6 +33,7 @@ export default {
|
|||
'menu.projectManagement': '项目管理',
|
||||
'menu.projectManagement.templateManager': '模板管理',
|
||||
'menu.projectManagement.log': '日志',
|
||||
'menu.projectManagement.taskCenter': '任务中心',
|
||||
'menu.projectManagement.fileManagement': '文件管理',
|
||||
'menu.projectManagement.messageManagement': '消息管理',
|
||||
'menu.projectManagement.commonScript': '公共脚本',
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// 实时
|
||||
export interface RealTaskCenterApiCaseItem {
|
||||
organizationName: string; // 所属组织
|
||||
projectName: string;
|
||||
projectId: string;
|
||||
id: string;
|
||||
resourceId: string;
|
||||
resourceName: string; // 资源名称 单独报告显示模块名称 集合报告显示报告名称
|
||||
triggerMode: string; // 触发模式(手动,定时,批量,测试计划)
|
||||
poolName: string; // 资源池名称
|
||||
status: string; // 执行状态/SUCCESS/ERROR
|
||||
operationName: string; // 操作人
|
||||
operationTime: string;
|
||||
integrated: boolean; // 是否为集合报告
|
||||
}
|
||||
// 定时任务
|
||||
export interface TimingTaskCenterApiCaseItem {
|
||||
organizationName: string;
|
||||
projectName: string;
|
||||
projectId: string;
|
||||
id: string;
|
||||
taskName: string; // 任务名称
|
||||
resourceId: string; // 资源Id
|
||||
resourceNum: number; // 资源业务id
|
||||
resourceName: string; // 资源名称
|
||||
resourceType: string; // 资源分类
|
||||
value: string;
|
||||
nextTime: string; // 下次执行时间
|
||||
enable: true; // 任务状态
|
||||
createUserName: string;
|
||||
createTime: string;
|
||||
}
|
|
@ -58,7 +58,7 @@ export interface UpdatePluginModel {
|
|||
global?: boolean | string; // 是否选择全部组织
|
||||
description?: string;
|
||||
enable?: boolean;
|
||||
organizationIds?: Array<string | number>; // 指定组织
|
||||
organizationIds?: string[]; // 指定组织
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
|
|||
const appStore = useAppStore();
|
||||
if (!appStore.packageType) {
|
||||
await appStore.initSystemPackage();
|
||||
next();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
next();
|
||||
} else {
|
||||
// 未登录的都直接跳转至登录页,访问的页面地址缓存到 query 上
|
||||
if (to.name === 'login') {
|
||||
|
|
|
@ -289,6 +289,17 @@ const ProjectManagement: AppRouteRecordRaw = {
|
|||
isTopMenu: true,
|
||||
},
|
||||
},
|
||||
// 任务中心
|
||||
{
|
||||
path: 'taskCenter',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TASK_CENTER,
|
||||
component: () => import('@/views/project-management/taskCenter/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.projectManagement.taskCenter',
|
||||
roles: ['*'],
|
||||
isTopMenu: true,
|
||||
},
|
||||
},
|
||||
// 菜单管理-误报规则
|
||||
{
|
||||
path: 'errorReportRule',
|
||||
|
|
|
@ -322,6 +322,17 @@ const Setting: AppRouteRecordRaw = {
|
|||
isTopMenu: true,
|
||||
},
|
||||
},
|
||||
// 任务中心
|
||||
{
|
||||
path: 'taskCenter',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TASK_CENTER,
|
||||
component: () => import('@/views/setting/organization/taskCenter/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.projectManagement.taskCenter',
|
||||
roles: ['*'],
|
||||
isTopMenu: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -5,19 +5,19 @@ import { cloneDeep } from 'lodash-es';
|
|||
|
||||
import type { BreadcrumbItem } from '@/components/business/ms-breadcrumb/types';
|
||||
|
||||
import { getProjectInfo } from '@/api/modules/project-management/basicInfo';
|
||||
import { getProjectList } from '@/api/modules/project-management/project';
|
||||
import { getPageConfig } from '@/api/modules/setting/config';
|
||||
import { getPackageType, getSystemVersion } from '@/api/modules/system';
|
||||
import { getPackageType, getSystemVersion, getUserHasProjectPermission } from '@/api/modules/system';
|
||||
import { getMenuList } from '@/api/modules/user';
|
||||
import defaultSettings from '@/config/settings.json';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { NO_PROJECT_ROUTE_NAME } from '@/router/constants';
|
||||
import { NO_PROJECT_ROUTE_NAME, NO_RESOURCE_ROUTE_NAME, WHITE_LIST } from '@/router/constants';
|
||||
import { watchStyle, watchTheme } from '@/utils/theme';
|
||||
|
||||
import type { PageConfig, PageConfigKeys, Style, Theme } from '@/models/setting/config';
|
||||
import { ProjectListItem } from '@/models/setting/project';
|
||||
|
||||
import useUserStore from '../user';
|
||||
import type { AppState } from './types';
|
||||
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
|
||||
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||
|
@ -64,6 +64,7 @@ const useAppStore = defineStore('app', {
|
|||
},
|
||||
packageType: '',
|
||||
projectList: [] as ProjectListItem[],
|
||||
ordList: [],
|
||||
}),
|
||||
|
||||
getters: {
|
||||
|
@ -206,6 +207,12 @@ const useAppStore = defineStore('app', {
|
|||
setCurrentProjectId(id: string) {
|
||||
this.currentProjectId = id;
|
||||
},
|
||||
/**
|
||||
* 设置当前组织列表
|
||||
*/
|
||||
setOrdList(ordList: { id: string; name: string }[]) {
|
||||
this.ordList = ordList;
|
||||
},
|
||||
/**
|
||||
* 设置当前系统包类型
|
||||
*/
|
||||
|
@ -240,6 +247,32 @@ const useAppStore = defineStore('app', {
|
|||
console.log(error);
|
||||
}
|
||||
},
|
||||
async validateUserProjectPermission() {
|
||||
try {
|
||||
const router = useRouter();
|
||||
const HasProjectPermission = await getUserHasProjectPermission(this.currentProjectId);
|
||||
if (!HasProjectPermission) {
|
||||
// 没有项目权限(用户所在的当前项目被禁用&用户被移除出去该项目)
|
||||
router.push({
|
||||
name: NO_PROJECT_ROUTE_NAME,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const res = await getProjectInfo(this.currentProjectId);
|
||||
if (res.deleted) {
|
||||
// 如果项目被删除或者被禁用,跳转到无项目页面
|
||||
router.push({
|
||||
name: NO_PROJECT_ROUTE_NAME,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 初始化页面配置
|
||||
*/
|
||||
|
|
|
@ -39,6 +39,7 @@ export interface AppState {
|
|||
currentMenuConfig: string[];
|
||||
packageType: string;
|
||||
projectList: ProjectListItem[];
|
||||
ordList: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface UploadFileTaskState {
|
||||
|
|
|
@ -55,10 +55,6 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
|||
console.log(error);
|
||||
}
|
||||
},
|
||||
// 设置是否是编辑或者新增成功状态
|
||||
setIsAlreadySuccess(state: boolean) {
|
||||
this.operatingState = state;
|
||||
},
|
||||
// 设置菜单
|
||||
setTab(list: TabItemType[]) {
|
||||
this.tabSettingList = list;
|
||||
|
@ -85,6 +81,15 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
|||
};
|
||||
});
|
||||
},
|
||||
// 初始化count
|
||||
initCountMap(countMap: Record<string, any>) {
|
||||
this.tabSettingList = this.tabSettingList.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
total: countMap[item.key] || 0,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
try {
|
||||
if (appStore.getCurrentOrgId && hasAnyPermission(['PROJECT_BASE_INFO:READ'])) {
|
||||
const res = await getProjectList(appStore.getCurrentOrgId); // TODO:
|
||||
projectList.value = res;
|
||||
} else {
|
||||
projectList.value = [];
|
||||
}
|
||||
|
|
|
@ -455,7 +455,7 @@
|
|||
color: rgb(var(--danger-6));
|
||||
}
|
||||
}
|
||||
:deep(.active .arco-badge-number) {
|
||||
:deep(.active .arco-badge-text) {
|
||||
background: rgb(var(--primary-5));
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
|
||||
|
@ -63,27 +62,6 @@
|
|||
fileList: [],
|
||||
});
|
||||
|
||||
const initDetail = {
|
||||
id: '',
|
||||
templateId: '',
|
||||
name: '',
|
||||
prerequisite: '', // prerequisite
|
||||
caseEditType: 'STEP', // 编辑模式:步骤模式/文本模式
|
||||
steps: '',
|
||||
textDescription: '',
|
||||
expectedResult: '', // 预期结果
|
||||
description: '',
|
||||
publicCase: false, // 是否公共用例
|
||||
moduleId: '',
|
||||
versionId: '',
|
||||
tags: [],
|
||||
projectId: appStore.currentProjectId,
|
||||
customFields: {}, // 自定义字段集合
|
||||
relateFileMetaIds: [], // 关联文件ID集合
|
||||
deleteFileMetaIds: [], // 删除本地上传的文件id
|
||||
unLinkFilesIds: [], // 取消关联的文件id
|
||||
};
|
||||
|
||||
const title = ref('');
|
||||
const loading = ref(false);
|
||||
const isEdit = computed(() => !!route.query.id);
|
||||
|
@ -99,6 +77,7 @@
|
|||
// 编辑用例
|
||||
if (route.params.mode === 'edit') {
|
||||
await updateCaseRequest(caseDetailInfo.value);
|
||||
featureCaseStore.setModuleId([caseDetailInfo.value.request.moduleId]);
|
||||
Message.success(t('caseManagement.featureCase.editSuccess'));
|
||||
router.push({
|
||||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE,
|
||||
|
@ -118,7 +97,6 @@
|
|||
}
|
||||
createSuccessId.value = res.data.id;
|
||||
Message.success(route.params.mode === 'copy' ? t('ms.description.copySuccess') : t('common.addSuccess'));
|
||||
featureCaseStore.setIsAlreadySuccess(true);
|
||||
isShowTip.value = !getIsVisited();
|
||||
if (isReview) {
|
||||
router.back();
|
||||
|
|
|
@ -91,7 +91,14 @@
|
|||
</template>
|
||||
<template #default>
|
||||
<div ref="wrapperRef" class="wrapperRef bg-white">
|
||||
<MsSplitBox ref="wrapperRef" expand-direction="right" :max="0.7" :min="0.7" :size="900">
|
||||
<MsSplitBox
|
||||
ref="wrapperRef"
|
||||
:class="isFullScreen ? 'h-[100%]' : 'h-[calc(100% - 78px)]'"
|
||||
expand-direction="right"
|
||||
:max="0.7"
|
||||
:min="0.7"
|
||||
:size="900"
|
||||
>
|
||||
<template #first>
|
||||
<div class="leftWrapper">
|
||||
<div class="header h-[50px]">
|
||||
|
@ -103,7 +110,6 @@
|
|||
<a-badge
|
||||
class="ml-1"
|
||||
:class="activeTab === tab.key ? 'active' : ''"
|
||||
:count="1000"
|
||||
:text="getTotal(tab.total)"
|
||||
/> </div
|
||||
></a-menu-item>
|
||||
|
@ -182,17 +188,17 @@
|
|||
</div>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
<inputComment
|
||||
v-model:content="content"
|
||||
v-model:notice-user-ids="noticeUserIds"
|
||||
v-permission="['FUNCTIONAL_CASE:READ+COMMENT']"
|
||||
:is-active="isActive"
|
||||
is-show-avatar
|
||||
is-use-bottom
|
||||
@publish="publishHandler"
|
||||
@cancel="cancelPublish"
|
||||
/>
|
||||
</div>
|
||||
<inputComment
|
||||
v-model:content="content"
|
||||
v-model:notice-user-ids="noticeUserIds"
|
||||
v-permission="['FUNCTIONAL_CASE:READ+COMMENT']"
|
||||
:is-active="isActive"
|
||||
is-show-avatar
|
||||
is-use-bottom
|
||||
@publish="publishHandler"
|
||||
@cancel="cancelPublish"
|
||||
/>
|
||||
</template>
|
||||
</MsDetailDrawer>
|
||||
<SettingDrawer ref="settingDrawerRef" v-model:visible="showSettingDrawer" />
|
||||
|
@ -212,7 +218,7 @@
|
|||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||
import inputComment from '@/components/business/ms-comment/input.vue';
|
||||
import { CommentItem, CommentParams } from '@/components/business/ms-comment/types';
|
||||
import { CommentParams } from '@/components/business/ms-comment/types';
|
||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||
import SettingDrawer from './tabContent/settingDrawer.vue';
|
||||
import TabDefect from './tabContent/tabBug/tabDefect.vue';
|
||||
|
@ -334,9 +340,24 @@
|
|||
const detailInfo = ref<DetailCase>({ ...initDetail });
|
||||
const customFields = ref<CustomAttributes[]>([]);
|
||||
const caseLevels = ref<CaseLevel>('P0');
|
||||
|
||||
// 初始化count
|
||||
function setCount(detail: DetailCase) {
|
||||
const { bugCount, caseCount, caseReviewCount, demandCount, relateEdgeCount, testPlanCount } = detail;
|
||||
const countMap: Record<string, any> = {
|
||||
case: caseCount,
|
||||
dependency: relateEdgeCount,
|
||||
caseReview: caseReviewCount,
|
||||
testPlan: testPlanCount,
|
||||
bug: bugCount,
|
||||
requirement: demandCount,
|
||||
};
|
||||
featureCaseStore.initCountMap(countMap);
|
||||
}
|
||||
function loadedCase(detail: DetailCase) {
|
||||
getCaseTree();
|
||||
detailInfo.value = { ...detail };
|
||||
setCount(detail);
|
||||
customFields.value = detailInfo.value.customFields;
|
||||
caseLevels.value = getCaseLevels(customFields.value) as CaseLevel;
|
||||
}
|
||||
|
@ -476,6 +497,23 @@
|
|||
tabDetailRef.value.handleOK();
|
||||
}
|
||||
|
||||
function getTotal(total: number): string {
|
||||
if (total === 0) {
|
||||
return '0';
|
||||
}
|
||||
if (total && total !== 0) {
|
||||
if (total <= 99) {
|
||||
return String(total);
|
||||
}
|
||||
|
||||
if (total > 99) {
|
||||
return `${total}+`;
|
||||
}
|
||||
}
|
||||
|
||||
return `${total}+`;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => customFields.value,
|
||||
() => {
|
||||
|
@ -544,13 +582,6 @@
|
|||
tabDetailRef.value.handleOK();
|
||||
}, 300);
|
||||
|
||||
function getTotal(total: number) {
|
||||
if (total <= 99) {
|
||||
return String(total);
|
||||
}
|
||||
return `${total}+`;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.detailId,
|
||||
(val) => {
|
||||
|
@ -562,9 +593,6 @@
|
|||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.wrapperRef {
|
||||
height: calc(100% - 78px);
|
||||
}
|
||||
:deep(.arco-menu-light) {
|
||||
height: 50px;
|
||||
background: none !important;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
:row-count="filterRowCount"
|
||||
@keyword-search="fetchData"
|
||||
@adv-search="handleAdvSearch"
|
||||
@reset="fetchData"
|
||||
@refresh="fetchData()"
|
||||
>
|
||||
<template #left>
|
||||
<div class="text-[var(--color-text-1)]"
|
||||
|
@ -524,14 +524,14 @@
|
|||
label: 'caseManagement.featureCase.associatedDemand',
|
||||
eventTag: 'associatedDemand',
|
||||
},
|
||||
{
|
||||
label: 'caseManagement.featureCase.generatingDependencies',
|
||||
eventTag: 'generatingDependencies',
|
||||
},
|
||||
{
|
||||
label: 'caseManagement.featureCase.addToPublic',
|
||||
eventTag: 'addToPublic',
|
||||
},
|
||||
// {
|
||||
// label: 'caseManagement.featureCase.generatingDependencies',
|
||||
// eventTag: 'generatingDependencies',
|
||||
// },
|
||||
// {
|
||||
// label: 'caseManagement.featureCase.addToPublic',
|
||||
// eventTag: 'addToPublic',
|
||||
// },
|
||||
{
|
||||
isDivider: true,
|
||||
},
|
||||
|
@ -703,9 +703,10 @@
|
|||
|
||||
// 获取父组件模块数量
|
||||
function emitTableParams() {
|
||||
const moduleIds = props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds];
|
||||
emit('init', {
|
||||
keyword: keyword.value,
|
||||
moduleIds: [],
|
||||
moduleIds,
|
||||
projectId: currentProjectId.value,
|
||||
current: propsRes.value.msPagination?.current,
|
||||
pageSize: propsRes.value.msPagination?.pageSize,
|
||||
|
@ -727,7 +728,7 @@
|
|||
if (props.activeFolder === 'all') {
|
||||
searchParams.value.moduleIds = [];
|
||||
} else {
|
||||
searchParams.value.moduleIds = [moduleId.value, ...props.offspringIds];
|
||||
searchParams.value.moduleIds = [...featureCaseStore.moduleId, ...props.offspringIds];
|
||||
}
|
||||
setLoadListParams({
|
||||
...searchParams.value,
|
||||
|
@ -838,7 +839,7 @@
|
|||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
condition: { keyword: keyword.value },
|
||||
projectId: currentProjectId.value,
|
||||
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder],
|
||||
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
|
||||
moduleId: selectedModuleKeys.value[0],
|
||||
};
|
||||
if (isMove.value) {
|
||||
|
@ -1029,7 +1030,7 @@
|
|||
...customFieldsColumns,
|
||||
...columns.slice(columns.length - 1, columns.length),
|
||||
];
|
||||
tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_TABLE, fullColumns, 'drawer');
|
||||
await tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_TABLE, fullColumns, 'drawer');
|
||||
tableRef.value?.initColumn(fullColumns);
|
||||
}
|
||||
|
||||
|
|
|
@ -181,6 +181,7 @@
|
|||
asterisk-position="end"
|
||||
:label="t('system.orgTemplate.modules')"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.moduleRuleTip') }]"
|
||||
@change="changeSelectModule"
|
||||
>
|
||||
<a-tree-select
|
||||
v-model="form.moduleId"
|
||||
|
@ -592,6 +593,10 @@
|
|||
},
|
||||
});
|
||||
|
||||
function changeSelectModule(value: string) {
|
||||
featureCaseStore.setModuleId([value]);
|
||||
}
|
||||
|
||||
// 监视文件列表处理关联和本地文件
|
||||
watch(
|
||||
() => fileList.value,
|
||||
|
@ -616,7 +621,6 @@
|
|||
if (val) {
|
||||
params.value.request = { ...form.value };
|
||||
emit('update:formModeValue', params.value);
|
||||
featureCaseStore.setModuleId([form.value.moduleId]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -22,20 +22,26 @@
|
|||
}}<span class="mx-1 font-medium text-[rgb(var(--danger-6))]">{{ validateResultInfo.failCount }}</span
|
||||
>{{ t('caseManagement.featureCase.caseCount') }}</span
|
||||
>
|
||||
<a-popover position="bottom">
|
||||
<a-popover
|
||||
position="bottom"
|
||||
:content-style="{
|
||||
padding: '0px',
|
||||
}"
|
||||
>
|
||||
<span v-if="validateResultInfo.failCount" class="font-medium text-[rgb(var(--primary-5))]">{{
|
||||
t('caseManagement.featureCase.viewErrorDetail')
|
||||
}}</span>
|
||||
<template #title>
|
||||
<div class="w-[440px]"
|
||||
<div class="w-[440px] px-4 pt-4"
|
||||
>{{ t('caseManagement.featureCase.someCaseImportFailed') }}
|
||||
<span class="text-[var(--color-text-4)]">({{ validateResultInfo.failCount }})</span></div
|
||||
<span class="text-[14px] font-medium text-[var(--color-text-4)]"
|
||||
>({{ validateResultInfo.failCount }})</span
|
||||
></div
|
||||
>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="w-[440px]">
|
||||
<a-divider class="mx-0 my-0" />
|
||||
<div class="max-h-[400px] overflow-hidden">
|
||||
<div class="max-h-[400px] overflow-hidden px-4">
|
||||
<div
|
||||
v-for="(item, index) of validateResultInfo.errorMessages"
|
||||
:key="`${item.rowNum}-${index}`"
|
||||
|
@ -44,9 +50,9 @@
|
|||
{{ item.errMsg }}
|
||||
</div>
|
||||
</div>
|
||||
<a-button class="mt-[8px]" type="text" long @click="showMore">{{
|
||||
<div class="moreBtn h-[40px] text-[14px]" type="text" long @click="showMore">{{
|
||||
t('caseManagement.featureCase.ViewMore')
|
||||
}}</a-button>
|
||||
}}</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
|
@ -77,13 +83,11 @@
|
|||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
<MsDrawer
|
||||
v-model:visible="showMoreFailureCase"
|
||||
:title="t('caseManagement.featureCase.cancelValidateSuccess', { number: validateResultInfo.failCount })"
|
||||
:width="960"
|
||||
:footer="false"
|
||||
no-content-padding
|
||||
>
|
||||
<MsDrawer v-model:visible="showMoreFailureCase" :width="960" :footer="false" no-content-padding>
|
||||
<template #title>
|
||||
{{ t('caseManagement.featureCase.importFailedCases')
|
||||
}}<span class="text-[var(--color-text-4)]">({{ validateResultInfo.failCount }})</span>
|
||||
</template>
|
||||
<MsList
|
||||
mode="static"
|
||||
:virtual-list-props="{
|
||||
|
@ -203,4 +207,12 @@
|
|||
background: var(--color-text-input-border);
|
||||
@apply mr-2 mt-2;
|
||||
}
|
||||
.moreBtn {
|
||||
color: rgb(var(--primary-5));
|
||||
box-shadow: 0 -1px 4px rgba(31 35 41/10%);
|
||||
@apply mt-2 flex items-center justify-center;
|
||||
}
|
||||
:deep(.arco-popover-popup-content) {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
</template>
|
||||
</MsAdvanceFilter>
|
||||
<ms-base-table
|
||||
class="mb-4"
|
||||
class="my-4"
|
||||
v-bind="propsRes"
|
||||
:action-config="tableBatchActions"
|
||||
@selected-change="handleTableSelect"
|
||||
|
|
|
@ -136,7 +136,11 @@
|
|||
import ValidateModal from './components/export/validateModal.vue';
|
||||
import ValidateResult from './components/export/validateResult.vue';
|
||||
|
||||
import { createCaseModuleTree, importExcelCase, importExcelChecked } from '@/api/modules/case-management/featureCase';
|
||||
import featureCase, {
|
||||
createCaseModuleTree,
|
||||
importExcelCase,
|
||||
importExcelChecked,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
|
@ -266,16 +270,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
// 设置默认选中状态
|
||||
router.beforeEach((to: any, from: any, next) => {
|
||||
const routeEnumValues = Object.values(CaseManagementRouteEnum);
|
||||
if (!routeEnumValues.includes(to.name)) {
|
||||
// 当前路由不在枚举中,清空仓库的状态值
|
||||
featureCaseStore.setIsAlreadySuccess(false);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
const showExcelModal = ref<boolean>(false);
|
||||
const validateType = ref<'Excel' | 'Xmind'>('Excel');
|
||||
|
||||
|
@ -389,12 +383,6 @@
|
|||
importLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (featureCaseStore.operatingState) {
|
||||
[activeFolder.value] = featureCaseStore.moduleId;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -217,7 +217,7 @@ export default {
|
|||
'Check, the original use case will be overwritten when the ID is the same',
|
||||
'caseManagement.featureCase.notSelectedRecoverCase': 'If the ID already exists, the use case is skipped',
|
||||
'caseManagement.featureCase.cancelValidate': 'Cancel the check',
|
||||
'caseManagement.featureCase.cancelValidateSuccess': 'Cancel check successfully',
|
||||
'caseManagement.featureCase.importFailedCases': 'Import failed use cases {number}',
|
||||
'caseManagement.featureCase.importFailedCountTitle': 'Import failure use case ({number})',
|
||||
'caseManagement.featureCase.beforeUploadTip':
|
||||
'Before uploading, please click edit content {type} in the template format',
|
||||
|
|
|
@ -213,7 +213,7 @@ export default {
|
|||
'caseManagement.featureCase.selectedRecoverCase': '勾选,ID相同时覆盖原用例',
|
||||
'caseManagement.featureCase.notSelectedRecoverCase': '不勾选,ID已存在时,跳过该用例',
|
||||
'caseManagement.featureCase.cancelValidate': '取消校验',
|
||||
'caseManagement.featureCase.cancelValidateSuccess': '取消校验成功',
|
||||
'caseManagement.featureCase.importFailedCases': '导入失败用例 {number}',
|
||||
'caseManagement.featureCase.importFailedCountTitle': '导入失败用例({number})',
|
||||
'caseManagement.featureCase.beforeUploadTip': '上传前请先按 { type } 模板中的格式编辑内容',
|
||||
'caseManagement.featureCase.downloadTemplate': '下载 {type} 模板',
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
<template>
|
||||
<div class="px-[16px]">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<span>{{ t('project.taskCenter.apiCaseList', { type: props.name }) }}</span>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
@search="searchList"
|
||||
@press-enter="searchList"
|
||||
></a-input-search>
|
||||
</div>
|
||||
|
||||
<ms-base-table
|
||||
v-bind="propsRes"
|
||||
ref="tableRef"
|
||||
:action-config="tableBatchActions"
|
||||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #statusFilter="{ columnConfig }">
|
||||
<a-trigger
|
||||
v-model:popup-visible="statusFilterVisible"
|
||||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="statusFilterVisible = true">
|
||||
<div class="font-medium">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
</div>
|
||||
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="flex items-center justify-center px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="statusListFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of statusFilters" :key="key" :value="key">
|
||||
<ExecutionStatus :module-type="props.moduleType" :status="key" />
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<ExecutionStatus :module-type="props.moduleType" :status="record.status" />
|
||||
</template>
|
||||
<template #triggerMode="{ record }">
|
||||
<span>{{ t(ExecutionMethodsLabel[record.triggerMode]) }}</span>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsButton class="!mr-0" @click="stop(record)">{{ t('project.taskCenter.stop') }}</MsButton>
|
||||
<a-divider direction="vertical" />
|
||||
<MsButton class="!mr-0" @click="execution(record)">{{ t('project.taskCenter.execution') }}</MsButton>
|
||||
<MsButton class="!mr-0">{{ t('project.taskCenter.viewReport') }}</MsButton>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import ExecutionStatus from './executionStatus.vue';
|
||||
|
||||
import {
|
||||
getRealOrdApiCaseList,
|
||||
getRealProApiCaseList,
|
||||
getRealSysApiCaseList,
|
||||
} from '@/api/modules/project-management/taskCenter';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
|
||||
import { ExecutionMethodsLabel, TaskCenterEnum } from '@/enums/taskCenter';
|
||||
|
||||
import { TaskStatus } from './utils';
|
||||
|
||||
const { openModal } = useModal();
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
group: 'system' | 'organization' | 'project';
|
||||
moduleType: keyof typeof TaskCenterEnum;
|
||||
name: string;
|
||||
}>();
|
||||
|
||||
const keyword = ref<string>('');
|
||||
const statusFilterVisible = ref(false);
|
||||
const statusListFilters = ref<string[]>(Object.keys(TaskStatus[props.moduleType]));
|
||||
|
||||
const filterOptions = computed(() => {
|
||||
return statusListFilters.value.map((item) => {
|
||||
return {
|
||||
label: item,
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const loadRealMap = ref({
|
||||
system: getRealSysApiCaseList,
|
||||
organization: getRealOrdApiCaseList,
|
||||
project: getRealProApiCaseList,
|
||||
});
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.taskCenter.resourceID',
|
||||
dataIndex: 'resourceId',
|
||||
slotName: 'resourceId',
|
||||
width: 300,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.resourceName',
|
||||
slotName: 'resourceName',
|
||||
dataIndex: 'resourceName',
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.executionResult',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
titleSlotName: 'statusFilter',
|
||||
// filterConfig: {
|
||||
// filterSlotName: 'status', // 筛选组件的slotName
|
||||
// multiple: true, // 是否多选
|
||||
// options: filterOptions.value,
|
||||
// },
|
||||
showInTable: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.executionMode',
|
||||
dataIndex: 'triggerMode',
|
||||
slotName: 'triggerMode',
|
||||
showInTable: true,
|
||||
isTag: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.resourcePool',
|
||||
slotName: 'poolName',
|
||||
dataIndex: 'poolName',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.operator',
|
||||
slotName: 'operationName',
|
||||
dataIndex: 'operationName',
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.operating',
|
||||
dataIndex: 'operationTime',
|
||||
slotName: 'operationTime',
|
||||
width: 180,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'common.operation',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setProps } = useTable(
|
||||
loadRealMap.value[props.group],
|
||||
{
|
||||
columns,
|
||||
scroll: {
|
||||
x: '100%',
|
||||
},
|
||||
showSetting: false,
|
||||
selectable: true,
|
||||
heightUsed: 300,
|
||||
enableDrag: true,
|
||||
showSelectAll: true,
|
||||
}
|
||||
);
|
||||
|
||||
function initData() {
|
||||
setLoadListParams({
|
||||
keyword: keyword.value,
|
||||
moduleType: props.moduleType,
|
||||
});
|
||||
loadList();
|
||||
}
|
||||
|
||||
const tableBatchActions = {
|
||||
baseAction: [
|
||||
{
|
||||
label: 'project.taskCenter.batchStop',
|
||||
eventTag: 'batchStop',
|
||||
},
|
||||
{
|
||||
label: 'project.taskCenter.batchExecution',
|
||||
eventTag: 'batchExecution',
|
||||
},
|
||||
],
|
||||
};
|
||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {}
|
||||
|
||||
function searchList() {
|
||||
resetSelector();
|
||||
initData();
|
||||
}
|
||||
|
||||
function stop(record: any) {
|
||||
openModal({
|
||||
type: 'warning',
|
||||
title: t('project.taskCenter.batchStopTask', { num: 3 }),
|
||||
content: t('project.taskCenter.stopTaskContent'),
|
||||
okText: t('project.taskCenter.confirmStop'),
|
||||
cancelText: t('common.cancel'),
|
||||
okButtonProps: {
|
||||
status: 'danger',
|
||||
},
|
||||
onBeforeOk: async () => {
|
||||
try {
|
||||
resetSelector();
|
||||
Message.success(t('project.taskCenter.stopSuccess'));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
hideCancel: false,
|
||||
});
|
||||
}
|
||||
function execution(record: any) {}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initData();
|
||||
});
|
||||
|
||||
const statusFilters = computed(() => {
|
||||
return Object.keys(TaskStatus[props.moduleType]);
|
||||
});
|
||||
function handleFilterHidden(val: boolean) {
|
||||
if (!val) {
|
||||
initData();
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.moduleType,
|
||||
(val) => {
|
||||
if (val) {
|
||||
resetSelector();
|
||||
initData();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,178 @@
|
|||
<template>
|
||||
<div class="flex items-center justify-start">
|
||||
<MsIcon :type="getExecutionResult().icon" :class="getExecutionResult()?.color" size="14" />
|
||||
<span class="ml-1">{{ t(getExecutionResult().label) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TaskCenterEnum } from '@/enums/taskCenter';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
status: string;
|
||||
moduleType: keyof typeof TaskCenterEnum;
|
||||
}>();
|
||||
|
||||
export interface IconType {
|
||||
icon: string;
|
||||
label: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
const iconTypeStatus = ref({
|
||||
[TaskCenterEnum.API_CASE]: {
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
FAKE_ERROR: {
|
||||
icon: 'icon-icon_warning_colorful',
|
||||
label: 'project.taskCenter.falseAlarm',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: '!text-[var(--color-text-input-border)]',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RERUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.rerun',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
PENDING: {
|
||||
icon: 'icon-icon_wait',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.API_SCENARIO]: {
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
FAKE_ERROR: {
|
||||
icon: 'icon-icon_warning_colorful',
|
||||
label: 'project.taskCenter.falseAlarm',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: '!text-[var(--color-text-input-border)]',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RERUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.rerun',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
PENDING: {
|
||||
icon: 'icon-icon_wait',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.LOAD_TEST]: {
|
||||
STARTING: {
|
||||
icon: 'icon-icon_restarting',
|
||||
label: 'project.taskCenter.starting',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
COMPLETED: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.complete',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: '!text-[var(--color-text-input-border)]',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.UI_TEST]: {
|
||||
PENDING: {
|
||||
icon: 'icon-icon_wait',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RERUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.rerun',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: '!text-[var(--color-text-input-border)]',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.TEST_PLAN]: {
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
STARTING: {
|
||||
icon: 'icon-icon_restarting',
|
||||
label: 'project.taskCenter.starting',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function getExecutionResult(): IconType {
|
||||
return iconTypeStatus.value[props.moduleType][props.status];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<div class="p-4 pt-0">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<!-- TODO这个版本不上 -->
|
||||
<a-button type="primary">
|
||||
{{ t('project.taskCenter.createTask') }}
|
||||
</a-button>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
@search="searchList"
|
||||
@press-enter="searchList"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<ms-base-table
|
||||
v-bind="propsRes"
|
||||
ref="tableRef"
|
||||
:action-config="tableBatchActions"
|
||||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #resourceNum="{ record }">
|
||||
<a-button type="text" class="flex w-full">{{ record.resourceNum }}</a-button>
|
||||
</template>
|
||||
<template #resourceName="{ record }">
|
||||
<a-button type="text" class="flex w-full">{{ record.resourceName }}</a-button>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<a-switch v-model="record.enable" size="small" type="line" />
|
||||
<!-- TODO这一版不上 -->
|
||||
<!-- <a-divider direction="vertical" />
|
||||
<MsButton class="!mr-0" @click="edit(record)">{{ t('common.edit') }}</MsButton>
|
||||
<a-divider direction="vertical" />
|
||||
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect" /> -->
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
|
||||
import {
|
||||
getScheduleOrgApiCaseList,
|
||||
getScheduleProApiCaseList,
|
||||
getScheduleSysApiCaseList,
|
||||
} from '@/api/modules/project-management/taskCenter';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TaskCenterEnum } from '@/enums/taskCenter';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
group: 'system' | 'organization' | 'project';
|
||||
moduleType: keyof typeof TaskCenterEnum;
|
||||
name: string;
|
||||
}>();
|
||||
|
||||
const keyword = ref<string>('');
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.taskCenter.resourceID',
|
||||
dataIndex: 'resourceNum',
|
||||
slotName: 'resourceNum',
|
||||
width: 300,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.resourceName',
|
||||
slotName: 'resourceName',
|
||||
dataIndex: 'resourceName',
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.resourceClassification',
|
||||
dataIndex: 'resourceType',
|
||||
slotName: 'resourceType',
|
||||
showInTable: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.operationRule',
|
||||
dataIndex: 'value',
|
||||
slotName: 'value',
|
||||
showInTable: true,
|
||||
isTag: true,
|
||||
width: 150,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.operator',
|
||||
slotName: 'createUserName',
|
||||
dataIndex: 'createUserName',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.operating',
|
||||
slotName: 'createTime',
|
||||
dataIndex: 'createTime',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.taskCenter.nextExecutionTime',
|
||||
slotName: 'nextTime',
|
||||
dataIndex: 'nextTime',
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'common.operation',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
const loadRealMap = ref({
|
||||
system: getScheduleSysApiCaseList,
|
||||
organization: getScheduleOrgApiCaseList,
|
||||
project: getScheduleProApiCaseList,
|
||||
});
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setProps } = useTable(
|
||||
loadRealMap.value[props.group],
|
||||
{
|
||||
columns,
|
||||
scroll: {
|
||||
x: '100%',
|
||||
},
|
||||
showSetting: false,
|
||||
selectable: true,
|
||||
heightUsed: 300,
|
||||
enableDrag: true,
|
||||
showSelectAll: true,
|
||||
}
|
||||
);
|
||||
|
||||
function initData() {
|
||||
setLoadListParams({
|
||||
keyword: keyword.value,
|
||||
moduleType: props.moduleType,
|
||||
});
|
||||
loadList();
|
||||
}
|
||||
|
||||
function searchList() {
|
||||
resetSelector();
|
||||
initData();
|
||||
}
|
||||
|
||||
const tableBatchActions = {
|
||||
baseAction: [
|
||||
{
|
||||
label: 'project.taskCenter.batchStop',
|
||||
eventTag: 'batchStop',
|
||||
},
|
||||
{
|
||||
label: 'project.taskCenter.batchExecution',
|
||||
eventTag: 'batchExecution',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {}
|
||||
|
||||
function edit(record: any) {}
|
||||
|
||||
const moreActions: ActionsItem[] = [
|
||||
{
|
||||
label: 'common.delete',
|
||||
danger: true,
|
||||
eventTag: 'delete',
|
||||
},
|
||||
];
|
||||
|
||||
function handleMoreActionSelect(item: ActionsItem) {}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initData();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.moduleType,
|
||||
(val) => {
|
||||
if (val) {
|
||||
resetSelector();
|
||||
initData();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,131 @@
|
|||
<template>
|
||||
<div class="box h-full">
|
||||
<div class="left" :class="getStyleClass()">
|
||||
<div class="item" :class="[activeTask === 'real' ? 'active' : '']" @click="toggleTask('real')">
|
||||
{{ t('project.taskCenter.realTimeTask') }}
|
||||
</div>
|
||||
<div class="item" :class="[activeTask === 'timing' ? 'active' : '']" @click="toggleTask('timing')">
|
||||
{{ t('project.taskCenter.scheduledTask') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a-tabs v-model:active-key="activeTab" class="no-content">
|
||||
<a-tab-pane v-for="item of rightTabList" :key="item.value" :title="item.label" />
|
||||
</a-tabs>
|
||||
<a-divider margin="0" class="!mb-[16px]"></a-divider>
|
||||
<!-- 接口用例列表-->
|
||||
<ApiCase v-if="activeTask === 'real'" :name="listName" :module-type="activeTab" :group="props.group" />
|
||||
<ScheduledTask v-if="activeTask === 'timing'" :name="listName" :group="props.group" :module-type="activeTab" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import ApiCase from './apiCase.vue';
|
||||
import ScheduledTask from './scheduledTask.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TaskCenterEnum } from '@/enums/taskCenter';
|
||||
|
||||
const { t } = useI18n();
|
||||
const activeTab = ref<keyof typeof TaskCenterEnum>(TaskCenterEnum.API_CASE);
|
||||
|
||||
const props = defineProps<{
|
||||
group: 'system' | 'organization' | 'project';
|
||||
mode?: 'modal' | 'normal';
|
||||
}>();
|
||||
|
||||
const realTabList = ref([
|
||||
{
|
||||
value: TaskCenterEnum.API_CASE,
|
||||
label: t('project.taskCenter.interfaceCase'),
|
||||
},
|
||||
{
|
||||
value: TaskCenterEnum.API_SCENARIO,
|
||||
label: t('project.taskCenter.apiScenario'),
|
||||
},
|
||||
{
|
||||
value: TaskCenterEnum.UI_TEST,
|
||||
label: t('project.taskCenter.uiDefaultFile'),
|
||||
},
|
||||
{
|
||||
value: TaskCenterEnum.LOAD_TEST,
|
||||
label: t('project.taskCenter.performanceTest'),
|
||||
},
|
||||
{
|
||||
value: TaskCenterEnum.TEST_PLAN,
|
||||
label: t('project.taskCenter.testPlan'),
|
||||
},
|
||||
]);
|
||||
|
||||
const timingTabList = ref([
|
||||
{
|
||||
value: TaskCenterEnum.TEST_RESOURCE,
|
||||
label: t('project.taskCenter.testResource'),
|
||||
},
|
||||
{
|
||||
value: TaskCenterEnum.API_IMPORT,
|
||||
label: t('project.taskCenter.apiImport'),
|
||||
},
|
||||
]);
|
||||
|
||||
const activeTask = ref('real');
|
||||
|
||||
const rightTabList = computed(() => {
|
||||
return activeTask.value === 'real' ? realTabList.value : timingTabList.value;
|
||||
});
|
||||
|
||||
function toggleTask(activeType: string) {
|
||||
activeTask.value = activeType;
|
||||
if (activeTask.value === 'real') {
|
||||
activeTab.value = TaskCenterEnum.API_CASE;
|
||||
} else {
|
||||
activeTab.value = TaskCenterEnum.TEST_RESOURCE;
|
||||
}
|
||||
}
|
||||
|
||||
function getStyleClass() {
|
||||
return props.mode === 'modal' ? ['p-0', 'pt-[24px]', 'pr-[24px]'] : ['p-[24px]'];
|
||||
}
|
||||
|
||||
const listName = computed(() => {
|
||||
return rightTabList.value.find((item) => item.value === activeTab.value)?.label || '';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.box {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.left {
|
||||
width: 252px;
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--color-text-n8);
|
||||
.item {
|
||||
padding: 0 20px;
|
||||
height: 38px;
|
||||
font-size: 14px;
|
||||
line-height: 38px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
color: rgb(var(--primary-5));
|
||||
background: rgb(var(--primary-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: calc(100% - 300px);
|
||||
flex-grow: 1; /* 自适应 */
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.no-content {
|
||||
:deep(.arco-tabs-content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,149 @@
|
|||
import { TaskCenterEnum } from '@/enums/taskCenter';
|
||||
|
||||
export const TaskStatus = {
|
||||
[TaskCenterEnum.API_CASE]: {
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
FAKE_ERROR: {
|
||||
icon: 'icon-icon_warning_colorful',
|
||||
label: 'project.taskCenter.falseAlarm',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: '!var(--color-text-input-border)',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RERUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.rerun',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
PENDING: {
|
||||
icon: 'icon-icon_wait',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.API_SCENARIO]: {
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
FAKE_ERROR: {
|
||||
icon: 'icon-icon_warning_colorful',
|
||||
label: 'project.taskCenter.falseAlarm',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: 'var(--color-text-input-border)',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RERUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.rerun',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
PENDING: {
|
||||
icon: 'icon-icon_wait',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.LOAD_TEST]: {
|
||||
STARTING: {
|
||||
icon: 'icon-icon_restarting',
|
||||
label: 'project.taskCenter.starting',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
COMPLETED: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.complete',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: 'var(--color-text-input-border)',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.UI_TEST]: {
|
||||
PENDING: {
|
||||
icon: 'icon-icon_wait',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.inExecution',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
RERUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.rerun',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
ERROR: {
|
||||
icon: 'icon-icon_close_colorful',
|
||||
label: 'project.taskCenter.failure',
|
||||
},
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
STOPPED: {
|
||||
icon: 'icon-icon_block_filled',
|
||||
label: 'project.taskCenter.stop',
|
||||
color: 'var(--color-text-input-border)',
|
||||
},
|
||||
},
|
||||
[TaskCenterEnum.TEST_PLAN]: {
|
||||
RUNNING: {
|
||||
icon: 'icon-icon_testing',
|
||||
label: 'project.taskCenter.queuing',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
SUCCESS: {
|
||||
icon: 'icon-icon_succeed_colorful',
|
||||
label: 'project.taskCenter.successful',
|
||||
},
|
||||
STARTING: {
|
||||
icon: 'icon-icon_restarting',
|
||||
label: 'project.taskCenter.starting',
|
||||
color: '!text-[rgb(var(--link-6))]',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {};
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<MsCard simple no-content-padding>
|
||||
<TaskCenter group="project" />
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import TaskCenter from './component/taskCom.vue';
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.box {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.left {
|
||||
padding: 24px;
|
||||
width: 252px;
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--color-text-n8);
|
||||
.item {
|
||||
padding: 0 20px;
|
||||
height: 38px;
|
||||
font-size: 14px;
|
||||
line-height: 38px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
background: rgb(var(--primary-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: calc(100% - 300px);
|
||||
flex-grow: 1; /* 自适应 */
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.no-content {
|
||||
:deep(.arco-tabs-content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,43 @@
|
|||
export default {
|
||||
'project.taskCenter.interfaceCase': 'Interface use cases',
|
||||
'project.taskCenter.apiScenario': 'Interface scenario',
|
||||
'project.taskCenter.uiDefaultFile': 'UI testing',
|
||||
'project.taskCenter.performanceTest': 'Performance test',
|
||||
'project.taskCenter.testPlan': 'Test plan',
|
||||
'project.taskCenter.resourceID': 'Resource ID',
|
||||
'project.taskCenter.resourceName': 'resourceName',
|
||||
'project.taskCenter.executionResult': 'Execution result',
|
||||
'project.taskCenter.executionMode': 'Execution mode',
|
||||
'project.taskCenter.resourcePool': 'Resource pool',
|
||||
'project.taskCenter.operator': 'operator',
|
||||
'project.taskCenter.operating': 'Operating time',
|
||||
'project.taskCenter.batchStop': 'Batch stop',
|
||||
'project.taskCenter.batchExecution': 'Batch execution',
|
||||
'project.taskCenter.stop': 'stop',
|
||||
'project.taskCenter.execution': 'execution',
|
||||
'project.taskCenter.viewReport': 'View report',
|
||||
'project.taskCenter.batchStopTask': 'Are you sure to stop {num} tasks?',
|
||||
'project.taskCenter.stopTask': 'Are you sure to stop {name} task?',
|
||||
'project.taskCenter.stopTaskContent': 'After stopping,{name} please operate with caution?',
|
||||
'project.taskCenter.confirmStop': 'Confirm stop',
|
||||
'project.taskCenter.stopSuccess': 'Stop successfully',
|
||||
'project.taskCenter.testResource': 'Test resource',
|
||||
'project.taskCenter.apiImport': 'API import',
|
||||
'project.taskCenter.resourceClassification': 'Resource Classification',
|
||||
'project.taskCenter.operationRule': 'Operation rule',
|
||||
'project.taskCenter.nextExecutionTime': 'Next execution time',
|
||||
'project.taskCenter.successful': 'Successful',
|
||||
'project.taskCenter.failure': 'failure',
|
||||
'project.taskCenter.falseAlarm': 'False alarm',
|
||||
'project.taskCenter.inExecution': 'In execution',
|
||||
'project.taskCenter.rerun': 'Rerun',
|
||||
'project.taskCenter.queuing': 'Be queuing',
|
||||
'project.taskCenter.starting': 'Be starting',
|
||||
'project.taskCenter.complete': 'complete',
|
||||
'project.taskCenter.scheduledTask': 'Scheduled task',
|
||||
'project.taskCenter.manualExecution': 'Manual execution',
|
||||
'project.taskCenter.interfaceCall': 'Interface call',
|
||||
'project.taskCenter.realTimeTask': 'Real Time Task',
|
||||
'project.taskCenter.createTask': 'create',
|
||||
'project.taskCenter.apiCaseList': '{type} list',
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
export default {
|
||||
'project.taskCenter.interfaceCase': '接口用例',
|
||||
'project.taskCenter.apiScenario': '接口场景',
|
||||
'project.taskCenter.uiDefaultFile': 'UI 测试',
|
||||
'project.taskCenter.performanceTest': '性能测试',
|
||||
'project.taskCenter.testPlan': '测试计划',
|
||||
'project.taskCenter.resourceID': '资源 ID',
|
||||
'project.taskCenter.resourceName': '资源名称',
|
||||
'project.taskCenter.executionResult': '执行结果',
|
||||
'project.taskCenter.executionMode': '执行方式',
|
||||
'project.taskCenter.resourcePool': '资源池',
|
||||
'project.taskCenter.operator': '操作人',
|
||||
'project.taskCenter.operating': '操作时间',
|
||||
'project.taskCenter.batchStop': '批量停止',
|
||||
'project.taskCenter.batchExecution': '批量执行',
|
||||
'project.taskCenter.stop': '停止',
|
||||
'project.taskCenter.execution': '执行',
|
||||
'project.taskCenter.viewReport': '查看报告',
|
||||
'project.taskCenter.batchStopTask': '确定停止 {num} 个任务吗?',
|
||||
'project.taskCenter.stopTask': '确定停止 {name} 个任务吗?',
|
||||
'project.taskCenter.stopTaskContent': '停止后,{ name }请谨慎操作?',
|
||||
'project.taskCenter.confirmStop': '确认停止',
|
||||
'project.taskCenter.stopSuccess': '停止成功',
|
||||
'project.taskCenter.testResource': '测试资源',
|
||||
'project.taskCenter.apiImport': 'API 导入',
|
||||
'project.taskCenter.resourceClassification': '资源分类',
|
||||
'project.taskCenter.operationRule': '运行规则',
|
||||
'project.taskCenter.nextExecutionTime': '下次执行时间',
|
||||
'project.taskCenter.successful': '成功',
|
||||
'project.taskCenter.failure': '失败',
|
||||
'project.taskCenter.falseAlarm': '误报',
|
||||
'project.taskCenter.inExecution': '执行中',
|
||||
'project.taskCenter.rerun': '重跑中',
|
||||
'project.taskCenter.queuing': '排队中',
|
||||
'project.taskCenter.starting': '启动中',
|
||||
'project.taskCenter.complete': '完成',
|
||||
'project.taskCenter.scheduledTask': '定时任务',
|
||||
'project.taskCenter.manualExecution': '手动执行',
|
||||
'project.taskCenter.interfaceCall': '接口调用',
|
||||
'project.taskCenter.realTimeTask': '实时任务',
|
||||
'project.taskCenter.createTask': '创建定时任务',
|
||||
'project.taskCenter.apiCaseList': '{type}列表',
|
||||
};
|
|
@ -2,8 +2,9 @@
|
|||
<MsCard has-breadcrumb simple>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<span v-if="isEnableOrdTemplate" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
|
||||
<!--TODO 这个版本不允许修改默认模版也不允许创建用例模版 -->
|
||||
<a-button
|
||||
v-else
|
||||
v-if="!isEnableOrdTemplate && route.query.type === 'BUG'"
|
||||
v-permission="['PROJECT_TEMPLATE:READ+ADD']"
|
||||
type="primary"
|
||||
:disabled="false"
|
||||
|
@ -22,12 +23,20 @@
|
|||
</div>
|
||||
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
||||
<template #defaultTemplate="{ record }">
|
||||
<a-switch
|
||||
<!-- <a-switch
|
||||
v-model="record.enableDefault"
|
||||
:disabled="record.enableDefault || isEnableOrdTemplate"
|
||||
size="small"
|
||||
type="line"
|
||||
@change="(value) => changeDefault(value, record)"
|
||||
/> -->
|
||||
<!-- TODO 这个版本不允许修改默认模版也不允许创建用例模版 -->
|
||||
<a-switch
|
||||
v-model="record.enableDefault"
|
||||
:disabled="true"
|
||||
size="small"
|
||||
type="line"
|
||||
@change="(value) => changeDefault(value, record)"
|
||||
/>
|
||||
</template>
|
||||
<template #enableThirdPart="{ record }">
|
||||
|
|
|
@ -166,18 +166,25 @@
|
|||
// 测试连接是否通过
|
||||
const testLink = async () => {
|
||||
testLoading.value = true;
|
||||
try {
|
||||
const formValue = {
|
||||
...fApi.value.formData(),
|
||||
};
|
||||
await postValidate(formValue, pluginId.value);
|
||||
if (!isConfigOrigin.value) isDisabled.value = false;
|
||||
Message.success(t('organization.service.successMessage'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
testLoading.value = false;
|
||||
}
|
||||
fApi.value?.validate(async (valid: any, fail: any) => {
|
||||
if (valid === true) {
|
||||
try {
|
||||
const formValue = {
|
||||
...fApi.value.formData(),
|
||||
};
|
||||
await postValidate(formValue, pluginId.value);
|
||||
if (!isConfigOrigin.value) isDisabled.value = false;
|
||||
Message.success(t('organization.service.successMessage'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
testLoading.value = false;
|
||||
}
|
||||
} else {
|
||||
console.log(fail);
|
||||
testLoading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 创建&编辑
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
v-model="keyword"
|
||||
:placeholder="t('organization.service.searchService')"
|
||||
:max-length="255"
|
||||
class="w-[240px]"
|
||||
allow-clear
|
||||
@search="searchHandler"
|
||||
@press-enter="searchHandler"
|
||||
@clear="searchHandler"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -179,7 +181,8 @@
|
|||
};
|
||||
|
||||
const searchHandler = () => {
|
||||
filterList.value = data.value.filter((item) => item.title?.includes(keyword.value));
|
||||
const keywordLower = keyword.value.toLowerCase();
|
||||
filterList.value = data.value.filter((item: any) => item.title.toLowerCase().includes(keywordLower));
|
||||
};
|
||||
|
||||
const serviceVisible = ref<boolean>(false);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<MsCard simple no-content-padding>
|
||||
<TaskCenter group="organization" />
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import TaskCenter from '@/views/project-management/taskCenter/component/taskCom.vue';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -36,7 +36,12 @@
|
|||
}"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item field="type" :label="t('system.orgTemplate.fieldType')" asterisk-position="end" :required="true">
|
||||
<a-form-item
|
||||
field="type"
|
||||
:label="t('system.orgTemplate.fieldType')"
|
||||
asterisk-position="end"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.typeEmptyTip') }]"
|
||||
>
|
||||
<a-select
|
||||
v-model="fieldForm.type"
|
||||
class="w-[260px]"
|
||||
|
|
|
@ -37,15 +37,17 @@
|
|||
'cursor-pointer': props.mode === 'project',
|
||||
}"
|
||||
/>
|
||||
<span
|
||||
class="ml-2"
|
||||
:class="{
|
||||
'text-[rgb(var(--primary-5))]': props.mode === 'project',
|
||||
'cursor-pointer': props.mode === 'project',
|
||||
}"
|
||||
@click="showDetail(record)"
|
||||
>{{ record.name }}</span
|
||||
>
|
||||
<a-tooltip :content="record.name">
|
||||
<div
|
||||
class="ellipsis ml-2 max-w-[200px]"
|
||||
:class="{
|
||||
'text-[rgb(var(--primary-5))]': props.mode === 'project',
|
||||
'cursor-pointer': props.mode === 'project',
|
||||
}"
|
||||
@click="showDetail(record)"
|
||||
>{{ record.name }}</div
|
||||
>
|
||||
</a-tooltip>
|
||||
<MsTag v-if="record.internal" size="small" class="ml-2">{{ t('system.orgTemplate.isSystem') }}</MsTag></div
|
||||
>
|
||||
</template>
|
||||
|
@ -58,7 +60,7 @@
|
|||
:ok-text="t('system.orgTemplate.confirm')"
|
||||
@confirm="handleOk(record)"
|
||||
>
|
||||
<MsButton v-permission="props.updatePermission" class="!mr-0">{{
|
||||
<MsButton v-permission="props.updatePermission" :disabled="record.internal" class="!mr-0">{{
|
||||
t('system.orgTemplate.edit')
|
||||
}}</MsButton></MsPopConfirm
|
||||
>
|
||||
|
@ -82,7 +84,7 @@
|
|||
v-model:visible="showDetailVisible"
|
||||
:width="480"
|
||||
:footer="false"
|
||||
:title="t('system.orgTemplate.filedDetail', { name: detailInfo?.name })"
|
||||
:title="t('system.orgTemplate.filedDetail', { name: characterLimit(detailInfo?.name) })"
|
||||
>
|
||||
<div class="p-4">
|
||||
<div class="flex">
|
||||
|
@ -197,7 +199,6 @@
|
|||
dataIndex: 'name',
|
||||
width: 300,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.columnFieldType',
|
||||
|
@ -208,7 +209,9 @@
|
|||
{
|
||||
title: 'system.orgTemplate.columnFieldDescription',
|
||||
dataIndex: 'remark',
|
||||
width: 300,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.columnFieldUpdatedTime',
|
||||
|
@ -384,4 +387,9 @@
|
|||
width: 70%;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -330,28 +330,6 @@
|
|||
isPreview.value = !isPreview.value;
|
||||
}
|
||||
|
||||
// 计算当前级别title
|
||||
const breadTitle = computed(() => {
|
||||
const firstBreadTitle = getCardList('organization').find((item) => item.key === route.query.type)?.name;
|
||||
const ThirdBreadTitle = title.value;
|
||||
return {
|
||||
firstBreadTitle,
|
||||
ThirdBreadTitle,
|
||||
};
|
||||
});
|
||||
|
||||
// 更新面包屑标题
|
||||
const setBreadText = () => {
|
||||
const { breadcrumbList } = appStore;
|
||||
const { firstBreadTitle, ThirdBreadTitle } = breadTitle.value;
|
||||
if (firstBreadTitle) {
|
||||
breadcrumbList[0].locale = firstBreadTitle;
|
||||
if (appStore.breadcrumbList.length > 2) {
|
||||
breadcrumbList[2].locale = ThirdBreadTitle;
|
||||
}
|
||||
appStore.setBreadcrumbList(breadcrumbList);
|
||||
}
|
||||
};
|
||||
// 字段表编辑更新表
|
||||
const updateHandler = (flag: boolean) => {
|
||||
isEditField.value = flag;
|
||||
|
@ -373,7 +351,6 @@
|
|||
);
|
||||
|
||||
onMounted(() => {
|
||||
// setBreadText();
|
||||
getClassifyField();
|
||||
if (!isEdit.value) {
|
||||
selectData.value = totalTemplateField.value.filter((item) => item.internal);
|
||||
|
|
|
@ -34,6 +34,50 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal
|
||||
v-model:visible="showEnableVisible"
|
||||
class="ms-modal-form ms-modal-small"
|
||||
unmount-on-close
|
||||
title-align="start"
|
||||
:mask="true"
|
||||
:mask-closable="false"
|
||||
@close="cancelHandler"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<icon-close-circle-fill class="mr-2 text-[20px] text-[rgb(var(--danger-6))]" />
|
||||
<span>{{ t('system.orgTemplate.enableTip') }}</span></div
|
||||
>
|
||||
</template>
|
||||
|
||||
<span class="text-[rgb(var(--warning-6))]">{{ t('system.orgTemplate.enableWarningTip') }}</span>
|
||||
<a-input
|
||||
v-model="validateKeyWord"
|
||||
:placeholder="t('personal.searchOrgPlaceholder')"
|
||||
allow-clear
|
||||
class="mb-4 mt-[8px]"
|
||||
:max-length="255"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<a-button type="secondary" @click="cancelHandler">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<slot name="self-button"></slot>
|
||||
<a-button
|
||||
class="ml-3"
|
||||
type="primary"
|
||||
:loading="confirmLoading"
|
||||
:disabled="!validateKeyWord.trim().length"
|
||||
status="danger"
|
||||
@click="okHandler"
|
||||
>
|
||||
{{ t('common.confirmEnable') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -84,18 +128,40 @@
|
|||
danger: true,
|
||||
},
|
||||
]);
|
||||
const showEnableVisible = ref<boolean>(false);
|
||||
const validateKeyWord = ref<string>('');
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
|
||||
// 启用模板
|
||||
const enableHandler = async () => {
|
||||
const orgName = computed(() => {
|
||||
return appStore.ordList.find((item: any) => item.id === appStore.currentOrgId)?.name;
|
||||
});
|
||||
|
||||
async function okHandler() {
|
||||
if (validateKeyWord.value.trim() !== '' && validateKeyWord.value !== orgName.value) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
confirmLoading.value = true;
|
||||
if (props.mode) {
|
||||
await enableOrOffTemplate(currentOrgId.value, props.cardItem.key);
|
||||
Message.success(t('system.orgTemplate.enabledSuccessfully'));
|
||||
await templateStore.getStatus();
|
||||
emit('updateState');
|
||||
confirmLoading.value = false;
|
||||
showEnableVisible.value = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
// 启用模板
|
||||
const enableHandler = async () => {
|
||||
try {
|
||||
showEnableVisible.value = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -127,6 +193,11 @@
|
|||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
function cancelHandler() {
|
||||
showEnableVisible.value = false;
|
||||
validateKeyWord.value = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="mb-4 flex items-center justify-between">
|
||||
<span v-if="isEnableOrdTemplate" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
|
||||
<a-button
|
||||
v-else
|
||||
v-if="!isEnableOrdTemplate && route.query.type === 'BUG'"
|
||||
v-permission="['ORGANIZATION_TEMPLATE:READ+ADD']"
|
||||
type="primary"
|
||||
:disabled="false"
|
||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
|||
'system.orgTemplate.fieldNameRules': 'The field name cannot be empty',
|
||||
'system.orgTemplate.fieldNamePlaceholder': 'Please enter a field name',
|
||||
'system.orgTemplate.description': 'Description',
|
||||
'system.orgTemplate.resDescription': 'Describe the resource pool',
|
||||
'system.orgTemplate.resDescription': 'Describe the fields pool',
|
||||
'system.orgTemplate.fieldType': 'Field type',
|
||||
'system.orgTemplate.fieldTypePlaceholder': 'Please select a field type',
|
||||
'system.orgTemplate.allowMultiMember': 'Allows multiple members to be added',
|
||||
|
@ -177,4 +177,7 @@ export default {
|
|||
'system.orgTemplate.templateCase': 'Case',
|
||||
'system.orgTemplate.templateApi': 'Api',
|
||||
'system.orgTemplate.templateBug': 'Bug',
|
||||
'system.orgTemplate.enableTip': 'Are you sure to enable the project template',
|
||||
'system.orgTemplate.enableWarningTip': 'Enabled, irreversible for organization template, please careful operation.',
|
||||
'system.orgTemplate.typeEmptyTip': 'The type cannot be empty',
|
||||
};
|
||||
|
|
|
@ -49,7 +49,7 @@ export default {
|
|||
'system.orgTemplate.fieldNameRules': '字段名称不能为空',
|
||||
'system.orgTemplate.fieldNamePlaceholder': '请输入字段名称',
|
||||
'system.orgTemplate.description': '描述',
|
||||
'system.orgTemplate.resDescription': '请对该资源池进行描述',
|
||||
'system.orgTemplate.resDescription': '请对该字段进行描述',
|
||||
'system.orgTemplate.fieldType': '字段类型',
|
||||
'system.orgTemplate.fieldTypePlaceholder': '请选择字段类型',
|
||||
'system.orgTemplate.allowMultiMember': '允许添加多个成员',
|
||||
|
@ -166,4 +166,7 @@ export default {
|
|||
'system.orgTemplate.templateCase': '用例',
|
||||
'system.orgTemplate.templateApi': '接口',
|
||||
'system.orgTemplate.templateBug': '缺陷',
|
||||
'system.orgTemplate.enableTip': '确认启用项目模版吗',
|
||||
'system.orgTemplate.enableWarningTip': '启用后,不可恢复为组织模版,请谨慎操作!',
|
||||
'system.orgTemplate.typeEmptyTip': '类型不能为空',
|
||||
};
|
||||
|
|
|
@ -23,14 +23,16 @@
|
|||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="global" :label="t('system.plugin.appOrganize')" asterisk-position="end">
|
||||
<a-radio-group v-model="form.global">
|
||||
<a-form-item
|
||||
:tooltip="t('system.plugin.allOrganizeTip')"
|
||||
field="global"
|
||||
:label="t('system.plugin.appOrganize')"
|
||||
asterisk-position="end"
|
||||
:row-class="form.global ? 'allOrganizeTip' : ''"
|
||||
>
|
||||
<a-radio-group v-model="form.global" type="button">
|
||||
<a-radio :value="true"
|
||||
>{{ t('system.plugin.allOrganize')
|
||||
}}<span class="float-right mx-1 mt-[1px]">
|
||||
<a-tooltip :content="t('system.plugin.allOrganizeTip')" position="top">
|
||||
<IconQuestionCircle class="h-[16px] w-[16px] text-[--color-text-4]"
|
||||
/></a-tooltip> </span
|
||||
>{{ t('system.plugin.allOrganize') }}<span class="float-right mx-1 mt-[1px]"> </span
|
||||
></a-radio>
|
||||
<a-radio :value="false">{{ t('system.plugin.theOrganize') }}</a-radio>
|
||||
</a-radio-group>
|
||||
|
@ -40,6 +42,7 @@
|
|||
field="organizationIds"
|
||||
:label="t('system.plugin.selectOrganization')"
|
||||
asterisk-position="end"
|
||||
:row-class="showIncludeOrg || showRemoveOrg ? 'allOrganizeTip' : ''"
|
||||
:rules="[{ required: true, message: t('system.plugin.selectOrganizeTip') }]"
|
||||
>
|
||||
<a-select
|
||||
|
@ -51,6 +54,15 @@
|
|||
<a-option v-for="item of organizeList" :key="item.id" :value="item.id">{{ item.name }}</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<div v-if="form.global" class="mb-[16px] ml-1 text-[12px] text-[rgb(var(--danger-6))]">
|
||||
{{ title }} {{ t('system.plugin.switchAllOrganizeTip') }}
|
||||
</div>
|
||||
<div v-if="showIncludeOrg" class="mb-[16px] ml-1 text-[12px] text-[rgb(var(--danger-6))]">
|
||||
{{ title }} {{ t('system.plugin.switchSectionOrganizeTip') }}
|
||||
</div>
|
||||
<div v-if="showRemoveOrg" class="mb-[16px] ml-1 text-[12px] text-[rgb(var(--danger-6))]">
|
||||
{{ t('system.plugin.changeOrganizeTip') }}
|
||||
</div>
|
||||
<a-form-item field="description" :label="t('system.plugin.description')" asterisk-position="end">
|
||||
<a-textarea
|
||||
v-model="form.description"
|
||||
|
@ -72,6 +84,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch, watchEffect } from 'vue';
|
||||
import { FormInstance, Message, SelectOptionData, ValidatedError } from '@arco-design/web-vue';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import { updatePlugin } from '@/api/modules/setting/pluginManger';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -112,8 +125,10 @@
|
|||
UpdateFormRef.value?.resetFields();
|
||||
updateVisible.value = false;
|
||||
};
|
||||
const originOrgIds = ref<string[]>([]);
|
||||
const open = (record: PluginItem) => {
|
||||
title.value = record.name as string;
|
||||
originOrgIds.value = (record.organizations || []).map((item) => item.id);
|
||||
form.value = {
|
||||
...record,
|
||||
organizationIds: (record.organizations || []).map((item) => item.id),
|
||||
|
@ -147,10 +162,31 @@
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
const showIncludeOrg = computed(() => {
|
||||
const { organizationIds } = form.value;
|
||||
if (!form.value.global && !isEqual(organizationIds, originOrgIds.value)) {
|
||||
return originOrgIds.value?.every((item) => organizationIds?.includes(item));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const showRemoveOrg = computed(() => {
|
||||
const { organizationIds } = form.value;
|
||||
if (!form.value.global && !isEqual(originOrgIds.value, organizationIds)) {
|
||||
return originOrgIds.value?.some((item) => !organizationIds?.includes(item));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
UpdateFormRef,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped lang="less">
|
||||
.allOrganizeTip {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -108,4 +108,7 @@ export default {
|
|||
'system.plugin.deleteContentTip':
|
||||
'After deletion, the defects/requirements of the platform cannot be synchronized, and the historical data is automatically switched to other templates. Please exercise caution!',
|
||||
'system.plugin.allOrganizeTip': 'This rule is common to new organizations',
|
||||
'system.plugin.switchAllOrganizeTip': 'The plugin will be visible to all organizations',
|
||||
'system.plugin.switchSectionOrganizeTip': 'Plug-in to specify organization is visible',
|
||||
'system.plugin.changeOrganizeTip': 'Changing the organization will make historical data unavailable, so be careful!',
|
||||
};
|
||||
|
|
|
@ -87,4 +87,7 @@ export default {
|
|||
'system.plugin.databaseDriver': '数据库驱动',
|
||||
'system.plugin.deleteContentTip': '删除后,将无法同步该平台的缺陷/需求,历史数据自动切换为其它模板展示,请谨慎操作!',
|
||||
'system.plugin.allOrganizeTip': '新建组织通用此规则',
|
||||
'system.plugin.switchAllOrganizeTip': '插件将对所有组织可见',
|
||||
'system.plugin.switchSectionOrganizeTip': '插件将对指定组织可见',
|
||||
'system.plugin.changeOrganizeTip': ' 变更组织会导致历史数据不可用,请谨慎操作!',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue