feat(接口场景): 场景大框架&接口定义列表执行
This commit is contained in:
parent
f8343ea765
commit
1c86838b8b
|
@ -32,7 +32,12 @@
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MsButton type="text" class="more-btn" @click="toggleExpand">
|
<MsButton
|
||||||
|
v-if="props.simpleShowCount !== undefined && props.simpleShowCount > 0"
|
||||||
|
type="text"
|
||||||
|
class="more-btn"
|
||||||
|
@click="toggleExpand"
|
||||||
|
>
|
||||||
<div v-if="isExpand" class="flex items-center gap-[4px]">
|
<div v-if="isExpand" class="flex items-center gap-[4px]">
|
||||||
{{ t('msDetailCard.collapse') }}
|
{{ t('msDetailCard.collapse') }}
|
||||||
<icon-up :size="14" />
|
<icon-up :size="14" />
|
||||||
|
|
|
@ -59,11 +59,41 @@ export const pathMap: PathMapItem[] = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'API_TEST_MANAGEMENT', // 接口测试-接口管理
|
key: 'API_TEST_MANAGEMENT', // 接口测试-接口定义
|
||||||
locale: 'menu.apiTest.management',
|
locale: 'menu.apiTest.management',
|
||||||
route: RouteEnum.API_TEST_MANAGEMENT,
|
route: RouteEnum.API_TEST_MANAGEMENT,
|
||||||
permission: [],
|
permission: [],
|
||||||
level: MENU_LEVEL[2],
|
level: MENU_LEVEL[2],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'API_TEST_MANAGEMENT_MODULE', // 接口测试-接口定义-模块
|
||||||
|
locale: 'common.module',
|
||||||
|
route: RouteEnum.API_TEST_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'API_TEST_MANAGEMENT_DEFINITION', // 接口测试-接口定义
|
||||||
|
locale: 'menu.apiTest.management.definition',
|
||||||
|
route: RouteEnum.API_TEST_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'API_TEST_MANAGEMENT_MOCK', // 接口测试-接口定义-mock
|
||||||
|
locale: 'MOCK',
|
||||||
|
route: RouteEnum.API_TEST_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'API_TEST_MANAGEMENT_CASE', // 接口测试-接口定义-case
|
||||||
|
locale: 'CASE',
|
||||||
|
route: RouteEnum.API_TEST_MANAGEMENT,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'API_TEST_REPORT', // 接口测试-接口测试报告
|
key: 'API_TEST_REPORT', // 接口测试-接口测试报告
|
||||||
|
|
|
@ -213,3 +213,19 @@ export enum RequestCaseStatus {
|
||||||
PROCESSING = 'PROCESSING',
|
PROCESSING = 'PROCESSING',
|
||||||
DONE = 'DONE',
|
DONE = 'DONE',
|
||||||
}
|
}
|
||||||
|
// 创建接口场景组成部分
|
||||||
|
export enum ScenarioCreateComposition {
|
||||||
|
STEP = 'STEP',
|
||||||
|
PARAMS = 'PARAMS',
|
||||||
|
PRE_POST = 'PRE_POST',
|
||||||
|
ASSERTION = 'ASSERTION',
|
||||||
|
SETTING = 'SETTING',
|
||||||
|
}
|
||||||
|
// 接口场景详情组成部分
|
||||||
|
export enum ScenarioDetailComposition {
|
||||||
|
BASE_INFO = 'BASE_INFO',
|
||||||
|
EXECUTE_HISTORY = 'EXECUTE_HISTORY',
|
||||||
|
CHANGE_HISTORY = 'CHANGE_HISTORY',
|
||||||
|
DEPENDENCY = 'DEPENDENCY',
|
||||||
|
QUOTE = 'QUOTE',
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ export default {
|
||||||
'menu.apiTest.debug': 'API debug',
|
'menu.apiTest.debug': 'API debug',
|
||||||
'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.scenario': 'API Scenario',
|
'menu.apiTest.scenario': 'API Scenario',
|
||||||
'menu.apiTest.report': 'API Report',
|
'menu.apiTest.report': 'API Report',
|
||||||
'menu.uiTest': 'UI Test',
|
'menu.uiTest': 'UI Test',
|
||||||
|
|
|
@ -27,6 +27,7 @@ export default {
|
||||||
'menu.apiTest.debug': '接口调试',
|
'menu.apiTest.debug': '接口调试',
|
||||||
'menu.apiTest.debug.debug': '调试',
|
'menu.apiTest.debug.debug': '调试',
|
||||||
'menu.apiTest.management': '接口管理',
|
'menu.apiTest.management': '接口管理',
|
||||||
|
'menu.apiTest.management.definition': '接口定义',
|
||||||
'menu.apiTest.api': 'API列表',
|
'menu.apiTest.api': 'API列表',
|
||||||
'menu.apiTest.scenario': '接口场景',
|
'menu.apiTest.scenario': '接口场景',
|
||||||
'menu.apiTest.report': '接口报告',
|
'menu.apiTest.report': '接口报告',
|
||||||
|
|
|
@ -483,6 +483,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// TODO:代码拆分,结构优化
|
||||||
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
||||||
import { cloneDeep, debounce } from 'lodash-es';
|
import { cloneDeep, debounce } from 'lodash-es';
|
||||||
|
|
||||||
|
@ -562,6 +563,7 @@
|
||||||
mode?: 'definition' | 'debug';
|
mode?: 'definition' | 'debug';
|
||||||
executeLoading: boolean; // 执行中loading
|
executeLoading: boolean; // 执行中loading
|
||||||
isCopy?: boolean; // 是否是复制
|
isCopy?: boolean; // 是否是复制
|
||||||
|
isExecute?: boolean; // 是否是执行
|
||||||
}
|
}
|
||||||
export type RequestParam = ExecuteApiRequestFullParams & {
|
export type RequestParam = ExecuteApiRequestFullParams & {
|
||||||
responseDefinition?: ResponseItem[];
|
responseDefinition?: ResponseItem[];
|
||||||
|
@ -603,18 +605,6 @@
|
||||||
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||||
const isInitPluginForm = ref(false);
|
const isInitPluginForm = ref(false);
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.request.id,
|
|
||||||
() => {
|
|
||||||
if (temporaryResponseMap[props.request.reportId]) {
|
|
||||||
// 如果有缓存的报告未读取,则直接赋值
|
|
||||||
requestVModel.value.response = temporaryResponseMap[props.request.reportId];
|
|
||||||
requestVModel.value.executeLoading = false;
|
|
||||||
delete temporaryResponseMap[props.request.reportId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function handleActiveDebugChange() {
|
function handleActiveDebugChange() {
|
||||||
if (!loading.value || (!isHttpProtocol.value && isInitPluginForm.value)) {
|
if (!loading.value || (!isHttpProtocol.value && isInitPluginForm.value)) {
|
||||||
// 如果是因为加载详情触发的change则不需要标记为未保存;或者是插件协议的话需要等待表单初始化完毕
|
// 如果是因为加载详情触发的change则不需要标记为未保存;或者是插件协议的话需要等待表单初始化完毕
|
||||||
|
@ -878,25 +868,6 @@
|
||||||
handleActiveDebugChange();
|
handleActiveDebugChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => requestVModel.value.id,
|
|
||||||
async () => {
|
|
||||||
if (requestVModel.value.protocol !== 'HTTP') {
|
|
||||||
requestVModel.value.activeTab = RequestComposition.PLUGIN;
|
|
||||||
if (protocolOptions.value.length === 0) {
|
|
||||||
// 还没初始化过协议列表,则初始化;在这里初始化是为了阻塞脚本的初始化,避免脚本初始化时协议列表还没初始化
|
|
||||||
await initProtocolList();
|
|
||||||
}
|
|
||||||
initPluginScript();
|
|
||||||
} else {
|
|
||||||
initProtocolList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理url输入框变化,解析成参数表格
|
* 处理url输入框变化,解析成参数表格
|
||||||
*/
|
*/
|
||||||
|
@ -994,35 +965,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reportId = ref('');
|
|
||||||
const websocket = ref<WebSocket>();
|
|
||||||
/**
|
|
||||||
* 开启websocket监听,接收执行结果
|
|
||||||
*/
|
|
||||||
function debugSocket(executeType?: 'localExec' | 'serverExec') {
|
|
||||||
websocket.value = getSocket(
|
|
||||||
reportId.value,
|
|
||||||
executeType === 'localExec' ? '/ws/debug' : '',
|
|
||||||
executeType === 'localExec' ? localExecuteUrl.value : ''
|
|
||||||
);
|
|
||||||
websocket.value.addEventListener('message', (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.msgType === 'EXEC_RESULT') {
|
|
||||||
if (requestVModel.value.reportId === data.reportId) {
|
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
|
||||||
requestVModel.value.response = data.taskResult;
|
|
||||||
requestVModel.value.executeLoading = false;
|
|
||||||
} else {
|
|
||||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
|
||||||
temporaryResponseMap[data.reportId] = data.taskResult;
|
|
||||||
}
|
|
||||||
} else if (data.msgType === 'EXEC_END') {
|
|
||||||
// 执行结束,关闭websocket
|
|
||||||
websocket.value?.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveModalVisible = ref(false);
|
const saveModalVisible = ref(false);
|
||||||
const saveModalForm = ref({
|
const saveModalForm = ref({
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -1061,6 +1003,38 @@
|
||||||
return conditionCopy;
|
return conditionCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reportId = ref('');
|
||||||
|
const websocket = ref<WebSocket>();
|
||||||
|
/**
|
||||||
|
* 开启websocket监听,接收执行结果
|
||||||
|
*/
|
||||||
|
function debugSocket(executeType?: 'localExec' | 'serverExec') {
|
||||||
|
websocket.value = getSocket(
|
||||||
|
reportId.value,
|
||||||
|
executeType === 'localExec' ? '/ws/debug' : '',
|
||||||
|
executeType === 'localExec' ? localExecuteUrl.value : ''
|
||||||
|
);
|
||||||
|
websocket.value.addEventListener('message', (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (data.msgType === 'EXEC_RESULT') {
|
||||||
|
if (requestVModel.value.reportId === data.reportId) {
|
||||||
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
|
requestVModel.value.response = data.taskResult;
|
||||||
|
requestVModel.value.executeLoading = false;
|
||||||
|
requestVModel.value.isExecute = false;
|
||||||
|
} else {
|
||||||
|
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||||
|
temporaryResponseMap[data.reportId] = data.taskResult;
|
||||||
|
}
|
||||||
|
} else if (data.msgType === 'EXEC_END') {
|
||||||
|
// 执行结束,关闭websocket
|
||||||
|
websocket.value?.close();
|
||||||
|
requestVModel.value.executeLoading = false;
|
||||||
|
requestVModel.value.isExecute = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成请求参数
|
* 生成请求参数
|
||||||
* @param executeType 执行类型,执行时传入
|
* @param executeType 执行类型,执行时传入
|
||||||
|
@ -1214,6 +1188,34 @@
|
||||||
requestVModel.value.executeLoading = false;
|
requestVModel.value.executeLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => requestVModel.value.id,
|
||||||
|
async () => {
|
||||||
|
if (requestVModel.value.protocol !== 'HTTP') {
|
||||||
|
requestVModel.value.activeTab = RequestComposition.PLUGIN;
|
||||||
|
if (protocolOptions.value.length === 0) {
|
||||||
|
// 还没初始化过协议列表,则初始化;在这里初始化是为了阻塞脚本的初始化,避免脚本初始化时协议列表还没初始化
|
||||||
|
await initProtocolList();
|
||||||
|
}
|
||||||
|
await initPluginScript();
|
||||||
|
} else {
|
||||||
|
await initProtocolList();
|
||||||
|
}
|
||||||
|
if (props.request.isExecute && !requestVModel.value.executeLoading) {
|
||||||
|
// 如果是执行操作打开接口详情,且该接口不在执行状态中,则立即执行
|
||||||
|
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
|
||||||
|
} else if (temporaryResponseMap[props.request.reportId]) {
|
||||||
|
// 如果有缓存的报告未读取,则直接赋值
|
||||||
|
requestVModel.value.response = temporaryResponseMap[props.request.reportId];
|
||||||
|
requestVModel.value.executeLoading = false;
|
||||||
|
delete temporaryResponseMap[props.request.reportId];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
async function updateRequest() {
|
async function updateRequest() {
|
||||||
try {
|
try {
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
|
@ -1266,7 +1268,6 @@
|
||||||
requestVModel.value.label = res.name;
|
requestVModel.value.label = res.name;
|
||||||
requestVModel.value.url = res.path;
|
requestVModel.value.url = res.path;
|
||||||
requestVModel.value.path = res.path;
|
requestVModel.value.path = res.path;
|
||||||
console.log('requestVModel.value', requestVModel.value);
|
|
||||||
if (!props.isDefinition) {
|
if (!props.isDefinition) {
|
||||||
saveModalVisible.value = false;
|
saveModalVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,7 +265,12 @@
|
||||||
document.querySelector(`#renameSpan${_tab.id}`)?.dispatchEvent(new Event('click'));
|
document.querySelector(`#renameSpan${_tab.id}`)?.dispatchEvent(new Event('click'));
|
||||||
break;
|
break;
|
||||||
case 'copy':
|
case 'copy':
|
||||||
addResponseTab({ ..._tab, label: `${_tab.label || _tab.name}-Copy`, name: `${_tab.label || _tab.name}-Copy` });
|
addResponseTab({
|
||||||
|
..._tab,
|
||||||
|
id: new Date().getTime(),
|
||||||
|
label: `copy-${t(_tab.label || _tab.name)}`,
|
||||||
|
name: `copy-${t(_tab.label || _tab.name)}`,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
_tab.showPopConfirm = true;
|
_tab.showPopConfirm = true;
|
||||||
|
|
|
@ -89,6 +89,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
/**
|
||||||
|
* @description 接口测试-接口调试
|
||||||
|
*/
|
||||||
import { onBeforeRouteLeave } from 'vue-router';
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="['p-[0_16px_16px_16px]', props.class]">
|
<div :class="['p-[0_16px_16px_16px]', props.class]">
|
||||||
<div class="mb-[16px] flex items-center justify-between">
|
<div class="mb-[16px] flex items-center justify-end">
|
||||||
<div class="flex items-center gap-[8px]">
|
<div class="flex items-center gap-[8px]">
|
||||||
<a-input-search
|
<a-input-search
|
||||||
v-model:model-value="keyword"
|
v-model:model-value="keyword"
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
</a-select>
|
</a-select>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<MsButton type="text" class="!mr-0">
|
<MsButton type="text" class="!mr-0" @click="executeDefinition(record)">
|
||||||
{{ t('apiTestManagement.execute') }}
|
{{ t('apiTestManagement.execute') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||||
|
@ -277,7 +277,7 @@
|
||||||
readOnly?: boolean; // 是否是只读模式
|
readOnly?: boolean; // 是否是只读模式
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'openApiTab', record: ApiDefinitionDetail): void;
|
(e: 'openApiTab', record: ApiDefinitionDetail, isExecute?: boolean): void;
|
||||||
(e: 'openCopyApiTab', record: ApiDefinitionDetail): void;
|
(e: 'openCopyApiTab', record: ApiDefinitionDetail): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -759,6 +759,10 @@
|
||||||
emit('openCopyApiTab', record);
|
emit('openCopyApiTab', record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function executeDefinition(record: ApiDefinitionDetail) {
|
||||||
|
emit('openApiTab', record, true);
|
||||||
|
}
|
||||||
|
|
||||||
// 拖拽排序
|
// 拖拽排序
|
||||||
async function handleTableDragSort(params: DragSortParams) {
|
async function handleTableDragSort(params: DragSortParams) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
:active-module="props.activeModule"
|
:active-module="props.activeModule"
|
||||||
:offspring-ids="props.offspringIds"
|
:offspring-ids="props.offspringIds"
|
||||||
:protocol="props.protocol"
|
:protocol="props.protocol"
|
||||||
@open-api-tab="openApiTab"
|
@open-api-tab="(record, isExecute) => openApiTab(record, false, isExecute)"
|
||||||
@open-copy-api-tab="openApiTab($event, true)"
|
@open-copy-api-tab="openApiTab($event, true)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,7 +107,6 @@
|
||||||
moduleTree: ModuleTreeNode[]; // 模块树
|
moduleTree: ModuleTreeNode[]; // 模块树
|
||||||
protocol: string;
|
protocol: string;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits(['addDone']);
|
|
||||||
|
|
||||||
const refreshModuleTree: (() => Promise<any>) | undefined = inject('refreshModuleTree');
|
const refreshModuleTree: (() => Promise<any>) | undefined = inject('refreshModuleTree');
|
||||||
|
|
||||||
|
@ -157,6 +156,7 @@
|
||||||
},
|
},
|
||||||
rawBody: { value: '' },
|
rawBody: { value: '' },
|
||||||
};
|
};
|
||||||
|
// 调试返回的响应内容
|
||||||
const defaultResponse: RequestTaskResult = {
|
const defaultResponse: RequestTaskResult = {
|
||||||
requestResults: [
|
requestResults: [
|
||||||
{
|
{
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
console: '',
|
console: '',
|
||||||
}; // 调试返回的响应内容
|
};
|
||||||
const defaultDefinitionParams: RequestParam = {
|
const defaultDefinitionParams: RequestParam = {
|
||||||
id: initDefaultId,
|
id: initDefaultId,
|
||||||
moduleId: props.activeModule === 'all' ? 'root' : props.activeModule,
|
moduleId: props.activeModule === 'all' ? 'root' : props.activeModule,
|
||||||
|
@ -276,20 +276,24 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
async function openApiTab(apiInfo: ModuleTreeNode | ApiDefinitionDetail | string, isCopy = false) {
|
async function openApiTab(apiInfo: ModuleTreeNode | ApiDefinitionDetail | string, isCopy = false, isExecute = false) {
|
||||||
const isLoadedTabIndex = apiTabs.value.findIndex(
|
const isLoadedTabIndex = apiTabs.value.findIndex(
|
||||||
(e) => e.id === (typeof apiInfo === 'string' ? apiInfo : apiInfo.id)
|
(e) => e.id === (typeof apiInfo === 'string' ? apiInfo : apiInfo.id)
|
||||||
);
|
);
|
||||||
if (isLoadedTabIndex > -1 && !isCopy) {
|
if (isLoadedTabIndex > -1 && !isCopy) {
|
||||||
// 如果点击的请求在tab中已经存在,则直接切换到该tab
|
// 如果点击的请求在tab中已经存在,则直接切换到该tab
|
||||||
activeApiTab.value = apiTabs.value[isLoadedTabIndex] as RequestParam;
|
activeApiTab.value = {
|
||||||
|
...(apiTabs.value[isLoadedTabIndex] as RequestParam),
|
||||||
|
isExecute,
|
||||||
|
mode: isExecute ? 'debug' : 'definition',
|
||||||
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await getDefinitionDetail(typeof apiInfo === 'string' ? apiInfo : apiInfo.id);
|
const res = await getDefinitionDetail(typeof apiInfo === 'string' ? apiInfo : apiInfo.id);
|
||||||
const name = isCopy ? `${res.name}-copy` : res.name;
|
const name = isCopy ? `copy-${res.name}` : res.name;
|
||||||
definitionActiveKey.value = isCopy ? 'definition' : 'preview';
|
definitionActiveKey.value = isCopy || isExecute ? 'definition' : 'preview';
|
||||||
let parseRequestBodyResult;
|
let parseRequestBodyResult;
|
||||||
if (res.protocol === 'HTTP') {
|
if (res.protocol === 'HTTP') {
|
||||||
parseRequestBodyResult = parseRequestBodyFiles(res.request.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
parseRequestBodyResult = parseRequestBodyFiles(res.request.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||||
|
@ -305,6 +309,9 @@
|
||||||
isNew: isCopy,
|
isNew: isCopy,
|
||||||
unSaved: isCopy,
|
unSaved: isCopy,
|
||||||
isCopy,
|
isCopy,
|
||||||
|
id: isCopy ? new Date().getTime() : res.id,
|
||||||
|
isExecute,
|
||||||
|
mode: isExecute ? 'debug' : 'definition',
|
||||||
...parseRequestBodyResult,
|
...parseRequestBodyResult,
|
||||||
});
|
});
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
@change-protocol="handleProtocolChange"
|
@change-protocol="handleProtocolChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="b-0 absolute w-full p-[9px]">
|
<div class="w-full p-[8px]">
|
||||||
<a-divider class="!my-0 !mb-0" />
|
<a-divider class="!my-0 !mb-0" />
|
||||||
<div class="case h-[38px]">
|
<div class="case h-[38px]">
|
||||||
<div class="flex items-center" :class="getActiveClass('recycle')" @click="setActiveFolder('recycle')">
|
<div class="flex items-center" :class="getActiveClass('recycle')" @click="setActiveFolder('recycle')">
|
||||||
|
@ -52,6 +52,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
/**
|
||||||
|
* @description 接口测试-接口管理
|
||||||
|
*/
|
||||||
import { provide } from 'vue';
|
import { provide } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> assertion </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> changeHistory </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> dependency </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> executeHistory </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> params </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> prePost </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> quote </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> setting </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> step </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div> create </div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,180 @@
|
||||||
|
<template>
|
||||||
|
<div class="h-full w-full overflow-hidden">
|
||||||
|
<div class="px-[24px] pt-[16px]">
|
||||||
|
<MsDetailCard :title="`【${previewDetail.num}】${previewDetail.name}`" :description="description">
|
||||||
|
<template #titleAppend>
|
||||||
|
<apiStatus :status="previewDetail.status" size="small" />
|
||||||
|
</template>
|
||||||
|
<template #titleRight>
|
||||||
|
<a-button
|
||||||
|
type="outline"
|
||||||
|
:loading="followLoading"
|
||||||
|
size="mini"
|
||||||
|
class="arco-btn-outline--secondary mr-[4px] !bg-transparent"
|
||||||
|
@click="toggleFollowReview"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-[4px]">
|
||||||
|
<MsIcon
|
||||||
|
:type="previewDetail.follow ? 'icon-icon_collect_filled' : 'icon-icon_collection_outlined'"
|
||||||
|
:class="`${previewDetail.follow ? 'text-[rgb(var(--warning-6))]' : 'text-[var(--color-text-4)]'}`"
|
||||||
|
:size="14"
|
||||||
|
/>
|
||||||
|
{{ t(previewDetail.follow ? 'common.forked' : 'common.fork') }}
|
||||||
|
</div>
|
||||||
|
</a-button>
|
||||||
|
<a-button type="outline" size="mini" class="arco-btn-outline--secondary !bg-transparent" @click="share">
|
||||||
|
<div class="flex items-center gap-[4px]">
|
||||||
|
<MsIcon type="icon-icon_share1" class="text-[var(--color-text-4)]" :size="14" />
|
||||||
|
{{ t('common.share') }}
|
||||||
|
</div>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #type="{ value }">
|
||||||
|
<apiMethodName :method="value as RequestMethods" tag-size="small" is-tag />
|
||||||
|
</template>
|
||||||
|
</MsDetailCard>
|
||||||
|
</div>
|
||||||
|
<div class="h-[calc(100%-124px)]">
|
||||||
|
<a-tabs v-model:active-key="activeKey" class="h-full" animation lazy-load>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioDetailComposition.BASE_INFO"
|
||||||
|
:title="t('apiScenario.baseInfo')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
BASE_INFO
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="ScenarioCreateComposition.STEP" :title="t('apiScenario.step')" class="px-[24px] py-[16px]">
|
||||||
|
<step v-if="activeKey === ScenarioCreateComposition.STEP" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioCreateComposition.PARAMS"
|
||||||
|
:title="t('apiScenario.params')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
<params v-if="activeKey === ScenarioCreateComposition.PARAMS" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioCreateComposition.PRE_POST"
|
||||||
|
:title="t('apiScenario.prePost')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
<prePost v-if="activeKey === ScenarioCreateComposition.PRE_POST" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioCreateComposition.ASSERTION"
|
||||||
|
:title="t('apiScenario.assertion')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
<assertion v-if="activeKey === ScenarioCreateComposition.ASSERTION" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioDetailComposition.EXECUTE_HISTORY"
|
||||||
|
:title="t('apiScenario.executeHistory')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
<executeHistory v-if="activeKey === ScenarioDetailComposition.EXECUTE_HISTORY" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioDetailComposition.CHANGE_HISTORY"
|
||||||
|
:title="t('apiScenario.changeHistory')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
<changeHistory v-if="activeKey === ScenarioDetailComposition.CHANGE_HISTORY" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="ScenarioDetailComposition.DEPENDENCY"
|
||||||
|
:title="t('apiScenario.dependency')"
|
||||||
|
class="px-[24px] py-[16px]"
|
||||||
|
>
|
||||||
|
<dependency v-if="activeKey === ScenarioDetailComposition.DEPENDENCY" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="ScenarioDetailComposition.QUOTE" :title="t('apiScenario.quote')" class="px-[24px] py-[16px]">
|
||||||
|
<quote v-if="activeKey === ScenarioDetailComposition.QUOTE" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="ScenarioCreateComposition.SETTING" :title="t('common.setting')" class="px-[24px] py-[16px]">
|
||||||
|
<setting v-if="activeKey === ScenarioCreateComposition.SETTING" />
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import MsDetailCard from '@/components/pure/ms-detail-card/index.vue';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
|
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||||
|
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
|
||||||
|
import { RequestMethods, ScenarioCreateComposition, ScenarioDetailComposition } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
// 组成部分异步导入
|
||||||
|
const step = defineAsyncComponent(() => import('../components/step/index.vue'));
|
||||||
|
const params = defineAsyncComponent(() => import('../components/params.vue'));
|
||||||
|
const prePost = defineAsyncComponent(() => import('../components/prePost.vue'));
|
||||||
|
const assertion = defineAsyncComponent(() => import('../components/assertion.vue'));
|
||||||
|
const executeHistory = defineAsyncComponent(() => import('../components/executeHistory.vue'));
|
||||||
|
const changeHistory = defineAsyncComponent(() => import('../components/changeHistory.vue'));
|
||||||
|
const dependency = defineAsyncComponent(() => import('../components/dependency.vue'));
|
||||||
|
const quote = defineAsyncComponent(() => import('../components/quote.vue'));
|
||||||
|
const setting = defineAsyncComponent(() => import('../components/setting.vue'));
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
detail: RequestParam;
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits(['updateFollow']);
|
||||||
|
|
||||||
|
const { copy, isSupported } = useClipboard();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const previewDetail = ref<RequestParam>(cloneDeep(props.detail));
|
||||||
|
|
||||||
|
const description = computed(() => [
|
||||||
|
{
|
||||||
|
key: 'type',
|
||||||
|
locale: 'something.type',
|
||||||
|
value: 'type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'path',
|
||||||
|
locale: 'something.path',
|
||||||
|
value: 'path',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const followLoading = ref(false);
|
||||||
|
async function toggleFollowReview() {
|
||||||
|
try {
|
||||||
|
followLoading.value = true;
|
||||||
|
Message.success(previewDetail.value.follow ? t('common.unFollowSuccess') : t('common.followSuccess'));
|
||||||
|
emit('updateFollow');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
followLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function share() {
|
||||||
|
if (isSupported) {
|
||||||
|
copy(`${window.location.href}&dId=${previewDetail.value.id}`);
|
||||||
|
Message.success(t('common.copySuccess'));
|
||||||
|
} else {
|
||||||
|
Message.error(t('common.copyNotSupport'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeKey = ref<ScenarioCreateComposition | ScenarioDetailComposition>(ScenarioDetailComposition.BASE_INFO);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.arco-tabs-content) {
|
||||||
|
@apply pt-0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,10 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded-2xl bg-white">
|
<MsCard no-content-padding simple>
|
||||||
<div class="p-[24px] pb-[16px]">
|
<div class="p-[24px_24px_8px_24px]">
|
||||||
<span>场景列表接口(标签页配置未实现)</span>
|
<MsEditableTab
|
||||||
|
v-model:active-tab="activeApiTab"
|
||||||
|
v-model:tabs="apiTabs"
|
||||||
|
class="flex-1 overflow-hidden"
|
||||||
|
@add="newTab"
|
||||||
|
>
|
||||||
|
<template #label="{ tab }">
|
||||||
|
<a-tooltip :content="tab.label" :mouse-enter-delay="500">
|
||||||
|
<div class="one-line-text max-w-[144px]">
|
||||||
|
{{ tab.label }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</MsEditableTab>
|
||||||
</div>
|
</div>
|
||||||
<a-divider class="!my-0" />
|
<a-divider class="!my-0" />
|
||||||
<div class="pageWrap">
|
<div v-if="activeApiTab.id === 'all'" class="pageWrap">
|
||||||
<MsSplitBox :size="300" :max="0.5">
|
<MsSplitBox :size="300" :max="0.5">
|
||||||
<template #first>
|
<template #first>
|
||||||
<div class="p-[24px] pb-0">
|
<div class="p-[24px] pb-0">
|
||||||
|
@ -36,7 +49,8 @@
|
||||||
</template>
|
</template>
|
||||||
</MsSplitBox>
|
</MsSplitBox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<detail v-else :detail="activeApiTab"></detail>
|
||||||
|
</MsCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -46,9 +60,12 @@
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
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 ApiTable from '@/views/api-test/management/components/management/api/apiTable.vue';
|
import ApiTable from '@/views/api-test/management/components/management/api/apiTable.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -56,6 +73,25 @@
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const apiTabs = ref<any[]>([
|
||||||
|
{
|
||||||
|
id: 'all',
|
||||||
|
label: t('apiScenario.allScenario'),
|
||||||
|
closable: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const activeApiTab = ref<any>(apiTabs.value[0]);
|
||||||
|
|
||||||
|
function newTab() {
|
||||||
|
apiTabs.value.push({
|
||||||
|
id: `newTab${apiTabs.value.length}`,
|
||||||
|
label: `New Tab ${apiTabs.value.length}`,
|
||||||
|
closable: true,
|
||||||
|
});
|
||||||
|
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
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 activeFolder = ref<string>('all');
|
||||||
|
@ -86,8 +122,7 @@
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.pageWrap {
|
.pageWrap {
|
||||||
min-width: 1000px;
|
height: calc(100% - 65px);
|
||||||
height: calc(100vh - 166px);
|
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
@apply bg-white;
|
@apply bg-white;
|
||||||
.case {
|
.case {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
|
'apiScenario.allScenario': 'All scenarios',
|
||||||
'apiScenario.createScenario': 'Create scenario',
|
'apiScenario.createScenario': 'Create scenario',
|
||||||
'apiScenario.importScenario': 'Import scenario',
|
'apiScenario.importScenario': 'Import scenario',
|
||||||
'apiScenario.tree.selectorPlaceholder': 'Please enter the module name',
|
'apiScenario.tree.selectorPlaceholder': 'Please enter the module name',
|
||||||
|
@ -6,15 +7,20 @@ export default {
|
||||||
'apiScenario.tree.showLeafNodeScenario': 'Show subdirectory scenarios',
|
'apiScenario.tree.showLeafNodeScenario': 'Show subdirectory scenarios',
|
||||||
'apiScenario.tree.recycleBin': 'Recycle bin',
|
'apiScenario.tree.recycleBin': 'Recycle bin',
|
||||||
'apiScenario.tree.noMatchModule': 'No matching module/scene yet',
|
'apiScenario.tree.noMatchModule': 'No matching module/scene yet',
|
||||||
|
|
||||||
'apiScenario.createSubModule': 'Create sub-module',
|
'apiScenario.createSubModule': 'Create sub-module',
|
||||||
|
|
||||||
'apiScenario.module.deleteTipTitle': 'Delete {name} module?',
|
'apiScenario.module.deleteTipTitle': 'Delete {name} module?',
|
||||||
'apiScenario.module.deleteTipContent':
|
'apiScenario.module.deleteTipContent':
|
||||||
'After deletion, all scenarios under the module will be deleted synchronously. Please operate with caution.',
|
'After deletion, all scenarios under the module will be deleted synchronously. Please operate with caution.',
|
||||||
|
|
||||||
'apiScenario.deleteConfirm': 'Confirm',
|
'apiScenario.deleteConfirm': 'Confirm',
|
||||||
'apiScenario.deleteSuccess': 'Success',
|
'apiScenario.deleteSuccess': 'Success',
|
||||||
|
|
||||||
'apiScenario.moveSuccess': 'Success',
|
'apiScenario.moveSuccess': 'Success',
|
||||||
|
'apiScenario.baseInfo': 'Base info',
|
||||||
|
'apiScenario.step': 'Step',
|
||||||
|
'apiScenario.params': 'Params',
|
||||||
|
'apiScenario.prePost': 'Pre/Post',
|
||||||
|
'apiScenario.assertion': 'Assertion',
|
||||||
|
'apiScenario.executeHistory': 'Execute history',
|
||||||
|
'apiScenario.changeHistory': 'Change history',
|
||||||
|
'apiScenario.dependency': 'Dependencies',
|
||||||
|
'apiScenario.quote': 'Reference',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
|
'apiScenario.allScenario': '全部场景',
|
||||||
'apiScenario.createScenario': '新建场景',
|
'apiScenario.createScenario': '新建场景',
|
||||||
'apiScenario.importScenario': '导入场景',
|
'apiScenario.importScenario': '导入场景',
|
||||||
'apiScenario.tree.selectorPlaceholder': '请输入模块名称',
|
'apiScenario.tree.selectorPlaceholder': '请输入模块名称',
|
||||||
|
@ -6,14 +7,19 @@ export default {
|
||||||
'apiScenario.tree.showLeafNodeScenario': '显示子目录场景',
|
'apiScenario.tree.showLeafNodeScenario': '显示子目录场景',
|
||||||
'apiScenario.tree.recycleBin': '回收站',
|
'apiScenario.tree.recycleBin': '回收站',
|
||||||
'apiScenario.tree.noMatchModule': '暂无匹配的模块/场景',
|
'apiScenario.tree.noMatchModule': '暂无匹配的模块/场景',
|
||||||
|
|
||||||
'apiScenario.createSubModule': '新建子模块',
|
'apiScenario.createSubModule': '新建子模块',
|
||||||
|
|
||||||
'apiScenario.module.deleteTipTitle': '是否删除 {name} 模块?',
|
'apiScenario.module.deleteTipTitle': '是否删除 {name} 模块?',
|
||||||
'apiScenario.module.deleteTipContent': '删除后,会同步删除模块下的所有场景,请谨慎操作.',
|
'apiScenario.module.deleteTipContent': '删除后,会同步删除模块下的所有场景,请谨慎操作.',
|
||||||
|
|
||||||
'apiScenario.deleteConfirm': '确认删除',
|
'apiScenario.deleteConfirm': '确认删除',
|
||||||
'apiScenario.deleteSuccess': '删除成功',
|
'apiScenario.deleteSuccess': '删除成功',
|
||||||
|
|
||||||
'apiScenario.moveSuccess': '移动成功',
|
'apiScenario.moveSuccess': '移动成功',
|
||||||
|
'apiScenario.baseInfo': '基本信息',
|
||||||
|
'apiScenario.step': '步骤',
|
||||||
|
'apiScenario.params': '参数',
|
||||||
|
'apiScenario.prePost': '前/后置',
|
||||||
|
'apiScenario.assertion': '断言',
|
||||||
|
'apiScenario.executeHistory': '执行历史',
|
||||||
|
'apiScenario.changeHistory': '变更历史',
|
||||||
|
'apiScenario.dependency': '依赖关系',
|
||||||
|
'apiScenario.quote': '引用关系',
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue