feat(接口测试): 接口场景表格
接口场景表格查询功能开发,以及删除、批量移动、批量复制、部分字段批量修改的功能
This commit is contained in:
parent
e26bb58dd9
commit
9c40e9f602
|
@ -125,11 +125,11 @@
|
||||||
<property name="condition" value="request.condition.combine"/>
|
<property name="condition" value="request.condition.combine"/>
|
||||||
</include>
|
</include>
|
||||||
</if>
|
</if>
|
||||||
<if test="request.condition.keyword != null">
|
<if test="request.condition.keyword != null and request.condition.keyword != ''">
|
||||||
and (
|
and (
|
||||||
api_scenario.num like concat('%', #{request.keyword},'%')
|
api_scenario.num like concat('%', #{request.condition.keyword},'%')
|
||||||
or api_scenario.name like concat('%', #{request.keyword},'%')
|
or api_scenario.name like concat('%', #{request.condition.keyword},'%')
|
||||||
or api_scenario.tags like JSON_CONTAINS(tags, concat('["',#{request.keyword},'"]'))
|
or api_scenario.tags like JSON_CONTAINS(tags, concat('["',#{request.condition.keyword},'"]'))
|
||||||
)
|
)
|
||||||
</if>
|
</if>
|
||||||
<include refid="filters">
|
<include refid="filters">
|
||||||
|
|
|
@ -1,19 +1,30 @@
|
||||||
import MSR from '@/api/http/index';
|
import MSR from '@/api/http/index';
|
||||||
import {
|
import {
|
||||||
AddModuleUrl,
|
AddModuleUrl,
|
||||||
|
BatchCopyScenarioUrl,
|
||||||
|
BatchEditScenarioUrl,
|
||||||
|
BatchMoveScenarioUrl,
|
||||||
|
BatchRecycleScenarioUrl,
|
||||||
DeleteModuleUrl,
|
DeleteModuleUrl,
|
||||||
GetModuleCountUrl,
|
GetModuleCountUrl,
|
||||||
GetModuleTreeUrl,
|
GetModuleTreeUrl,
|
||||||
MoveModuleUrl,
|
MoveModuleUrl,
|
||||||
|
RecycleScenarioUrl,
|
||||||
|
ScenarioPageUrl,
|
||||||
UpdateModuleUrl,
|
UpdateModuleUrl,
|
||||||
|
UpdateScenarioUrl,
|
||||||
} from '@/api/requrls/api-test/scenario';
|
} from '@/api/requrls/api-test/scenario';
|
||||||
|
|
||||||
import { ApiScenarioGetModuleParams, ApiScenarioModuleUpdateParams } from '@/models/apiTest/scenario';
|
|
||||||
import {
|
import {
|
||||||
AddModuleParams,
|
ApiScenarioBatchDeleteParams,
|
||||||
ModuleTreeNode,
|
ApiScenarioBatchEditParams,
|
||||||
MoveModules,
|
ApiScenarioDetail,
|
||||||
} from '@/models/common';
|
ApiScenarioGetModuleParams,
|
||||||
|
ApiScenarioModuleUpdateParams,
|
||||||
|
ApiScenarioPageParams,
|
||||||
|
ApiScenarioUpdateDTO,
|
||||||
|
} from '@/models/apiTest/scenario';
|
||||||
|
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules } from '@/models/common';
|
||||||
|
|
||||||
// 更新模块
|
// 更新模块
|
||||||
export function updateModule(data: ApiScenarioModuleUpdateParams) {
|
export function updateModule(data: ApiScenarioModuleUpdateParams) {
|
||||||
|
@ -30,7 +41,6 @@ export function moveModule(data: MoveModules) {
|
||||||
return MSR.post({ url: MoveModuleUrl, data });
|
return MSR.post({ url: MoveModuleUrl, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取模块统计数量
|
// 获取模块统计数量
|
||||||
export function getModuleCount(data: ApiScenarioGetModuleParams) {
|
export function getModuleCount(data: ApiScenarioGetModuleParams) {
|
||||||
return MSR.post({ url: GetModuleCountUrl, data });
|
return MSR.post({ url: GetModuleCountUrl, data });
|
||||||
|
@ -45,3 +55,49 @@ export function addModule(data: AddModuleParams) {
|
||||||
export function deleteModule(id: string) {
|
export function deleteModule(id: string) {
|
||||||
return MSR.get({ url: DeleteModuleUrl, params: id });
|
return MSR.get({ url: DeleteModuleUrl, params: id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取接口场景列表
|
||||||
|
export function getScenarioPage(data: ApiScenarioPageParams) {
|
||||||
|
return MSR.post<CommonList<ApiScenarioDetail>>({ url: ScenarioPageUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新接口场景
|
||||||
|
export function updateScenario(data: ApiScenarioUpdateDTO) {
|
||||||
|
return MSR.post({ url: UpdateScenarioUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除场景
|
||||||
|
export function recycleScenario(id: string) {
|
||||||
|
return MSR.get({ url: RecycleScenarioUrl, params: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除场景
|
||||||
|
export function batchRecycleScenario(data: ApiScenarioBatchDeleteParams) {
|
||||||
|
return MSR.post({ url: BatchRecycleScenarioUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量操作场景
|
||||||
|
export function batchOptionScenario(
|
||||||
|
optionType: string,
|
||||||
|
data: {
|
||||||
|
moduleIds: string[];
|
||||||
|
selectAll: boolean;
|
||||||
|
condition: { keyword: string };
|
||||||
|
excludeIds: any[];
|
||||||
|
selectIds: any[];
|
||||||
|
projectId: string;
|
||||||
|
targetModuleId: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (optionType === 'batchMove') {
|
||||||
|
return MSR.post({ url: BatchMoveScenarioUrl, data });
|
||||||
|
}
|
||||||
|
if (optionType === 'batchCopy') {
|
||||||
|
return MSR.post({ url: BatchCopyScenarioUrl, data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量编辑场景
|
||||||
|
export function batchEditScenario(params: ApiScenarioBatchEditParams) {
|
||||||
|
return MSR.post({ url: BatchEditScenarioUrl, params });
|
||||||
|
}
|
||||||
|
|
|
@ -4,27 +4,32 @@ export const MoveModuleUrl = '/api/scenario/module/move'; // 移动模块
|
||||||
export const GetModuleCountUrl = '/api/scenario/module/count'; // 获取模块统计数量
|
export const GetModuleCountUrl = '/api/scenario/module/count'; // 获取模块统计数量
|
||||||
export const AddModuleUrl = '/api/scenario/module/add'; // 添加模块
|
export const AddModuleUrl = '/api/scenario/module/add'; // 添加模块
|
||||||
export const DeleteModuleUrl = '/api/scenario/module/delete'; // 删除模块
|
export const DeleteModuleUrl = '/api/scenario/module/delete'; // 删除模块
|
||||||
|
export const ScenarioPageUrl = '/api/scenario/page'; // 接口场景列表
|
||||||
|
export const UpdateScenarioUrl = '/api/scenario/update'; // 更新接口场景
|
||||||
|
export const RecycleScenarioUrl = '/api/scenario/delete-to-gc'; // 删除接口场景
|
||||||
|
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
|
||||||
|
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
|
||||||
|
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景
|
||||||
|
export const BatchEditScenarioUrl = '/api/scenario/batch-operation/edit'; // 批量编辑接口场景
|
||||||
|
|
||||||
// export const GetEnvModuleUrl = '/api/scenario/module/env/tree'; // 获取环境的模块树
|
// export const GetEnvModuleUrl = '/api/scenario/module/env/tree'; // 获取环境的模块树
|
||||||
// export const DefinitionPageUrl = '/api/scenario/page'; // 接口定义列表
|
// export const AddDefinitionUrl = '/api/scenario/add'; // 添加接口场景
|
||||||
// export const AddDefinitionUrl = '/api/scenario/add'; // 添加接口定义
|
// export const GetDefinitionDetailUrl = '/api/scenario/get-detail'; // 获取接口场景详情
|
||||||
// export const UpdateDefinitionUrl = '/api/scenario/update'; // 更新接口定义
|
|
||||||
// export const GetDefinitionDetailUrl = '/api/scenario/get-detail'; // 获取接口定义详情
|
|
||||||
// export const TransferFileUrl = '/api/scenario/transfer'; // 文件转存
|
// export const TransferFileUrl = '/api/scenario/transfer'; // 文件转存
|
||||||
// export const TransferFileModuleOptionUrl = '/api/scenario/transfer/options'; // 文件转存目录
|
// export const TransferFileModuleOptionUrl = '/api/scenario/transfer/options'; // 文件转存目录
|
||||||
// export const UploadTempFileUrl = '/api/scenario/upload/temp/file'; // 临时文件上传
|
// export const UploadTempFileUrl = '/api/scenario/upload/temp/file'; // 临时文件上传
|
||||||
// export const DefinitionMockPageUrl = '/api/scenario/mock/page'; // mock列表
|
// export const DefinitionMockPageUrl = '/api/scenario/mock/page'; // mock列表
|
||||||
// export const UpdateMockStatusUrl = '/api/scenario/mock/enable/'; // 更新mock状态
|
// export const UpdateMockStatusUrl = '/api/scenario/mock/enable/'; // 更新mock状态
|
||||||
// export const DeleteMockUrl = '/api/scenario/mock/delete'; // 刪除mock
|
// export const DeleteDefinitionUrl = '/api/scenario/delete-to-gc'; // 删除接口场景
|
||||||
// export const DeleteDefinitionUrl = '/api/scenario/delete-to-gc'; // 删除接口定义
|
// export const ImportDefinitionUrl = '/api/scenario/import'; // 导入接口场景
|
||||||
// export const ImportDefinitionUrl = '/api/scenario/import'; // 导入接口定义
|
// export const SortDefinitionUrl = '/api/scenario/edit/pos'; // 接口场景拖拽
|
||||||
// export const SortDefinitionUrl = '/api/scenario/edit/pos'; // 接口定义拖拽
|
// export const BatchUpdateDefinitionUrl = '/api/scenario/batch-update'; // 批量更新接口场景
|
||||||
// export const BatchUpdateDefinitionUrl = '/api/scenario/batch-update'; // 批量更新接口定义
|
// export const BatchMoveDefinitionUrl = '/api/scenario/batch-move'; // 批量移动接口场景
|
||||||
// export const BatchMoveDefinitionUrl = '/api/scenario/batch-move'; // 批量移动接口定义
|
|
||||||
// export const BatchDeleteDefinitionUrl = '/api/scenario/batch/delete-to-gc'; // 批量删除接口定义
|
// export const UpdateDefinitionScheduleUrl = '/api/scenario/schedule/update'; // 接口场景-定时同步-更新
|
||||||
// export const UpdateDefinitionScheduleUrl = '/api/scenario/schedule/update'; // 接口定义-定时同步-更新
|
// export const CheckDefinitionScheduleUrl = '/api/scenario/schedule/check'; // 接口场景-定时同步-检查 url 是否存在
|
||||||
// export const CheckDefinitionScheduleUrl = '/api/scenario/schedule/check'; // 接口定义-定时同步-检查 url 是否存在
|
// export const AddDefinitionScheduleUrl = '/api/scenario/schedule/add'; // 接口场景-定时同步-添加
|
||||||
// export const AddDefinitionScheduleUrl = '/api/scenario/schedule/add'; // 接口定义-定时同步-添加
|
// export const SwitchDefinitionScheduleUrl = '/api/scenario/schedule/switch'; // 接口场景-定时同步-开启关闭
|
||||||
// export const SwitchDefinitionScheduleUrl = '/api/scenario/schedule/switch'; // 接口定义-定时同步-开启关闭
|
// export const GetDefinitionScheduleUrl = '/api/scenario/schedule/get'; // 接口场景-定时同步-查询
|
||||||
// export const GetDefinitionScheduleUrl = '/api/scenario/schedule/get'; // 接口定义-定时同步-查询
|
// export const DeleteDefinitionScheduleUrl = '/api/scenario/schedule/delete'; // 接口场景-定时同步-删除
|
||||||
// export const DeleteDefinitionScheduleUrl = '/api/scenario/schedule/delete'; // 接口定义-定时同步-删除
|
// export const DebugDefinitionUrl = '/api/scenario/debug'; // 接口场景-调试
|
||||||
// export const DebugDefinitionUrl = '/api/scenario/debug'; // 接口定义-调试
|
|
||||||
|
|
|
@ -95,6 +95,13 @@ export const pathMap: PathMapItem[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'API_TEST_SCENARIO', // 接口测试-场景
|
||||||
|
locale: 'menu.apiTest.scenario',
|
||||||
|
route: RouteEnum.API_TEST_SCENARIO,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'API_TEST_REPORT', // 接口测试-接口测试报告
|
key: 'API_TEST_REPORT', // 接口测试-接口测试报告
|
||||||
locale: 'menu.apiTest.report',
|
locale: 'menu.apiTest.report',
|
||||||
|
|
|
@ -69,6 +69,13 @@ export enum RequestDefinitionStatus {
|
||||||
DEBUGGING = 'DEBUGGING',
|
DEBUGGING = 'DEBUGGING',
|
||||||
DONE = 'DONE',
|
DONE = 'DONE',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 接口场景状态
|
||||||
|
export enum ApiScenarioStatus {
|
||||||
|
DEPRECATED = 'DEPRECATED',
|
||||||
|
UNDERWAY = 'UNDERWAY',
|
||||||
|
COMPLETED = 'COMPLETED',
|
||||||
|
}
|
||||||
// 接口导入支持格式
|
// 接口导入支持格式
|
||||||
export enum RequestImportFormat {
|
export enum RequestImportFormat {
|
||||||
SWAGGER = 'Swagger3',
|
SWAGGER = 'Swagger3',
|
||||||
|
|
|
@ -114,6 +114,7 @@ export default {
|
||||||
'common.tagsInputPlaceholder': 'Enter the content and press Enter to directly add tags',
|
'common.tagsInputPlaceholder': 'Enter the content and press Enter to directly add tags',
|
||||||
'common.move': 'Move',
|
'common.move': 'Move',
|
||||||
'common.batchMove': 'Batch move',
|
'common.batchMove': 'Batch move',
|
||||||
|
'common.batchCopy': 'Batch copy',
|
||||||
'common.batchMoveSuccess': 'Batch move successful',
|
'common.batchMoveSuccess': 'Batch move successful',
|
||||||
'common.importSuccess': 'Import successful',
|
'common.importSuccess': 'Import successful',
|
||||||
'common.nameIsTooLang': 'The name exceeds 255 characters',
|
'common.nameIsTooLang': 'The name exceeds 255 characters',
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default {
|
||||||
'menu.apiTest.debug.debug': 'Debug',
|
'menu.apiTest.debug.debug': 'Debug',
|
||||||
'menu.apiTest.management': 'API Management',
|
'menu.apiTest.management': 'API Management',
|
||||||
'menu.apiTest.management.definition': 'API Definition',
|
'menu.apiTest.management.definition': 'API Definition',
|
||||||
'menu.apiTest.scenario': 'API Scenario',
|
'menu.apiTest.scenario': 'Scenario',
|
||||||
'menu.apiTest.report': 'API Report',
|
'menu.apiTest.report': 'API Report',
|
||||||
'menu.uiTest': 'UI Test',
|
'menu.uiTest': 'UI Test',
|
||||||
'menu.performanceTest': 'Performance Test',
|
'menu.performanceTest': 'Performance Test',
|
||||||
|
|
|
@ -117,6 +117,7 @@ export default {
|
||||||
'common.tagsInputPlaceholder': '输入内容后回车可直接添加标签',
|
'common.tagsInputPlaceholder': '输入内容后回车可直接添加标签',
|
||||||
'common.move': '移动',
|
'common.move': '移动',
|
||||||
'common.batchMove': '批量移动',
|
'common.batchMove': '批量移动',
|
||||||
|
'common.batchCopy': '批量复制',
|
||||||
'common.batchMoveSuccess': '批量移动成功',
|
'common.batchMoveSuccess': '批量移动成功',
|
||||||
'common.importSuccess': '导入成功',
|
'common.importSuccess': '导入成功',
|
||||||
'common.nameIsTooLang': '名称超过255个字符',
|
'common.nameIsTooLang': '名称超过255个字符',
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default {
|
||||||
'menu.apiTest.management': '接口管理',
|
'menu.apiTest.management': '接口管理',
|
||||||
'menu.apiTest.management.definition': '接口定义',
|
'menu.apiTest.management.definition': '接口定义',
|
||||||
'menu.apiTest.api': 'API列表',
|
'menu.apiTest.api': 'API列表',
|
||||||
'menu.apiTest.scenario': '接口场景',
|
'menu.apiTest.scenario': '场景',
|
||||||
'menu.apiTest.report': '接口报告',
|
'menu.apiTest.report': '接口报告',
|
||||||
'menu.uiTest': 'UI测试',
|
'menu.uiTest': 'UI测试',
|
||||||
'menu.workstation': '工作台',
|
'menu.workstation': '工作台',
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { RequestDefinitionStatus, RequestImportFormat, RequestImportType } from '@/enums/apiEnum';
|
import { ApiDefinitionCustomField } from '@/models/apiTest/management';
|
||||||
|
import { ApiScenarioStatus, RequestDefinitionStatus, RequestImportFormat, RequestImportType } from '@/enums/apiEnum';
|
||||||
import { BatchApiParams, ModuleTreeNode, TableQueryParams } from '../common';
|
|
||||||
import { ExecuteRequestParams, ResponseDefinition } from './common';
|
|
||||||
|
|
||||||
|
import { BatchApiParams, TableQueryParams } from '../common';
|
||||||
|
import { ResponseDefinition } from './common';
|
||||||
|
|
||||||
// 场景-更新模块参数
|
// 场景-更新模块参数
|
||||||
export interface ApiScenarioModuleUpdateParams {
|
export interface ApiScenarioModuleUpdateParams {
|
||||||
|
@ -12,35 +12,72 @@ export interface ApiScenarioModuleUpdateParams {
|
||||||
|
|
||||||
// 场景-获取模块树参数
|
// 场景-获取模块树参数
|
||||||
export interface ApiScenarioGetModuleParams {
|
export interface ApiScenarioGetModuleParams {
|
||||||
keyword: string;
|
keyword?: string;
|
||||||
searchMode?: 'AND' | 'OR';
|
searchMode?: 'AND' | 'OR';
|
||||||
filter?: Record<string, any>;
|
filter?: Record<string, any>;
|
||||||
combine?: Record<string, any>;
|
combine?: Record<string, any>;
|
||||||
moduleIds: string[];
|
moduleIds?: string[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
versionId?: string;
|
versionId?: string;
|
||||||
refId?: string;
|
refId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 环境-选中的模块
|
// 场景修改参数
|
||||||
export interface SelectedModule {
|
export interface ApiScenarioUpdateDTO {
|
||||||
// 选中的模块
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
priority?: string;
|
||||||
|
status?: ApiScenarioStatus;
|
||||||
|
moduleId?: string;
|
||||||
|
description?: string;
|
||||||
|
tags?: string[];
|
||||||
|
grouped?: boolean;
|
||||||
|
environmentId?: string;
|
||||||
|
uploadFileIds?: string[];
|
||||||
|
linkFileIds?: string[];
|
||||||
|
deleteFileIds?: string[];
|
||||||
|
unLinkFileIds?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 场景详情
|
||||||
|
export interface ApiScenarioDetail {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
method: string;
|
||||||
|
path: string;
|
||||||
|
num: number;
|
||||||
|
pos: number;
|
||||||
|
projectId: string;
|
||||||
moduleId: string;
|
moduleId: string;
|
||||||
containChildModule: boolean; // 是否包含新增子模块
|
latest: boolean;
|
||||||
disabled: boolean;
|
versionId: string;
|
||||||
|
refId: string;
|
||||||
|
createTime: number;
|
||||||
|
createUser: string;
|
||||||
|
updateTime: number;
|
||||||
|
updateUser: string;
|
||||||
|
deleteUser: string;
|
||||||
|
deleteTime: number;
|
||||||
|
deleted: boolean;
|
||||||
|
createUserName: string;
|
||||||
|
updateUserName: string;
|
||||||
|
deleteUserName: string;
|
||||||
|
versionName: string;
|
||||||
|
caseTotal: number;
|
||||||
|
casePassRate: string;
|
||||||
|
caseStatus: string;
|
||||||
|
follow: boolean;
|
||||||
|
tags: string[];
|
||||||
|
response: ResponseDefinition;
|
||||||
|
description: string;
|
||||||
|
status: RequestDefinitionStatus;
|
||||||
|
customFields: ApiDefinitionCustomField[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 环境-模块树
|
// 场景列表查询参数
|
||||||
export interface EnvModule {
|
|
||||||
moduleTree: ModuleTreeNode[];
|
|
||||||
selectedModules: SelectedModule[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义列表查询参数
|
|
||||||
export interface ApiScenarioPageParams extends TableQueryParams {
|
export interface ApiScenarioPageParams extends TableQueryParams {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
protocol: string;
|
|
||||||
projectId: string;
|
projectId: string;
|
||||||
versionId: string;
|
versionId: string;
|
||||||
refId: string;
|
refId: string;
|
||||||
|
@ -48,13 +85,7 @@ export interface ApiScenarioPageParams extends TableQueryParams {
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量更新场景参数
|
||||||
export interface mockParams {
|
|
||||||
id: string;
|
|
||||||
projectId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量更新定义参数
|
|
||||||
export interface ApiScenarioBatchUpdateParams extends BatchApiParams {
|
export interface ApiScenarioBatchUpdateParams extends BatchApiParams {
|
||||||
type?: string;
|
type?: string;
|
||||||
append?: boolean;
|
append?: boolean;
|
||||||
|
@ -64,18 +95,39 @@ export interface ApiScenarioBatchUpdateParams extends BatchApiParams {
|
||||||
customField?: Record<string, any>;
|
customField?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量移动定义参数
|
export interface BatchOptionParams extends BatchApiParams {
|
||||||
export interface ApiScenarioBatchMoveParams extends BatchApiParams {
|
apiScenarioId?: string;
|
||||||
moduleId: string | number;
|
versionId?: string;
|
||||||
|
refId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量删除定义参数
|
// 批量移动场景参数
|
||||||
|
export interface ApiScenarioBatchMoveParams extends BatchOptionParams {
|
||||||
|
targetModuleId: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量编辑场景参数
|
||||||
|
export interface ApiScenarioBatchEditParams extends BatchOptionParams {
|
||||||
|
// 修改操作的类型
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
// 修改标签相关
|
||||||
|
appendTag?: boolean;
|
||||||
|
tags?: string[];
|
||||||
|
|
||||||
|
// 修改环境相关
|
||||||
|
grouped?: boolean;
|
||||||
|
envId?: string;
|
||||||
|
groupId?: string;
|
||||||
|
|
||||||
|
// 修改状态
|
||||||
|
status?: string;
|
||||||
|
|
||||||
|
// 修改优先级
|
||||||
|
priority?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除场景参数
|
||||||
export interface ApiScenarioBatchDeleteParams extends BatchApiParams {
|
export interface ApiScenarioBatchDeleteParams extends BatchApiParams {
|
||||||
deleteAll: boolean;
|
deleteAll: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 场景-定时同步-更新参数
|
|
||||||
export interface UpdateScheduleParams {
|
|
||||||
id: string;
|
|
||||||
taskId: string;
|
|
||||||
}
|
|
||||||
|
|
|
@ -69,11 +69,12 @@ const ApiTest: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/api-test/scenario/index.vue'),
|
component: () => import('@/views/api-test/scenario/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.apiTest.scenario',
|
locale: 'menu.apiTest.scenario',
|
||||||
roles: ['*'],
|
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
|
roles: ['PROJECT_API_SCENARIO:READ'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 接口场景回收站
|
||||||
{
|
{
|
||||||
path: 'scenarioRecycle',
|
path: 'scenarioRecycle',
|
||||||
name: ApiTestRouteEnum.API_TEST_SCENARIO_RECYCLE,
|
name: ApiTestRouteEnum.API_TEST_SCENARIO_RECYCLE,
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import { RequestDefinitionStatus } from '@/enums/apiEnum';
|
import { ApiScenarioStatus, RequestDefinitionStatus } from '@/enums/apiEnum';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
status: RequestDefinitionStatus;
|
status: RequestDefinitionStatus | ApiScenarioStatus;
|
||||||
size?: Size;
|
size?: Size;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -27,6 +27,11 @@
|
||||||
color: 'rgb(var(--link-5))',
|
color: 'rgb(var(--link-5))',
|
||||||
text: 'apiTestManagement.processing',
|
text: 'apiTestManagement.processing',
|
||||||
},
|
},
|
||||||
|
[ApiScenarioStatus.UNDERWAY]: {
|
||||||
|
bgColor: 'rgb(var(--link-2))',
|
||||||
|
color: 'rgb(var(--link-5))',
|
||||||
|
text: 'apiTestManagement.processing',
|
||||||
|
},
|
||||||
[RequestDefinitionStatus.DEBUGGING]: {
|
[RequestDefinitionStatus.DEBUGGING]: {
|
||||||
bgColor: 'rgb(var(--link-2))',
|
bgColor: 'rgb(var(--link-2))',
|
||||||
color: 'rgb(var(--link-6))',
|
color: 'rgb(var(--link-6))',
|
||||||
|
@ -37,6 +42,11 @@
|
||||||
color: 'rgb(var(--success-6))',
|
color: 'rgb(var(--success-6))',
|
||||||
text: 'apiTestManagement.done',
|
text: 'apiTestManagement.done',
|
||||||
},
|
},
|
||||||
|
[ApiScenarioStatus.COMPLETED]: {
|
||||||
|
bgColor: 'rgb(var(--success-2))',
|
||||||
|
color: 'rgb(var(--success-6))',
|
||||||
|
text: 'apiTestManagement.done',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
const status = computed(() => {
|
const status = computed(() => {
|
||||||
const config = statusMap[props.status];
|
const config = statusMap[props.status];
|
||||||
|
|
|
@ -0,0 +1,235 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="mb-[8px] flex items-center gap-[8px]">
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="moduleKeyword"
|
||||||
|
:placeholder="t('apiScenario.tree.selectorPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<a-spin class="w-full" :loading="loading">
|
||||||
|
<MsTree
|
||||||
|
v-model:focus-node-key="focusNodeKey"
|
||||||
|
v-model:selected-keys="selectedKeys"
|
||||||
|
:data="folderTree"
|
||||||
|
:keyword="moduleKeyword"
|
||||||
|
:node-more-actions="folderMoreActions"
|
||||||
|
:default-expand-all="isExpandAll"
|
||||||
|
:expand-all="isExpandAll"
|
||||||
|
:empty-text="t('apiScenario.tree.noMatchModule')"
|
||||||
|
:virtual-list-props="virtualListProps"
|
||||||
|
:field-names="{
|
||||||
|
title: 'name',
|
||||||
|
key: 'id',
|
||||||
|
children: 'children',
|
||||||
|
count: 'count',
|
||||||
|
}"
|
||||||
|
block-node
|
||||||
|
title-tooltip-position="left"
|
||||||
|
@select="folderNodeSelect"
|
||||||
|
>
|
||||||
|
<template #title="nodeData">
|
||||||
|
<div :id="nodeData.id" class="inline-flex w-full">
|
||||||
|
<div class="one-line-text w-[calc(100%-32px)] text-[var(--color-text-1)]">{{ nodeData.name }}</div>
|
||||||
|
<div class="ml-[4px] text-[var(--color-text-4)]">({{ nodeData.count || 0 }})</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsTree>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeMount, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||||
|
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
|
|
||||||
|
import { getModuleCount, getModuleTree } from '@/api/modules/api-test/scenario';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useModal from '@/hooks/useModal';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
import { mapTree } from '@/utils';
|
||||||
|
|
||||||
|
import { ApiScenarioGetModuleParams } from '@/models/apiTest/scenario';
|
||||||
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
isExpandAll?: boolean; // 是否展开所有节点
|
||||||
|
}>(),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const emit = defineEmits(['init', 'newScenario', 'import', 'folderNodeSelect', 'clickScenario', 'changeProtocol']);
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openModal } = useModal();
|
||||||
|
|
||||||
|
function handleSelect(value: string | number | Record<string, any> | undefined) {
|
||||||
|
switch (value) {
|
||||||
|
case 'newScenario':
|
||||||
|
emit('newScenario');
|
||||||
|
break;
|
||||||
|
case 'import':
|
||||||
|
emit('import');
|
||||||
|
break;
|
||||||
|
case 'addModule':
|
||||||
|
document.querySelector('#addModulePopSpan')?.dispatchEvent(new Event('click'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const virtualListProps = computed(() => {
|
||||||
|
return {
|
||||||
|
height: 'calc(100vh - 343px)',
|
||||||
|
threshold: 200,
|
||||||
|
fixedSize: true,
|
||||||
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const moduleKeyword = ref('');
|
||||||
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
|
const focusNodeKey = ref<string | number>('');
|
||||||
|
const selectedKeys = ref<Array<string | number>>([]);
|
||||||
|
const allFolderClass = computed(() =>
|
||||||
|
selectedKeys.value[0] === 'all' ? 'folder-text folder-text--active' : 'folder-text'
|
||||||
|
);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const folderMoreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'common.rename',
|
||||||
|
eventTag: 'rename',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isDivider: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'common.delete',
|
||||||
|
eventTag: 'delete',
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const modulesCount = ref<Record<string, number>>({});
|
||||||
|
const allScenarioCount = computed(() => modulesCount.value.all || 0);
|
||||||
|
const isExpandAll = ref(props.isExpandAll);
|
||||||
|
const rootModulesName = ref<string[]>([]); // 根模块名称列表
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化模块树
|
||||||
|
* @param isSetDefaultKey 是否设置第一个节点为选中节点
|
||||||
|
*/
|
||||||
|
async function initModules(isSetDefaultKey = false) {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await getModuleTree({
|
||||||
|
keyword: moduleKeyword.value,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: [],
|
||||||
|
});
|
||||||
|
const nodePathObj: Record<string, any> = {};
|
||||||
|
folderTree.value = mapTree<ModuleTreeNode>(res, (e, fullPath) => {
|
||||||
|
// 拼接当前节点的完整路径
|
||||||
|
nodePathObj[e.id] = {
|
||||||
|
path: e.path,
|
||||||
|
fullPath,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...e,
|
||||||
|
hideMoreAction: e.id === 'root',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (isSetDefaultKey) {
|
||||||
|
selectedKeys.value = [folderTree.value[0].id];
|
||||||
|
}
|
||||||
|
emit('init', folderTree.value, nodePathObj);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.isExpandAll,
|
||||||
|
(val) => {
|
||||||
|
isExpandAll.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function changeExpand() {
|
||||||
|
isExpandAll.value = !isExpandAll.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理文件夹树节点选中事件
|
||||||
|
*/
|
||||||
|
function folderNodeSelect(_selectedKeys: (string | number)[], node: MsTreeNodeData) {
|
||||||
|
emit('folderNodeSelect', node);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initModuleCount(params: ApiScenarioGetModuleParams) {
|
||||||
|
try {
|
||||||
|
const res = await getModuleCount(params);
|
||||||
|
modulesCount.value = res;
|
||||||
|
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
count: res[node.id] || 0,
|
||||||
|
disabled: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
await initModules();
|
||||||
|
await initModuleCount({
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.folder {
|
||||||
|
@apply flex cursor-pointer items-center justify-between;
|
||||||
|
|
||||||
|
padding: 8px 4px;
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(var(--primary-1));
|
||||||
|
}
|
||||||
|
.folder-text {
|
||||||
|
@apply flex flex-1 cursor-pointer items-center;
|
||||||
|
.folder-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: var(--color-text-4);
|
||||||
|
}
|
||||||
|
.folder-name {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
.folder-count {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--color-text-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.folder-text--active {
|
||||||
|
.folder-icon,
|
||||||
|
.folder-name,
|
||||||
|
.folder-count {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(#root ~ .arco-tree-node-drag-icon) {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -6,15 +6,9 @@
|
||||||
:placeholder="t('apiScenario.tree.selectorPlaceholder')"
|
:placeholder="t('apiScenario.tree.selectorPlaceholder')"
|
||||||
allow-clear
|
allow-clear
|
||||||
/>
|
/>
|
||||||
<a-dropdown v-if="!props.readOnly" @select="handleSelect">
|
<a-button v-permission="['[PROJECT_API_SCENARIO:READ+ADD]']" type="primary" value="newScenario">{{
|
||||||
<a-button type="primary">{{ t('apiScenario.createScenario') }}</a-button>
|
t('apiScenario.createScenario')
|
||||||
<template #content>
|
}}</a-button>
|
||||||
<a-doption value="newScenario">{{ t('apiScenario.createScenario') }}</a-doption>
|
|
||||||
<a-doption value="import">
|
|
||||||
{{ t('apiScenario.importScenario') }}
|
|
||||||
</a-doption>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="folder" @click="setActiveFolder('all')">
|
<div class="folder" @click="setActiveFolder('all')">
|
||||||
<div :class="allFolderClass">
|
<div :class="allFolderClass">
|
||||||
|
@ -30,7 +24,7 @@
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<template v-if="!props.readOnly">
|
<template v-if="!props.readOnly">
|
||||||
<a-dropdown @select="handleSelect">
|
<a-dropdown @select="handleSelect">
|
||||||
<MsButton type="icon" class="!mr-0 p-[2px]">
|
<MsButton v-permission="['[PROJECT_API_SCENARIO:READ+ADD]']" type="icon" class="!mr-0 p-[2px]">
|
||||||
<MsIcon
|
<MsIcon
|
||||||
type="icon-icon_create_planarity"
|
type="icon-icon_create_planarity"
|
||||||
size="18"
|
size="18"
|
||||||
|
@ -73,7 +67,7 @@
|
||||||
children: 'children',
|
children: 'children',
|
||||||
count: 'count',
|
count: 'count',
|
||||||
}"
|
}"
|
||||||
:draggable="!props.readOnly"
|
:draggable="!props.readOnly && hasAnyPermission(['PROJECT_API_SCENARIO:READ+UPDATE'])"
|
||||||
block-node
|
block-node
|
||||||
title-tooltip-position="left"
|
title-tooltip-position="left"
|
||||||
@select="folderNodeSelect"
|
@select="folderNodeSelect"
|
||||||
|
@ -98,7 +92,13 @@
|
||||||
@close="resetFocusNodeKey"
|
@close="resetFocusNodeKey"
|
||||||
@add-finish="() => initModules()"
|
@add-finish="() => initModules()"
|
||||||
>
|
>
|
||||||
<MsButton type="icon" size="mini" class="ms-tree-node-extra__btn !mr-0" @click="setFocusNodeKey(nodeData)">
|
<MsButton
|
||||||
|
v-permission="['PROJECT_API_SCENARIO:READ+ADD']"
|
||||||
|
type="icon"
|
||||||
|
size="mini"
|
||||||
|
class="ms-tree-node-extra__btn !mr-0"
|
||||||
|
@click="setFocusNodeKey(nodeData)"
|
||||||
|
>
|
||||||
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
|
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</popConfirm>
|
</popConfirm>
|
||||||
|
@ -145,7 +145,9 @@
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import { mapTree } from '@/utils';
|
import { mapTree } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
|
import { ApiScenarioGetModuleParams } from '@/models/apiTest/scenario';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
@ -231,6 +233,7 @@
|
||||||
{
|
{
|
||||||
label: 'common.rename',
|
label: 'common.rename',
|
||||||
eventTag: 'rename',
|
eventTag: 'rename',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+UPDATE'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
isDivider: true,
|
isDivider: true,
|
||||||
|
@ -238,6 +241,7 @@
|
||||||
{
|
{
|
||||||
label: 'common.delete',
|
label: 'common.delete',
|
||||||
eventTag: 'delete',
|
eventTag: 'delete',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+DELETE'],
|
||||||
danger: true,
|
danger: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -299,28 +303,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initModuleCount() {
|
|
||||||
try {
|
|
||||||
const res = await getModuleCount({
|
|
||||||
keyword: moduleKeyword.value,
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
moduleIds: [],
|
|
||||||
});
|
|
||||||
modulesCount.value = res;
|
|
||||||
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
count: res[node.id] || 0,
|
|
||||||
draggable: !props.readOnly,
|
|
||||||
disabled: props.readOnly ? node.id === selectedKeys.value[0] : false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.isExpandAll,
|
() => props.isExpandAll,
|
||||||
(val) => {
|
(val) => {
|
||||||
|
@ -337,13 +319,10 @@
|
||||||
*/
|
*/
|
||||||
function folderNodeSelect(_selectedKeys: (string | number)[], node: MsTreeNodeData) {
|
function folderNodeSelect(_selectedKeys: (string | number)[], node: MsTreeNodeData) {
|
||||||
const offspringIds: string[] = [];
|
const offspringIds: string[] = [];
|
||||||
if (props.isShowScenario) {
|
mapTree(node.children || [], (e) => {
|
||||||
mapTree(node.children || [], (e) => {
|
offspringIds.push(e.id);
|
||||||
offspringIds.push(e.id);
|
return e;
|
||||||
return e;
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('folderNodeSelect', _selectedKeys, offspringIds);
|
emit('folderNodeSelect', _selectedKeys, offspringIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +345,6 @@
|
||||||
await deleteModule(node.id);
|
await deleteModule(node.id);
|
||||||
Message.success(t('apiScenario.deleteSuccess'));
|
Message.success(t('apiScenario.deleteSuccess'));
|
||||||
await initModules();
|
await initModules();
|
||||||
initModuleCount();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -437,7 +415,6 @@
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
await initModules();
|
await initModules();
|
||||||
initModuleCount();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,18 +425,35 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
await initModules();
|
|
||||||
initModuleCount();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
await initModules();
|
await initModules();
|
||||||
initModuleCount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function initModuleCount(params: ApiScenarioGetModuleParams) {
|
||||||
|
try {
|
||||||
|
const res = await getModuleCount(params);
|
||||||
|
modulesCount.value = res;
|
||||||
|
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
count: res[node.id] || 0,
|
||||||
|
draggable: !props.readOnly,
|
||||||
|
disabled: props.readOnly ? node.id === selectedKeys.value[0] : false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
await initModules();
|
||||||
|
await initModuleCount({
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
});
|
||||||
|
});
|
||||||
defineExpose({
|
defineExpose({
|
||||||
refresh,
|
refresh,
|
||||||
|
initModuleCount,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,885 @@
|
||||||
|
<template>
|
||||||
|
<div :class="['p-[16px_16px]', props.class]">
|
||||||
|
<div class="mb-[16px] flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<span class="flex items-center">
|
||||||
|
<a-switch
|
||||||
|
v-model:model-value="showItemFolderScenario"
|
||||||
|
size="small"
|
||||||
|
type="line"
|
||||||
|
@change="loadScenarioList(false)"
|
||||||
|
/>
|
||||||
|
<span style="margin-left: 8px; color: #323233; font-family: 'PingFang SC'">{{
|
||||||
|
t('apiScenario.table.showChildrenModuleScenario')
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-[8px]">
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('api_scenario.table.searchPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
class="mr-[8px] w-[240px]"
|
||||||
|
@search="loadScenarioList(true)"
|
||||||
|
@press-enter="loadScenarioList"
|
||||||
|
/>
|
||||||
|
<a-button type="outline" class="arco-btn-outline--secondary !p-[8px]" @click="loadScenarioList(true)">
|
||||||
|
<template #icon>
|
||||||
|
<icon-refresh class="text-[var(--color-text-4)]" />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ms-base-table
|
||||||
|
v-bind="propsRes"
|
||||||
|
:action-config="batchActions"
|
||||||
|
:first-column-width="44"
|
||||||
|
no-disable
|
||||||
|
filter-icon-align-left
|
||||||
|
v-on="propsEvent"
|
||||||
|
@selected-change="handleTableSelect"
|
||||||
|
@batch-action="handleTableBatch"
|
||||||
|
>
|
||||||
|
<template #statusFilter="{ columnConfig }">
|
||||||
|
<a-trigger
|
||||||
|
v-model:popup-visible="statusFilterVisible"
|
||||||
|
trigger="click"
|
||||||
|
@popup-visible-change="handleFilterHidden"
|
||||||
|
>
|
||||||
|
<MsButton type="text" class="arco-btn-text--secondary ml-[10px]" @click="statusFilterVisible = true">
|
||||||
|
{{ t(columnConfig.title as string) }}
|
||||||
|
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||||
|
</MsButton>
|
||||||
|
<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="statusFilters" direction="vertical" size="small">
|
||||||
|
<a-checkbox v-for="val of Object.values(ApiScenarioStatus)" :key="val" :value="val">
|
||||||
|
<apiStatus :status="val" />
|
||||||
|
</a-checkbox>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-trigger>
|
||||||
|
</template>
|
||||||
|
<template #num="{ record }">
|
||||||
|
<MsButton type="text" @click="openScenarioTab(record)">{{ record.num }}</MsButton>
|
||||||
|
</template>
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-select
|
||||||
|
v-model:model-value="record.status"
|
||||||
|
class="param-input w-full"
|
||||||
|
@change="() => handleStatusChange(record)"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<apiStatus :status="record.status" />
|
||||||
|
</template>
|
||||||
|
<a-option v-for="item of Object.values(ApiScenarioStatus)" :key="item" :value="item">
|
||||||
|
<apiStatus :status="item" />
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template #priority="{ record }">
|
||||||
|
<a-select
|
||||||
|
v-model:model-value="record.priority"
|
||||||
|
:placeholder="t('common.pleaseSelect')"
|
||||||
|
class="param-input w-full"
|
||||||
|
@change="() => handlePriorityStatusChange(record)"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span class="text-[var(--color-text-2)]">
|
||||||
|
<caseLevel :case-level="record.priority as CaseLevel" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<a-option v-for="item of scenarioPriorityList" :key="item.value" :value="item.value">
|
||||||
|
<caseLevel :case-level="item.text as CaseLevel" />
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template #action="{ record }">
|
||||||
|
<MsButton
|
||||||
|
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
||||||
|
type="text"
|
||||||
|
class="!mr-0"
|
||||||
|
@click="Message.info('// todo @ba1q1')"
|
||||||
|
>
|
||||||
|
{{ t('apiScenario.execute') }}
|
||||||
|
</MsButton>
|
||||||
|
<a-divider v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']" direction="vertical" :margin="8"></a-divider>
|
||||||
|
<MsButton
|
||||||
|
v-permission="['PROJECT_API_SCENARIO:READ+ADD']"
|
||||||
|
type="text"
|
||||||
|
class="!mr-0"
|
||||||
|
@click="Message.info('// todo @ba1q1')"
|
||||||
|
>
|
||||||
|
{{ t('common.copy') }}
|
||||||
|
</MsButton>
|
||||||
|
<a-divider v-permission="['PROJECT_API_SCENARIO:READ+ADD']" direction="vertical" :margin="8"></a-divider>
|
||||||
|
<MsTableMoreAction :list="tableMoreActionList" @select="handleTableMoreActionSelect($event, record)" />
|
||||||
|
</template>
|
||||||
|
</ms-base-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-modal v-model:visible="showBatchModal" title-align="start" class="ms-modal-upload ms-modal-medium" :width="480">
|
||||||
|
<template #title>
|
||||||
|
{{ t('common.batchEdit') }}
|
||||||
|
<div class="text-[var(--color-text-4)]">
|
||||||
|
{{
|
||||||
|
t('api_scenario.table.batchModalSubTitle', {
|
||||||
|
count: batchParams.currentSelectCount || tableSelected.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-form ref="batchFormRef" class="rounded-[4px]" :model="batchForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
field="attr"
|
||||||
|
:label="t('api_scenario.table.chooseAttr')"
|
||||||
|
:rules="[{ required: true, message: t('api_scenario.table.attrRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-select v-model="batchForm.attr" :placeholder="t('common.pleaseSelect')">
|
||||||
|
<a-option v-for="item of fullAttrs" :key="item.value" :value="item.value">
|
||||||
|
{{ t(item.name) }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="batchForm.attr === 'TAGS'"
|
||||||
|
field="values"
|
||||||
|
:label="t('api_scenario.table.batchUpdate')"
|
||||||
|
:validate-trigger="['blur', 'input']"
|
||||||
|
:rules="[{ required: true, message: t('api_scenario.table.valueRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
class="mb-0"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<MsTagsInput
|
||||||
|
v-model:model-value="batchForm.values"
|
||||||
|
placeholder="common.tagsInputPlaceholder"
|
||||||
|
allow-clear
|
||||||
|
unique-value
|
||||||
|
retain-input-value
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-else-if="batchForm.attr === 'Priority'"
|
||||||
|
field="value"
|
||||||
|
:label="t('api_scenario.table.batchUpdate')"
|
||||||
|
:rules="[{ required: true, message: t('api_scenario.table.valueRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
class="mb-0"
|
||||||
|
>
|
||||||
|
<a-select v-model="batchForm.value" :placeholder="t('common.pleaseSelect')">
|
||||||
|
<a-option v-for="item of scenarioPriorityList" :key="item.value" :value="item.value">
|
||||||
|
<caseLevel :case-level="item.text as CaseLevel" />
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-else
|
||||||
|
field="value"
|
||||||
|
:label="t('api_scenario.table.batchUpdate')"
|
||||||
|
:rules="[{ required: true, message: t('api_scenario.table.valueRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
class="mb-0"
|
||||||
|
>
|
||||||
|
<a-select v-model="batchForm.value" :placeholder="t('common.pleaseSelect')" :disabled="batchForm.attr === ''">
|
||||||
|
<a-option v-for="item of valueOptions" :key="item.value" :value="item.value">
|
||||||
|
{{ t(item.name) }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="secondary" :disabled="batchUpdateLoading" @click="cancelBatch">
|
||||||
|
{{ t('common.cancel') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" :loading="batchUpdateLoading" @click="batchUpdate">
|
||||||
|
{{ t('common.update') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
<!-- </MsDialog>-->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="moveModalVisible"
|
||||||
|
title-align="start"
|
||||||
|
class="ms-modal-no-padding ms-modal-small"
|
||||||
|
:mask-closable="false"
|
||||||
|
:ok-text="
|
||||||
|
t('api_scenario.table.batchMoveConfirm', {
|
||||||
|
opt: batchOptionType,
|
||||||
|
count: batchOptionScenarioCount,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:ok-button-props="{ disabled: selectedBatchOptModuleKey === '' }"
|
||||||
|
:cancel-button-props="{ disabled: scenarioBatchOptTreeLoading }"
|
||||||
|
:on-before-ok="handleScenarioTreeOperation"
|
||||||
|
@close="handleCancelScenarioTreeModal"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div v-if="isBatchCopy">
|
||||||
|
{{ t('common.batchCopy') }}
|
||||||
|
<div
|
||||||
|
class="one-line-text ml-[4px] max-w-[100%] text-[var(--color-text-4)]"
|
||||||
|
:title="t('api_scenario.table.batchModalSubTitle', { count: tableSelected.length })"
|
||||||
|
>
|
||||||
|
{{ t('api_scenario.table.batchModalSubTitle', { count: tableSelected.length }) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="isBatchMove">
|
||||||
|
{{ t('common.batchMove') }}
|
||||||
|
<div
|
||||||
|
class="one-line-text ml-[4px] max-w-[100%] text-[var(--color-text-4)]"
|
||||||
|
:title="t('api_scenario.table.batchModalSubTitle', { count: batchOptionScenarioCount })"
|
||||||
|
>
|
||||||
|
{{ t('api_scenario.table.batchModalSubTitle', { count: batchOptionScenarioCount }) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<operationScenarioModuleTree
|
||||||
|
v-if="moveModalVisible"
|
||||||
|
:is-expand-all="true"
|
||||||
|
:is-modal="true"
|
||||||
|
:active-module="props.activeModule"
|
||||||
|
@folder-node-select="folderNodeSelect"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
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 { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||||
|
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||||
|
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||||
|
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
|
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||||
|
import operationScenarioModuleTree from '@/views/api-test/scenario/components/operationScenarioModuleTree.vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
batchEditScenario,
|
||||||
|
batchOptionScenario,
|
||||||
|
batchRecycleScenario,
|
||||||
|
getScenarioPage,
|
||||||
|
recycleScenario,
|
||||||
|
updateScenario,
|
||||||
|
} from '@/api/modules/api-test/scenario';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useModal from '@/hooks/useModal';
|
||||||
|
import useTableStore from '@/hooks/useTableStore';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
|
import { ApiScenarioDetail, ApiScenarioUpdateDTO } from '@/models/apiTest/scenario';
|
||||||
|
import { ApiScenarioStatus } from '@/enums/apiEnum';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: string;
|
||||||
|
activeModule: string;
|
||||||
|
offspringIds: string[];
|
||||||
|
readOnly?: boolean; // 是否是只读模式
|
||||||
|
}>();
|
||||||
|
// 场景属性
|
||||||
|
const showItemFolderScenario = ref(false);
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openModal } = useModal();
|
||||||
|
const scenarioPriorityList = ref([
|
||||||
|
{
|
||||||
|
value: 'P0',
|
||||||
|
text: 'P0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'P1',
|
||||||
|
text: 'P1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'P2',
|
||||||
|
text: 'P2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'P3',
|
||||||
|
text: 'P3',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const folderTreePathMap = inject('folderTreePathMap');
|
||||||
|
const emit = defineEmits(['refreshModuleTree']);
|
||||||
|
const keyword = ref('');
|
||||||
|
const moveModalVisible = ref(false);
|
||||||
|
const isBatchMove = ref(false); // 是否批量移动场景
|
||||||
|
const isBatchCopy = ref(false); // 是否批量复制场景
|
||||||
|
|
||||||
|
let columns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'num',
|
||||||
|
slotName: 'num',
|
||||||
|
sortIndex: 1,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
fixed: 'left',
|
||||||
|
width: 126,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
showTooltip: true,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
width: 134,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.level',
|
||||||
|
dataIndex: 'priority',
|
||||||
|
slotName: 'priority',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.status',
|
||||||
|
dataIndex: 'status',
|
||||||
|
slotName: 'status',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.runResult',
|
||||||
|
titleSlotName: 'lastReportStatus',
|
||||||
|
dataIndex: 'lastReportStatus',
|
||||||
|
showTooltip: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.tags',
|
||||||
|
dataIndex: 'tags',
|
||||||
|
isTag: true,
|
||||||
|
isStringTag: true,
|
||||||
|
width: 456,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.scenarioEnv',
|
||||||
|
dataIndex: 'environmentName',
|
||||||
|
width: 159,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.steps',
|
||||||
|
dataIndex: 'stepTotal',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.passRate',
|
||||||
|
dataIndex: 'requestPassRate',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.module',
|
||||||
|
dataIndex: 'modulePath',
|
||||||
|
width: 176,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.createTime',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
width: 189,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.updateTime',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
width: 189,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.createUser',
|
||||||
|
dataIndex: 'createUser',
|
||||||
|
titleSlotName: 'createUser',
|
||||||
|
width: 109,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'apiScenario.table.columns.updateUser',
|
||||||
|
dataIndex: 'updateUser',
|
||||||
|
titleSlotName: 'updateUser',
|
||||||
|
width: 109,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'common.operation',
|
||||||
|
slotName: 'action',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
|
||||||
|
getScenarioPage,
|
||||||
|
{
|
||||||
|
columns: props.readOnly ? columns : [],
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
tableKey: props.readOnly ? undefined : TableKeyEnum.API_TEST,
|
||||||
|
showSetting: !props.readOnly,
|
||||||
|
selectable: true,
|
||||||
|
showSelectAll: !props.readOnly,
|
||||||
|
draggable: props.readOnly ? undefined : { type: 'handle', width: 32 },
|
||||||
|
heightUsed: 374,
|
||||||
|
},
|
||||||
|
(item) => ({
|
||||||
|
...item,
|
||||||
|
fullPath: folderTreePathMap?.[item.moduleId],
|
||||||
|
createTime: dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
updateTime: dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const batchActions = {
|
||||||
|
baseAction: [
|
||||||
|
{
|
||||||
|
label: 'common.edit',
|
||||||
|
eventTag: 'edit',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+UPDATE'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'apiScenario.execute',
|
||||||
|
eventTag: 'execute',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+EXECUTE'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'msTable.batch.moveTo',
|
||||||
|
eventTag: 'moveTo',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+UPDATE'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'msTable.batch.copyTo',
|
||||||
|
eventTag: 'copyTo',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+ADD'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
moreAction: [
|
||||||
|
{
|
||||||
|
label: 'common.delete',
|
||||||
|
eventTag: 'delete',
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+DELETE'],
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const tableMoreActionList = [
|
||||||
|
{
|
||||||
|
eventTag: 'delete',
|
||||||
|
label: t('common.delete'),
|
||||||
|
permission: ['PROJECT_API_SCENARIO:READ+DELETE'],
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const statusFilterVisible = ref(false);
|
||||||
|
const statusFilters = ref(Object.keys(ApiScenarioStatus));
|
||||||
|
const moduleIds = computed(() => {
|
||||||
|
if (props.activeModule === 'all') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (showItemFolderScenario.value) {
|
||||||
|
return [props.activeModule, ...props.offspringIds];
|
||||||
|
}
|
||||||
|
return [props.activeModule];
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadScenarioList(refreshTreeCount?: boolean) {
|
||||||
|
const params = {
|
||||||
|
keyword: keyword.value,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: moduleIds.value,
|
||||||
|
filter: {
|
||||||
|
status: statusFilters.value.length === Object.keys(ApiScenarioStatus).length ? undefined : statusFilters.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
setLoadListParams(params);
|
||||||
|
loadList();
|
||||||
|
if (refreshTreeCount) {
|
||||||
|
emit('refreshModuleTree', params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFilterHidden(val: boolean) {
|
||||||
|
if (!val) {
|
||||||
|
loadScenarioList(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleStatusChange(record: ApiScenarioUpdateDTO) {
|
||||||
|
try {
|
||||||
|
await updateScenario({
|
||||||
|
id: record.id,
|
||||||
|
status: record.status,
|
||||||
|
});
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handlePriorityStatusChange(record: ApiScenarioUpdateDTO) {
|
||||||
|
try {
|
||||||
|
await updateScenario({
|
||||||
|
id: record.id,
|
||||||
|
priority: record.priority,
|
||||||
|
});
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableSelected = ref<(string | number)[]>([]);
|
||||||
|
|
||||||
|
const batchParams = ref<BatchActionQueryParams>({
|
||||||
|
selectedIds: [],
|
||||||
|
selectAll: false,
|
||||||
|
excludeIds: [],
|
||||||
|
currentSelectCount: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除接口
|
||||||
|
*/
|
||||||
|
function deleteScenario(record?: ApiScenarioDetail, isBatch?: boolean, params?: BatchActionQueryParams) {
|
||||||
|
let title = t('api_scenario.table.deleteScenarioTipTitle', { name: record?.name });
|
||||||
|
let selectIds = [record?.id || ''];
|
||||||
|
if (isBatch) {
|
||||||
|
title = t('api_scenario.table.batchDeleteScenarioTip', {
|
||||||
|
count: params?.currentSelectCount || tableSelected.value.length,
|
||||||
|
});
|
||||||
|
selectIds = tableSelected.value as string[];
|
||||||
|
}
|
||||||
|
openModal({
|
||||||
|
type: 'error',
|
||||||
|
title,
|
||||||
|
content: t('api_scenario.table.deleteScenarioTip'),
|
||||||
|
okText: t('common.confirmDelete'),
|
||||||
|
cancelText: t('common.cancel'),
|
||||||
|
okButtonProps: {
|
||||||
|
status: 'danger',
|
||||||
|
},
|
||||||
|
maskClosable: false,
|
||||||
|
onBeforeOk: async () => {
|
||||||
|
try {
|
||||||
|
if (isBatch) {
|
||||||
|
await batchRecycleScenario({
|
||||||
|
selectIds,
|
||||||
|
selectAll: !!params?.selectAll,
|
||||||
|
excludeIds: params?.excludeIds || [],
|
||||||
|
condition: { keyword: keyword.value },
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: props.activeModule === 'all' ? [] : [props.activeModule],
|
||||||
|
deleteAll: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await recycleScenario(record?.id as string);
|
||||||
|
}
|
||||||
|
Message.success(t('common.deleteSuccess'));
|
||||||
|
resetSelector();
|
||||||
|
loadScenarioList(true);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideCancel: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理表格更多按钮事件
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
function handleTableMoreActionSelect(item: ActionsItem, record: ApiScenarioDetail) {
|
||||||
|
switch (item.eventTag) {
|
||||||
|
case 'delete':
|
||||||
|
deleteScenario(record);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理表格选中
|
||||||
|
*/
|
||||||
|
function handleTableSelect(arr: (string | number)[]) {
|
||||||
|
tableSelected.value = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showBatchModal = ref(false);
|
||||||
|
const batchUpdateLoading = ref(false);
|
||||||
|
const batchFormRef = ref<FormInstance>();
|
||||||
|
const batchForm = ref({
|
||||||
|
attr: '',
|
||||||
|
value: '',
|
||||||
|
values: [],
|
||||||
|
});
|
||||||
|
const fullAttrs = [
|
||||||
|
{
|
||||||
|
name: 'apiScenario.table.columns.level',
|
||||||
|
value: 'Priority',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'api_scenario.table.apiStatus',
|
||||||
|
value: 'Status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '标签(待定)',
|
||||||
|
value: 'Tags',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '环境(待定)',
|
||||||
|
value: 'Environment',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const valueOptions = computed(() => {
|
||||||
|
switch (batchForm.value.attr) {
|
||||||
|
case 'Status':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'api_scenario.table.status.underway',
|
||||||
|
value: ApiScenarioStatus.UNDERWAY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'api_scenario.table.status.completed',
|
||||||
|
value: ApiScenarioStatus.COMPLETED,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'api_scenario.table.status.deprecate',
|
||||||
|
value: ApiScenarioStatus.DEPRECATED,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function cancelBatch() {
|
||||||
|
showBatchModal.value = false;
|
||||||
|
batchFormRef.value?.resetFields();
|
||||||
|
batchForm.value = {
|
||||||
|
attr: '',
|
||||||
|
value: '',
|
||||||
|
values: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function batchUpdate() {
|
||||||
|
batchFormRef.value?.validate(async (errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
batchUpdateLoading.value = true;
|
||||||
|
|
||||||
|
// value: 'PRIORITY',
|
||||||
|
// value: 'STATUS',
|
||||||
|
const batchEditParam = {
|
||||||
|
selectIds: batchParams.value?.selectedIds || [],
|
||||||
|
selectAll: !!batchParams.value?.selectAll,
|
||||||
|
excludeIds: batchParams.value?.excludeIds || [],
|
||||||
|
condition: { keyword: keyword.value },
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: props.activeModule === 'all' ? [] : [props.activeModule],
|
||||||
|
type: batchForm.value?.attr,
|
||||||
|
priority: '',
|
||||||
|
status: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (batchForm.value.attr === 'Priority') {
|
||||||
|
batchEditParam.priority = batchForm.value.value;
|
||||||
|
} else if (batchForm.value.attr === 'Status') {
|
||||||
|
batchEditParam.status = batchForm.value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
await batchEditScenario(batchEditParam);
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
cancelBatch();
|
||||||
|
resetSelector();
|
||||||
|
loadScenarioList(true);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
batchUpdateLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedBatchOptModuleKey = ref(''); // 移动文件选中节点
|
||||||
|
const selectedBatchOptModuleName = ref(''); // 移动文件选中节点 用于页面文案显示
|
||||||
|
const batchOptionType = ref(''); // 批量操作类型 用于页面提示语
|
||||||
|
const batchOptionScenarioCount = ref<number>(0);
|
||||||
|
const activeScenario = ref<ApiScenarioDetail | null>(null); // 当前查看的接口项
|
||||||
|
const scenarioBatchOptTreeLoading = ref(false); // 批量移动文件loading
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量接口
|
||||||
|
*/
|
||||||
|
async function handleScenarioTreeOperation() {
|
||||||
|
try {
|
||||||
|
scenarioBatchOptTreeLoading.value = true;
|
||||||
|
let optionType = '';
|
||||||
|
if (isBatchMove.value) {
|
||||||
|
optionType = 'batchMove';
|
||||||
|
} else if (isBatchCopy.value) {
|
||||||
|
optionType = 'batchCopy';
|
||||||
|
}
|
||||||
|
await batchOptionScenario(optionType, {
|
||||||
|
selectIds: batchParams.value?.selectedIds || [],
|
||||||
|
selectAll: !!batchParams.value?.selectAll,
|
||||||
|
excludeIds: batchParams.value?.excludeIds || [],
|
||||||
|
condition: { keyword: keyword.value },
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: props.activeModule === 'all' ? [] : [props.activeModule],
|
||||||
|
targetModuleId: selectedBatchOptModuleKey.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
Message.success(
|
||||||
|
t('api_scenario.batch_operation.success', {
|
||||||
|
opt: batchOptionType.value,
|
||||||
|
name: selectedBatchOptModuleName?.value,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
tableSelected.value = [];
|
||||||
|
activeScenario.value = null;
|
||||||
|
loadScenarioList(true);
|
||||||
|
resetSelector();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
scenarioBatchOptTreeLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelScenarioTreeModal() {
|
||||||
|
moveModalVisible.value = false;
|
||||||
|
isBatchMove.value = false;
|
||||||
|
isBatchCopy.value = false;
|
||||||
|
selectedBatchOptModuleKey.value = '';
|
||||||
|
selectedBatchOptModuleName.value = '';
|
||||||
|
batchOptionType.value = '';
|
||||||
|
batchOptionScenarioCount.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理文件夹树节点选中事件
|
||||||
|
*/
|
||||||
|
function folderNodeSelect(node: MsTreeNodeData) {
|
||||||
|
selectedBatchOptModuleKey.value = node.id;
|
||||||
|
if (node.name != null) {
|
||||||
|
selectedBatchOptModuleName.value = node.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理表格选中后批量操作
|
||||||
|
* @param event 批量操作事件对象
|
||||||
|
*/
|
||||||
|
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||||
|
tableSelected.value = params?.selectedIds || [];
|
||||||
|
batchParams.value = params;
|
||||||
|
switch (event.eventTag) {
|
||||||
|
case 'delete':
|
||||||
|
deleteScenario(undefined, true, batchParams.value);
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
showBatchModal.value = true;
|
||||||
|
break;
|
||||||
|
case 'moveTo':
|
||||||
|
batchOptionType.value = t('common.move');
|
||||||
|
if (params.currentSelectCount != null) {
|
||||||
|
batchOptionScenarioCount.value = params.currentSelectCount;
|
||||||
|
}
|
||||||
|
isBatchMove.value = true;
|
||||||
|
moveModalVisible.value = true;
|
||||||
|
break;
|
||||||
|
case 'copyTo':
|
||||||
|
batchOptionType.value = t('common.copy');
|
||||||
|
if (params.currentSelectCount != null) {
|
||||||
|
batchOptionScenarioCount.value = params.currentSelectCount;
|
||||||
|
}
|
||||||
|
isBatchCopy.value = true;
|
||||||
|
moveModalVisible.value = true;
|
||||||
|
break;
|
||||||
|
case 'execute':
|
||||||
|
Message.info('// todo @ba1q1');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openScenarioTab(record: ApiScenarioDetail) {
|
||||||
|
Message.info('// todo @ba1q1');
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
loadScenarioList,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!props.readOnly) {
|
||||||
|
const tableStore = useTableStore();
|
||||||
|
await tableStore.initColumn(TableKeyEnum.API_TEST, columns, 'drawer', true);
|
||||||
|
} else {
|
||||||
|
columns = columns.filter(
|
||||||
|
(item) => !['version', 'createTime', 'updateTime', 'operation'].includes(item.dataIndex as string)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
loadScenarioList();
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => props.activeModule,
|
||||||
|
() => {
|
||||||
|
resetSelector();
|
||||||
|
loadScenarioList();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => batchForm.value.attr,
|
||||||
|
() => {
|
||||||
|
batchForm.value.value = '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.param-input:not(.arco-input-focus, .arco-select-view-focus)) {
|
||||||
|
&:not(:hover) {
|
||||||
|
border-color: transparent !important;
|
||||||
|
.arco-input::placeholder {
|
||||||
|
@apply invisible;
|
||||||
|
}
|
||||||
|
.arco-select-view-icon {
|
||||||
|
@apply invisible;
|
||||||
|
}
|
||||||
|
.arco-select-view-value {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -43,9 +43,12 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #second>
|
<template #second>
|
||||||
<div class="p-[24px]">
|
<ScenarioTable
|
||||||
<!-- <apiTable ref="apiTableRef" :active-module="activeFolder" :offspring-ids="offspringIds" />-->
|
ref="apiTableRef"
|
||||||
</div>
|
:active-module="activeModule"
|
||||||
|
:offspring-ids="offspringIds"
|
||||||
|
@refresh-module-tree="refreshTree"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</MsSplitBox>
|
</MsSplitBox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,10 +69,11 @@
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
import scenarioModuleTree from './components/scenarioModuleTree.vue';
|
import scenarioModuleTree from './components/scenarioModuleTree.vue';
|
||||||
import detail from './detail/index.vue';
|
import detail from './detail/index.vue';
|
||||||
import ApiTable from '@/views/api-test/management/components/management/api/apiTable.vue';
|
import ScenarioTable from '@/views/api-test/scenario/components/scenarioTable.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import { ApiScenarioGetModuleParams } from '@/models/apiTest/scenario';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -94,10 +98,10 @@
|
||||||
|
|
||||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
const folderTreePathMap = ref<Record<string, any>>({});
|
const folderTreePathMap = ref<Record<string, any>>({});
|
||||||
const activeFolder = ref<string>('all');
|
|
||||||
const activeModule = ref<string>('all');
|
const activeModule = ref<string>('all');
|
||||||
|
const activeFolder = ref<string>('all');
|
||||||
const offspringIds = ref<string[]>([]);
|
const offspringIds = ref<string[]>([]);
|
||||||
|
const recycleModulesCount = ref(0);
|
||||||
const isShowScenario = ref(false);
|
const isShowScenario = ref(false);
|
||||||
|
|
||||||
// 获取激活用例类型样式
|
// 获取激活用例类型样式
|
||||||
|
@ -105,9 +109,9 @@
|
||||||
return activeFolder.value === type ? 'folder-text case-active' : 'folder-text';
|
return activeFolder.value === type ? 'folder-text case-active' : 'folder-text';
|
||||||
};
|
};
|
||||||
|
|
||||||
const scenarioModuleTreeRef = ref();
|
const scenarioModuleTreeRef = ref<InstanceType<typeof scenarioModuleTree>>();
|
||||||
|
|
||||||
function handleModuleInit(tree, _protocol: string, pathMap: Record<string, any>) {
|
function handleModuleInit(tree: any, _protocol: string, pathMap: Record<string, any>) {
|
||||||
folderTree.value = tree;
|
folderTree.value = tree;
|
||||||
folderTreePathMap.value = pathMap;
|
folderTreePathMap.value = pathMap;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +121,10 @@
|
||||||
offspringIds.value = _offspringIds;
|
offspringIds.value = _offspringIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshTree(params: ApiScenarioGetModuleParams) {
|
||||||
|
scenarioModuleTreeRef.value?.initModuleCount(params);
|
||||||
|
}
|
||||||
|
|
||||||
function redirectRecycle() {}
|
function redirectRecycle() {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,25 @@ export default {
|
||||||
'apiScenario.params.paramValue': 'Parameter value',
|
'apiScenario.params.paramValue': 'Parameter value',
|
||||||
'apiScenario.params.tag': 'Tag',
|
'apiScenario.params.tag': 'Tag',
|
||||||
'apiScenario.params.desc': 'Description',
|
'apiScenario.params.desc': 'Description',
|
||||||
|
'apiScenario.table.showChildrenModuleScenario': 'Show subdirectory scenarios',
|
||||||
|
|
||||||
|
'apiScenario.table.columns.name': 'Name',
|
||||||
|
'apiScenario.table.columns.level': 'Level',
|
||||||
|
'apiScenario.table.columns.status': 'status',
|
||||||
|
'apiScenario.table.columns.runResult': 'Run result',
|
||||||
|
'apiScenario.table.columns.tags': 'Tags',
|
||||||
|
'apiScenario.table.columns.scenarioEnv': 'Scenario environment',
|
||||||
|
'apiScenario.table.columns.steps': 'Steps',
|
||||||
|
'apiScenario.table.columns.passRate': 'Pass rate',
|
||||||
|
'apiScenario.table.columns.module': 'Module',
|
||||||
|
'apiScenario.table.columns.createUser': 'Create user',
|
||||||
|
'apiScenario.table.columns.createTime': 'Create time',
|
||||||
|
'apiScenario.table.columns.updateUser': 'Update user',
|
||||||
|
'apiScenario.table.columns.updateTime': 'Update time',
|
||||||
|
|
||||||
|
'apiScenario.execute': 'Execute',
|
||||||
|
|
||||||
|
// 批量操作文案
|
||||||
|
'api_scenario.batch_operation.success': 'Success {opt} to {name}',
|
||||||
|
'api_scenario.table.batchMoveConfirm': 'Ready to {opt} {count} scenarios',
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,4 +31,39 @@ export default {
|
||||||
'apiScenario.params.paramValue': '参数值',
|
'apiScenario.params.paramValue': '参数值',
|
||||||
'apiScenario.params.tag': '标签',
|
'apiScenario.params.tag': '标签',
|
||||||
'apiScenario.params.desc': '描述',
|
'apiScenario.params.desc': '描述',
|
||||||
|
'apiScenario.table.showChildrenModuleScenario': '显示子目录场景',
|
||||||
|
|
||||||
|
'apiScenario.table.columns.name': '场景名称',
|
||||||
|
'apiScenario.table.columns.level': '场景等级',
|
||||||
|
'apiScenario.table.columns.status': '状态',
|
||||||
|
'apiScenario.table.columns.runResult': '运行结果',
|
||||||
|
'apiScenario.table.columns.tags': '标签',
|
||||||
|
'apiScenario.table.columns.scenarioEnv': '场景环境',
|
||||||
|
'apiScenario.table.columns.steps': '步骤数',
|
||||||
|
'apiScenario.table.columns.passRate': '通过率',
|
||||||
|
'apiScenario.table.columns.module': '所属模块',
|
||||||
|
'apiScenario.table.columns.createUser': '创建人',
|
||||||
|
'apiScenario.table.columns.createTime': '创建时间',
|
||||||
|
'apiScenario.table.columns.updateUser': '更新人',
|
||||||
|
'apiScenario.table.columns.updateTime': '更新时间',
|
||||||
|
|
||||||
|
'api_scenario.table.searchPlaceholder': '通过 ID/名称/标签搜索',
|
||||||
|
'api_scenario.table.batchModalSubTitle': '(已选 {count} 个场景)',
|
||||||
|
'api_scenario.table.chooseAttr': '选择属性',
|
||||||
|
'api_scenario.table.attrRequired': '属性不能为空',
|
||||||
|
'api_scenario.table.batchUpdate': '批量更新为',
|
||||||
|
'api_scenario.table.valueRequired': '属性值不能为空',
|
||||||
|
'api_scenario.table.deleteScenarioTipTitle': '确认删除 {name} 吗?',
|
||||||
|
'api_scenario.table.batchDeleteScenarioTip': '确认删除已选中的 {count} 个场景吗?',
|
||||||
|
'api_scenario.table.deleteScenarioTip': '删除后,可能导致引用该场景的资源执行失败,请谨慎操作!',
|
||||||
|
'api_scenario.table.apiStatus': '状态',
|
||||||
|
'api_scenario.table.status.underway': '进行中',
|
||||||
|
'api_scenario.table.status.completed': '已完成',
|
||||||
|
'api_scenario.table.status.deprecate': '已废弃',
|
||||||
|
|
||||||
|
'apiScenario.execute': '执行',
|
||||||
|
|
||||||
|
// 批量操作文案
|
||||||
|
'api_scenario.batch_operation.success': '成功{opt}至 {name}',
|
||||||
|
'api_scenario.table.batchMoveConfirm': '{opt}{count}个场景至已选模块',
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue