fix(all): 修复中级 bug
This commit is contained in:
parent
9e04fb2a47
commit
cbfb5e2221
|
@ -62,9 +62,10 @@ import {
|
||||||
ScenarioDetail,
|
ScenarioDetail,
|
||||||
ScenarioHistoryItem,
|
ScenarioHistoryItem,
|
||||||
ScenarioHistoryPageParams,
|
ScenarioHistoryPageParams,
|
||||||
|
ScenarioStepResourceInfo,
|
||||||
} from '@/models/apiTest/scenario';
|
} from '@/models/apiTest/scenario';
|
||||||
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules, TransferFileParams } from '@/models/common';
|
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules, TransferFileParams } from '@/models/common';
|
||||||
import { ApiScenarioStatus } from '@/enums/apiEnum';
|
import { ApiScenarioStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
import type { RequestParam as CaseRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
import type { RequestParam as CaseRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
import type { RequestParam } from '@/views/api-test/scenario/components/common/customApiDrawer.vue';
|
import type { RequestParam } from '@/views/api-test/scenario/components/common/customApiDrawer.vue';
|
||||||
|
@ -282,6 +283,6 @@ export function updateScenarioPro(id: string | number, priority: CaseLevel | und
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取跨项目信息
|
// 获取跨项目信息
|
||||||
export function getStepProjectInfo(id: string | number) {
|
export function getStepProjectInfo(id: string, type: ScenarioStepType) {
|
||||||
return MSR.get({ url: GetStepProjectInfoUrl, params: id });
|
return MSR.get<ScenarioStepResourceInfo>({ url: GetStepProjectInfoUrl, params: id, data: { resourceType: type } });
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const GetSystemRequestUrl = '/api/scenario/get/system-request'; // 获取
|
||||||
export const FollowScenarioUrl = '/api/scenario/follow'; // 关注/取消关注接口场景
|
export const FollowScenarioUrl = '/api/scenario/follow'; // 关注/取消关注接口场景
|
||||||
export const ScenarioScheduleConfigUrl = '/api/scenario/schedule-config'; // 场景定时任务
|
export const ScenarioScheduleConfigUrl = '/api/scenario/schedule-config'; // 场景定时任务
|
||||||
export const ScenarioScheduleConfigDeleteUrl = '/api/scenario/schedule-config-delete'; // 场景定时任务
|
export const ScenarioScheduleConfigDeleteUrl = '/api/scenario/schedule-config-delete'; // 场景定时任务
|
||||||
export const GetStepProjectInfoUrl = '/api/scenario/step/project-ifo'; // 获取跨项目信息
|
export const GetStepProjectInfoUrl = '/api/scenario/step/resource-info'; // 获取跨项目信息
|
||||||
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
|
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
|
||||||
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
|
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
|
||||||
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景
|
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-if="props.multiple" class="flex w-full items-center">
|
<div v-if="props.multiple" class="flex w-full items-center">
|
||||||
<dropdownMenu @link-file="associatedFile" @change="handleChange" />
|
<dropdownMenu :disabled="props.disabled" @link-file="associatedFile" @change="handleChange" />
|
||||||
<saveAsFilePopover
|
<saveAsFilePopover
|
||||||
v-if="props.fileSaveAsSourceId"
|
v-if="props.fileSaveAsSourceId"
|
||||||
v-model:visible="saveFilePopoverVisible"
|
v-model:visible="saveFilePopoverVisible"
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
:size="props.tagSize"
|
:size="props.tagSize"
|
||||||
class="m-0 border-none p-0"
|
class="m-0 border-none p-0"
|
||||||
:self-style="{ backgroundColor: 'transparent !important' }"
|
:self-style="{ backgroundColor: 'transparent !important' }"
|
||||||
:closable="data.value !== '__arco__more'"
|
:closable="data.value !== '__arco__more' || props.disabled"
|
||||||
@close="handleClose(data)"
|
@close="handleClose(data)"
|
||||||
>
|
>
|
||||||
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
|
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
|
||||||
|
|
|
@ -170,7 +170,7 @@
|
||||||
class="mb-[16px] flex items-baseline gap-[16px] overflow-hidden bg-[var(--color-text-n9)] p-[5px_8px]"
|
class="mb-[16px] flex items-baseline gap-[16px] overflow-hidden bg-[var(--color-text-n9)] p-[5px_8px]"
|
||||||
>
|
>
|
||||||
<div class="break-all text-[var(--color-text-3)]">{{ t('ms.paramsInput.preview') }}</div>
|
<div class="break-all text-[var(--color-text-3)]">{{ t('ms.paramsInput.preview') }}</div>
|
||||||
<a-spin :loading="previewLoading" class="flex flex-1 flex-wrap gap-[8px]">
|
<a-spin :loading="previewLoading" class="flex flex-1 flex-wrap items-baseline gap-[8px]">
|
||||||
<div class="param-preview">{{ paramPreview }}</div>
|
<div class="param-preview">{{ paramPreview }}</div>
|
||||||
<MsButton type="text" @click="getMockValue">{{ t('ms.paramsInput.previewClick') }}</MsButton>
|
<MsButton type="text" @click="getMockValue">{{ t('ms.paramsInput.previewClick') }}</MsButton>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
@ -510,7 +510,7 @@
|
||||||
}
|
}
|
||||||
if (valueArr[1]) {
|
if (valueArr[1]) {
|
||||||
// 匹配函数名和参数
|
// 匹配函数名和参数
|
||||||
const functionRegex = /([a-zA-Z]+)(?:\(([^)]*)\))?/;
|
const functionRegex = /([a-zA-Z0-9]+)(?:\(([^)]*)\))?/;
|
||||||
const functionMatch = valueArr[1].match(functionRegex);
|
const functionMatch = valueArr[1].match(functionRegex);
|
||||||
|
|
||||||
if (functionMatch) {
|
if (functionMatch) {
|
||||||
|
@ -654,6 +654,7 @@
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
.ms-params-input-setting-trigger {
|
.ms-params-input-setting-trigger {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
v-model:expanded-keys="expandedKeys"
|
v-model:expanded-keys="expandedKeys"
|
||||||
v-model:selected-keys="selectedKeys"
|
v-model:selected-keys="selectedKeys"
|
||||||
v-model:checked-keys="checkedKeys"
|
v-model:checked-keys="checkedKeys"
|
||||||
:data="data"
|
:data="filterTreeData"
|
||||||
class="ms-tree"
|
class="ms-tree"
|
||||||
:allow-drop="handleAllowDrop"
|
:allow-drop="handleAllowDrop"
|
||||||
@drag-start="onDragStart"
|
@drag-start="onDragStart"
|
||||||
|
@ -197,13 +197,13 @@
|
||||||
init(true);
|
init(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
const originTreeData = ref<MsTreeNodeData[]>([]); // 初始化时全量的树数据或在非搜索情况下更新后的全量树数据
|
const filterTreeData = ref<MsTreeNodeData[]>([]); // 初始化时全量的树数据或在非搜索情况下更新后的全量树数据
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => data.value,
|
() => data.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (!props.keyword) {
|
if (!props.keyword) {
|
||||||
originTreeData.value = cloneDeep(val);
|
filterTreeData.value = cloneDeep(val);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -220,7 +220,7 @@
|
||||||
const search = (_data: MsTreeNodeData[]) => {
|
const search = (_data: MsTreeNodeData[]) => {
|
||||||
const result: MsTreeNodeData[] = [];
|
const result: MsTreeNodeData[] = [];
|
||||||
_data.forEach((item) => {
|
_data.forEach((item) => {
|
||||||
if (item[props.fieldNames.title].toLowerCase().indexOf(keyword.toLowerCase()) > -1) {
|
if (item[props.fieldNames.title].toLowerCase().includes(keyword.toLowerCase())) {
|
||||||
result.push({ ...item, expanded: true });
|
result.push({ ...item, expanded: true });
|
||||||
} else if (item[props.fieldNames.children]) {
|
} else if (item[props.fieldNames.children]) {
|
||||||
const filterData = search(item[props.fieldNames.children]);
|
const filterData = search(item[props.fieldNames.children]);
|
||||||
|
@ -237,13 +237,17 @@
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
return search(originTreeData.value);
|
return search(data.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 防抖搜索
|
// 防抖搜索
|
||||||
const updateDebouncedSearch = debounce(() => {
|
const updateDebouncedSearch = debounce(() => {
|
||||||
if (props.keyword) {
|
if (props.keyword) {
|
||||||
data.value = searchData(props.keyword);
|
filterTreeData.value = searchData(props.keyword);
|
||||||
|
nextTick(() => {
|
||||||
|
// 展开所有搜索到的节点(expandedKeys控制了节点展开,但是节点展开折叠图标未变化,需要手动触发展开事件)
|
||||||
|
treeRef.value?.expandNode(expandedKeys.value);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, props.searchDebounce);
|
}, props.searchDebounce);
|
||||||
|
|
||||||
|
@ -251,7 +255,7 @@
|
||||||
() => props.keyword,
|
() => props.keyword,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
data.value = cloneDeep(originTreeData.value);
|
filterTreeData.value = cloneDeep(data.value);
|
||||||
} else {
|
} else {
|
||||||
updateDebouncedSearch();
|
updateDebouncedSearch();
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,6 +270,7 @@
|
||||||
contextmenu: !props.readOnly, // 只读模式下禁用右键菜单
|
contextmenu: !props.readOnly, // 只读模式下禁用右键菜单
|
||||||
...props,
|
...props,
|
||||||
language: props.language.toLowerCase(),
|
language: props.language.toLowerCase(),
|
||||||
|
theme: currentTheme.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听值的变化
|
// 监听值的变化
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import { XpathNode } from './types';
|
import { XpathNode } from './types';
|
||||||
import * as XmlBeautify from 'xml-beautify';
|
import XmlBeautify from 'xml-beautify';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
xmlString: string;
|
xmlString: string;
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
isValidXml.value = true;
|
isValidXml.value = true;
|
||||||
parsedXml.value = xmlDoc;
|
parsedXml.value = xmlDoc;
|
||||||
// 先将 XML 字符串格式化,然后解析转换并给每个开始标签加上复制 icon
|
// 先将 XML 字符串格式化,然后解析转换并给每个开始标签加上复制 icon
|
||||||
flattenedXml.value = new XmlBeautify({ parser: DOMParser })
|
flattenedXml.value = new XmlBeautify()
|
||||||
.beautify(props.xmlString)
|
.beautify(props.xmlString)
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
.replace(/>/g, '>')
|
.replace(/>/g, '>')
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<a-table
|
<a-table
|
||||||
v-bind="{ ...$attrs, ...scrollObj }"
|
v-bind="{ ...$attrs, ...scrollObj }"
|
||||||
:row-class="getRowClass"
|
:row-class="getRowClass"
|
||||||
:column-resizable="false"
|
:column-resizable="true"
|
||||||
:span-method="spanMethod"
|
:span-method="spanMethod"
|
||||||
:columns="currentColumns"
|
:columns="currentColumns"
|
||||||
:expanded-keys="props.expandedKeys"
|
:expanded-keys="props.expandedKeys"
|
||||||
|
@ -211,7 +211,7 @@
|
||||||
class="mt-[16px] flex h-[32px] flex-row flex-nowrap items-center"
|
class="mt-[16px] flex h-[32px] flex-row flex-nowrap items-center"
|
||||||
:class="{ 'justify-between': showBatchAction }"
|
:class="{ 'justify-between': showBatchAction }"
|
||||||
>
|
>
|
||||||
<span v-if="props.actionConfig && selectedCount > 0" class="title text-[var(--color-text-2)]">
|
<span v-if="props.actionConfig && selectedCount > 0 && !showBatchAction" class="title text-[var(--color-text-2)]">
|
||||||
{{ t('msTable.batch.selected', { count: selectedCount }) }}
|
{{ t('msTable.batch.selected', { count: selectedCount }) }}
|
||||||
<a-button class="clear-btn ml-[12px] px-2" type="text" @click="emit('clearSelector')">
|
<a-button class="clear-btn ml-[12px] px-2" type="text" @click="emit('clearSelector')">
|
||||||
{{ t('msTable.batch.clear') }}
|
{{ t('msTable.batch.clear') }}
|
||||||
|
|
|
@ -271,7 +271,7 @@ export interface ForEachController {
|
||||||
variable: string; // 变量名
|
variable: string; // 变量名
|
||||||
}
|
}
|
||||||
export interface CountController {
|
export interface CountController {
|
||||||
loops: number; // 循环次数
|
loops: string; // 循环次数
|
||||||
loopTime: number; // 循环间隔时间
|
loopTime: number; // 循环间隔时间
|
||||||
}
|
}
|
||||||
export interface WhileScript {
|
export interface WhileScript {
|
||||||
|
@ -478,3 +478,11 @@ export interface ApiScenarioBatchOptionResult {
|
||||||
success: number;
|
success: number;
|
||||||
error: number;
|
error: number;
|
||||||
}
|
}
|
||||||
|
// 场景跨项目步骤资源信息
|
||||||
|
export interface ScenarioStepResourceInfo {
|
||||||
|
id: string;
|
||||||
|
num: number;
|
||||||
|
name: string;
|
||||||
|
projectId: string;
|
||||||
|
projectName: string;
|
||||||
|
}
|
||||||
|
|
|
@ -545,17 +545,24 @@ export function deleteNode<T>(treeArr: TreeNode<T>[], targetKey: string | number
|
||||||
* @param treeArr 目标树
|
* @param treeArr 目标树
|
||||||
* @param targetKeys 目标节点唯一值的数组
|
* @param targetKeys 目标节点唯一值的数组
|
||||||
*/
|
*/
|
||||||
export function deleteNodes<T>(treeArr: TreeNode<T>[], targetKeys: (string | number)[], customKey = 'key'): void {
|
export function deleteNodes<T>(
|
||||||
|
treeArr: TreeNode<T>[],
|
||||||
|
targetKeys: (string | number)[],
|
||||||
|
deleteCondition?: (node: TreeNode<T>, parent?: TreeNode<T>) => boolean,
|
||||||
|
customKey = 'key'
|
||||||
|
): void {
|
||||||
const targetKeysSet = new Set(targetKeys);
|
const targetKeysSet = new Set(targetKeys);
|
||||||
function deleteNodesInTree(tree: TreeNode<T>[]): void {
|
function deleteNodesInTree(tree: TreeNode<T>[]): void {
|
||||||
for (let i = tree.length - 1; i >= 0; i--) {
|
for (let i = tree.length - 1; i >= 0; i--) {
|
||||||
const node = tree[i];
|
const node = tree[i];
|
||||||
if (targetKeysSet.has(node[customKey])) {
|
if (targetKeysSet.has(node[customKey])) {
|
||||||
tree.splice(i, 1); // 直接删除当前节点
|
if (deleteCondition && deleteCondition(node, node.parent)) {
|
||||||
targetKeysSet.delete(node[customKey]); // 删除后从集合中移除
|
tree.splice(i, 1); // 直接删除当前节点
|
||||||
// 重新调整剩余子节点的 sort 序号
|
targetKeysSet.delete(node[customKey]); // 删除后从集合中移除
|
||||||
for (let j = i; j < tree.length; j++) {
|
// 重新调整剩余子节点的 sort 序号
|
||||||
tree[j].sort = j + 1;
|
for (let j = i; j < tree.length; j++) {
|
||||||
|
tree[j].sort = j + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(node.children)) {
|
} else if (Array.isArray(node.children)) {
|
||||||
deleteNodesInTree(node.children); // 递归删除子节点
|
deleteNodesInTree(node.children); // 递归删除子节点
|
||||||
|
@ -634,12 +641,13 @@ export const getHashParameters = (): Record<string, string> => {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getGenerateId = () => {
|
export const getGenerateId = () => {
|
||||||
const timestamp = new Date().getTime().toString();
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
const randomDigits = Math.floor(Math.random() * 10000)
|
// eslint-disable-next-line no-bitwise
|
||||||
.toString()
|
const r = (Math.random() * 16) | 0;
|
||||||
.padStart(4, '0');
|
// eslint-disable-next-line no-bitwise
|
||||||
const generateId = timestamp + randomDigits;
|
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||||
return generateId.substring(0, 16);
|
return v.toString(16);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -46,7 +46,12 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- 前后置请求结束 -->
|
<!-- 前后置请求结束 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<a-radio-group v-model="condition.enableCommonScript" class="mb-[8px]" @change="emit('change')">
|
<a-radio-group
|
||||||
|
v-model="condition.enableCommonScript"
|
||||||
|
class="mb-[8px]"
|
||||||
|
:disabled="props.disabled"
|
||||||
|
@change="emit('change')"
|
||||||
|
>
|
||||||
<a-radio :value="false">{{ t('apiTestDebug.manual') }}</a-radio>
|
<a-radio :value="false">{{ t('apiTestDebug.manual') }}</a-radio>
|
||||||
<a-radio v-if="hasAnyPermission(['PROJECT_CUSTOM_FUNCTION:READ'])" :value="true">
|
<a-radio v-if="hasAnyPermission(['PROJECT_CUSTOM_FUNCTION:READ'])" :value="true">
|
||||||
{{ t('apiTestDebug.quote') }}
|
{{ t('apiTestDebug.quote') }}
|
||||||
|
@ -58,6 +63,7 @@
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
size="small"
|
size="small"
|
||||||
type="line"
|
type="line"
|
||||||
|
:disabled="props.disabled"
|
||||||
@change="emit('change')"
|
@change="emit('change')"
|
||||||
/>
|
/>
|
||||||
{{ t('apiTestDebug.preconditionAssociatedSceneResult') }}
|
{{ t('apiTestDebug.preconditionAssociatedSceneResult') }}
|
||||||
|
@ -79,6 +85,7 @@
|
||||||
v-model:model-value="condition.name"
|
v-model:model-value="condition.name"
|
||||||
:placeholder="t('apiTestDebug.preconditionScriptNamePlaceholder')"
|
:placeholder="t('apiTestDebug.preconditionScriptNamePlaceholder')"
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
|
:disabled="props.disabled"
|
||||||
size="small"
|
size="small"
|
||||||
@press-enter="isShowEditScriptNameInput = false"
|
@press-enter="isShowEditScriptNameInput = false"
|
||||||
@blur="isShowEditScriptNameInput = false"
|
@blur="isShowEditScriptNameInput = false"
|
||||||
|
@ -201,36 +208,39 @@
|
||||||
v-permission="['PROJECT_CUSTOM_FUNCTION:READ']"
|
v-permission="['PROJECT_CUSTOM_FUNCTION:READ']"
|
||||||
type="text"
|
type="text"
|
||||||
class="font-medium"
|
class="font-medium"
|
||||||
|
:disabled="props.disabled"
|
||||||
@click="showQuoteDrawer = true"
|
@click="showQuoteDrawer = true"
|
||||||
>
|
>
|
||||||
{{ t('apiTestDebug.quote') }}
|
{{ t('apiTestDebug.quote') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showParameters() || showScript()" class="h-[calc(100%-47px)] min-h-[300px]">
|
<div v-if="showParameters() || showScript()" class="min-h-[300px]">
|
||||||
<a-radio-group v-model:model-value="commonScriptShowType" size="small" type="button" class="mb-[8px] w-fit">
|
<div>
|
||||||
<a-radio v-if="showParameters()" value="parameters">{{ t('apiTestDebug.parameters') }}</a-radio>
|
<a-radio-group v-model:model-value="commonScriptShowType" size="small" type="button" class="mb-[8px] w-fit">
|
||||||
<a-radio value="scriptContent">{{ t('apiTestDebug.scriptContent') }}</a-radio>
|
<a-radio v-if="showParameters()" value="parameters">{{ t('apiTestDebug.parameters') }}</a-radio>
|
||||||
</a-radio-group>
|
<a-radio value="scriptContent">{{ t('apiTestDebug.scriptContent') }}</a-radio>
|
||||||
<paramTable
|
</a-radio-group>
|
||||||
v-if="commonScriptShowType === 'parameters'"
|
</div>
|
||||||
v-model:params="scriptParams"
|
<div class="h-[calc(100%-76px)]">
|
||||||
:scroll="{ x: '100%' }"
|
<paramTable
|
||||||
:columns="scriptColumns"
|
v-if="commonScriptShowType === 'parameters'"
|
||||||
:height-used="heightUsed"
|
v-model:params="scriptParams"
|
||||||
:selectable="false"
|
:disabled-param-value="props.disabled"
|
||||||
/>
|
:scroll="{ x: '100%' }"
|
||||||
<div v-show="commonScriptShowType === 'scriptContent'" class="h-[calc(100%-76px)]">
|
:columns="scriptColumns"
|
||||||
|
:height-used="heightUsed"
|
||||||
|
:selectable="false"
|
||||||
|
/>
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
v-if="condition.commonScriptInfo"
|
v-else-if="commonScriptShowType === 'scriptContent' && condition.commonScriptInfo"
|
||||||
v-model:model-value="condition.commonScriptInfo.script"
|
v-model:model-value="condition.commonScriptInfo.script"
|
||||||
theme="vs"
|
theme="vs"
|
||||||
height="100%"
|
:height="props.sqlCodeEditorHeight || '100%'"
|
||||||
:language="condition.commonScriptInfo.scriptLanguage || LanguageEnum.BEANSHELL_JSR233"
|
:language="condition.commonScriptInfo.scriptLanguage || LanguageEnum.BEANSHELL_JSR233"
|
||||||
:show-full-screen="false"
|
:show-full-screen="false"
|
||||||
:show-theme-change="false"
|
:show-theme-change="false"
|
||||||
read-only
|
read-only
|
||||||
>
|
/>
|
||||||
</MsCodeEditor>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -289,7 +299,9 @@
|
||||||
v-model:model-value="condition.variableNames"
|
v-model:model-value="condition.variableNames"
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:placeholder="t('apiTestDebug.storageByColPlaceholder', { a: 'id', b: 'email', c: '{id_1}', d: '{email_1}' })"
|
:placeholder="
|
||||||
|
t('apiTestDebug.storageByColPlaceholder', { a: 'id', b: 'email', c: '${id_1}', d: '${email_1}' })
|
||||||
|
"
|
||||||
@input="() => emit('change')"
|
@input="() => emit('change')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -350,6 +362,7 @@
|
||||||
ref="extractParamsTableRef"
|
ref="extractParamsTableRef"
|
||||||
:params="condition.extractors"
|
:params="condition.extractors"
|
||||||
:disabled-except-param="props.disabled"
|
:disabled-except-param="props.disabled"
|
||||||
|
:disabled-param-value="props.disabled"
|
||||||
:default-param-item="defaultExtractParamItem"
|
:default-param-item="defaultExtractParamItem"
|
||||||
:columns="extractParamsColumns"
|
:columns="extractParamsColumns"
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
|
@ -411,7 +424,7 @@
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<moreSetting v-model:config="activeRecord" is-popover class="mt-[12px]" />
|
<moreSetting v-model:config="activeRecord" is-popover class="mt-[12px]" />
|
||||||
<div class="flex items-center justify-end gap-[8px]">
|
<div v-show="!props.disabled" class="flex items-center justify-end gap-[8px]">
|
||||||
<a-button type="secondary" size="mini" @click="record.moreSettingPopoverVisible = false">
|
<a-button type="secondary" size="mini" @click="record.moreSettingPopoverVisible = false">
|
||||||
{{ t('common.cancel') }}
|
{{ t('common.cancel') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
@ -426,7 +439,11 @@
|
||||||
</paramTable>
|
</paramTable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<quoteSqlSourceDrawer v-model:visible="quoteSqlSourceDrawerVisible" @apply="handleQuoteSqlSourceApply" />
|
<quoteSqlSourceDrawer
|
||||||
|
v-model:visible="quoteSqlSourceDrawerVisible"
|
||||||
|
:selected-key="condition.dataSourceId"
|
||||||
|
@apply="handleQuoteSqlSourceApply"
|
||||||
|
/>
|
||||||
<fastExtraction
|
<fastExtraction
|
||||||
v-model:visible="fastExtractionVisible"
|
v-model:visible="fastExtractionVisible"
|
||||||
:response="props.response"
|
:response="props.response"
|
||||||
|
@ -512,6 +529,7 @@
|
||||||
requestRadioTextProps?: Record<string, any>; // 前后置请求前后置按钮文本
|
requestRadioTextProps?: Record<string, any>; // 前后置请求前后置按钮文本
|
||||||
showPrePostRequest?: boolean; // 是否展示前后置请求忽略
|
showPrePostRequest?: boolean; // 是否展示前后置请求忽略
|
||||||
totalList?: ExecuteConditionProcessor[]; // 总列表
|
totalList?: ExecuteConditionProcessor[]; // 总列表
|
||||||
|
sqlCodeEditorHeight?: string; // sql脚本编辑器高度
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
showAssociatedScene: false,
|
showAssociatedScene: false,
|
||||||
|
@ -530,27 +548,34 @@
|
||||||
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
||||||
const condition = useVModel(props, 'data', emit);
|
const condition = useVModel(props, 'data', emit);
|
||||||
|
|
||||||
watchEffect(() => {
|
watch(
|
||||||
if (condition.value.processorType === RequestConditionProcessor.SQL && condition.value.dataSourceId) {
|
() => currentEnvConfig?.value,
|
||||||
// 如果是SQL类型的条件且已选数据源,需要根据环境切换数据源
|
() => {
|
||||||
const dataSourceItem = currentEnvConfig?.value.dataSources.find(
|
if (condition.value.processorType === RequestConditionProcessor.SQL && condition.value.dataSourceId) {
|
||||||
(item) => item.dataSource === condition.value.dataSourceName
|
// 如果是SQL类型的条件且已选数据源,需要根据环境切换数据源
|
||||||
);
|
const dataSourceItem = currentEnvConfig?.value.dataSources.find(
|
||||||
if (dataSourceItem) {
|
(item) => item.dataSource === condition.value.dataSourceName
|
||||||
// 每次初始化都去查找一下最新的数据源,因为切换环境的时候数据源也需要切换
|
);
|
||||||
condition.value.dataSourceName = dataSourceItem.dataSource;
|
if (currentEnvConfig?.value.dataSources.length === 0) {
|
||||||
condition.value.dataSourceId = dataSourceItem.id;
|
// 如果没有数据源,就清除已选的数据源
|
||||||
} else if (currentEnvConfig && currentEnvConfig.value.dataSources.length > 0) {
|
condition.value.dataSourceName = '';
|
||||||
// 如果没有找到,就默认取第一个数据源
|
condition.value.dataSourceId = '';
|
||||||
condition.value.dataSourceName = currentEnvConfig.value.dataSources[0].dataSource;
|
} else if (dataSourceItem) {
|
||||||
condition.value.dataSourceId = currentEnvConfig.value.dataSources[0].id;
|
// 每次初始化都去查找一下最新的数据源,因为切换环境的时候数据源也需要切换
|
||||||
} else {
|
condition.value.dataSourceName = dataSourceItem.dataSource;
|
||||||
// 如果没有数据源,就清除已选的数据源
|
condition.value.dataSourceId = dataSourceItem.id;
|
||||||
condition.value.dataSourceName = '';
|
} else if (currentEnvConfig && currentEnvConfig.value.dataSources.length > 0) {
|
||||||
condition.value.dataSourceId = '';
|
// 如果没有找到,就默认取第一个数据源
|
||||||
|
condition.value.dataSourceName = currentEnvConfig.value.dataSources[0].dataSource;
|
||||||
|
condition.value.dataSourceId = currentEnvConfig.value.dataSources[0].id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
// 是否显示脚本名称编辑框
|
// 是否显示脚本名称编辑框
|
||||||
const isShowEditScriptNameInput = ref(false);
|
const isShowEditScriptNameInput = ref(false);
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
:show-associated-scene="props.showAssociatedScene"
|
:show-associated-scene="props.showAssociatedScene"
|
||||||
:show-pre-post-request="props.showPrePostRequest"
|
:show-pre-post-request="props.showPrePostRequest"
|
||||||
:request-radio-text-props="props.requestRadioTextProps"
|
:request-radio-text-props="props.requestRadioTextProps"
|
||||||
|
:sql-code-editor-height="props.sqlCodeEditorHeight"
|
||||||
@copy="copyListItem"
|
@copy="copyListItem"
|
||||||
@delete="deleteListItem"
|
@delete="deleteListItem"
|
||||||
@change="emit('change')"
|
@change="emit('change')"
|
||||||
|
@ -55,8 +56,8 @@
|
||||||
import { conditionTypeNameMap } from '@/config/apiTest';
|
import { conditionTypeNameMap } from '@/config/apiTest';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import { ConditionType, ExecuteConditionProcessor } from '@/models/apiTest/common';
|
import { ConditionType, ExecuteConditionProcessor, RegexExtract } from '@/models/apiTest/common';
|
||||||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
import { RequestConditionProcessor, RequestExtractExpressionEnum, RequestExtractScope } from '@/enums/apiEnum';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -69,6 +70,7 @@
|
||||||
response?: string; // 响应内容
|
response?: string; // 响应内容
|
||||||
showAssociatedScene?: boolean;
|
showAssociatedScene?: boolean;
|
||||||
showPrePostRequest?: boolean; // 是否展示前后置请求忽略选项
|
showPrePostRequest?: boolean; // 是否展示前后置请求忽略选项
|
||||||
|
sqlCodeEditorHeight?: string;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
showAssociatedScene: false,
|
showAssociatedScene: false,
|
||||||
|
@ -201,7 +203,6 @@
|
||||||
if (isEXTRACT) {
|
if (isEXTRACT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.value.push({
|
data.value.push({
|
||||||
id,
|
id,
|
||||||
processorType: RequestConditionProcessor.EXTRACT,
|
processorType: RequestConditionProcessor.EXTRACT,
|
||||||
|
@ -228,6 +229,11 @@
|
||||||
hasNoIdItem = true;
|
hasNoIdItem = true;
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
|
extractors: item.extractors?.map((e, j) => ({
|
||||||
|
...e,
|
||||||
|
extractScope: (e as RegexExtract).extractScope || RequestExtractScope.BODY,
|
||||||
|
id: new Date().getTime() + j,
|
||||||
|
})),
|
||||||
id: new Date().getTime() + i,
|
id: new Date().getTime() + i,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,10 +89,10 @@
|
||||||
(val) => {
|
(val) => {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
currentEnv.value = (envOptions.value[0]?.value as string) || '';
|
currentEnv.value = (envOptions.value[0]?.value as string) || '';
|
||||||
nextTick(() => {
|
|
||||||
initEnvironment();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
initEnvironment();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<template #typeTitle="{ columnConfig }">
|
<template #typeTitle="{ columnConfig }">
|
||||||
<div class="flex items-center text-[var(--color-text-3)]">
|
<div class="flex items-center text-[var(--color-text-3)]">
|
||||||
{{ t('apiTestDebug.paramType') }}
|
{{ t('apiTestDebug.paramType') }}
|
||||||
<a-tooltip :content="columnConfig.typeTitleTooltip" position="right">
|
<a-tooltip :content="columnConfig.typeTitleTooltip" :disabled="!columnConfig.typeTitleTooltip" position="right">
|
||||||
<icon-question-circle
|
<icon-question-circle
|
||||||
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
|
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
|
||||||
size="16"
|
size="16"
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<template #extractValueTitle>
|
<template #extractValueTitle>
|
||||||
<div class="flex items-center text-[var(--color-text-3)]">
|
<div class="flex items-center text-[var(--color-text-3)]">
|
||||||
{{ t('apiTestDebug.extractValueByColumn') }}
|
{{ t('apiTestDebug.extractValueByColumn') }}
|
||||||
<a-tooltip :content="t('caseManagement.caseReview.passRateTip')" position="right">
|
<a-tooltip :content="t('apiTestDebug.extractValueTitleTip')" position="right">
|
||||||
<icon-question-circle
|
<icon-question-circle
|
||||||
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
|
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
|
||||||
size="16"
|
size="16"
|
||||||
|
@ -211,7 +211,7 @@
|
||||||
<MsAddAttachment
|
<MsAddAttachment
|
||||||
v-else-if="record.paramType === RequestParamsType.FILE"
|
v-else-if="record.paramType === RequestParamsType.FILE"
|
||||||
v-model:file-list="record.files"
|
v-model:file-list="record.files"
|
||||||
:disabled="props.disabledExceptParam"
|
:disabled="props.disabledParamValue"
|
||||||
mode="input"
|
mode="input"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:fields="{
|
:fields="{
|
||||||
|
@ -819,7 +819,6 @@
|
||||||
() => props.params,
|
() => props.params,
|
||||||
(arr) => {
|
(arr) => {
|
||||||
if (arr.length > 0) {
|
if (arr.length > 0) {
|
||||||
let hasNoIdItem = false; // 是否有没有id的项,用以判断是否是后台数据初始化表格
|
|
||||||
paramsData.value = arr.map((item, i) => {
|
paramsData.value = arr.map((item, i) => {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
// 批量添加过来的数据最后一行会是 undefined
|
// 批量添加过来的数据最后一行会是 undefined
|
||||||
|
@ -830,7 +829,6 @@
|
||||||
}
|
}
|
||||||
if (!item.id) {
|
if (!item.id) {
|
||||||
// 后台存储无id,渲染时需要手动添加一次
|
// 后台存储无id,渲染时需要手动添加一次
|
||||||
hasNoIdItem = true;
|
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
id: new Date().getTime() + i,
|
id: new Date().getTime() + i,
|
||||||
|
@ -838,7 +836,11 @@
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
if (hasNoIdItem && !filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault && !props.isTreeTable) {
|
if (
|
||||||
|
!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault &&
|
||||||
|
!props.isTreeTable &&
|
||||||
|
!filterKeyValParams(arr, arr[arr.length - 2]).lastDataIsDefault // 为了判断最后俩行是否一致(因为下拉框切换会新增一行一样的数据,此时最后一条数据与默认数据是不一样的)
|
||||||
|
) {
|
||||||
addTableLine(arr.length - 1, false, true);
|
addTableLine(arr.length - 1, false, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1033,7 +1035,7 @@
|
||||||
*/
|
*/
|
||||||
function handleSearchParams(val: string, item: FormTableColumn) {
|
function handleSearchParams(val: string, item: FormTableColumn) {
|
||||||
item.autoCompleteParams = item.autoCompleteParams?.map((e) => {
|
item.autoCompleteParams = item.autoCompleteParams?.map((e) => {
|
||||||
e.isShow = (e.label || '').includes(val);
|
e.isShow = (e.label || '').toLowerCase().includes(val.toLowerCase());
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
:response="props.response"
|
:response="props.response"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:height-used="heightUsed"
|
:height-used="heightUsed"
|
||||||
|
:sql-code-editor-height="props.sqlCodeEditorHeight"
|
||||||
@change="emit('change')"
|
@change="emit('change')"
|
||||||
>
|
>
|
||||||
<template v-if="props.isDefinition" #titleRight>
|
<template v-if="props.isDefinition" #titleRight>
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
isDefinition?: boolean; // 是否是定义页面
|
isDefinition?: boolean; // 是否是定义页面
|
||||||
isScenario?: boolean; // 是否是场景页面
|
isScenario?: boolean; // 是否是场景页面
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
sqlCodeEditorHeight?: string;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:params', params: ExecuteConditionProcessor[]): void;
|
(e: 'update:params', params: ExecuteConditionProcessor[]): void;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
v-model:list="innerConfig.processors"
|
v-model:list="innerConfig.processors"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:condition-types="conditionTypes"
|
:condition-types="conditionTypes"
|
||||||
|
:sql-code-editor-height="props.sqlCodeEditorHeight"
|
||||||
add-text="apiTestDebug.precondition"
|
add-text="apiTestDebug.precondition"
|
||||||
@change="emit('change')"
|
@change="emit('change')"
|
||||||
>
|
>
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
isDefinition?: boolean; // 是否是定义页面
|
isDefinition?: boolean; // 是否是定义页面
|
||||||
isScenario?: boolean; // 是否是场景页面
|
isScenario?: boolean; // 是否是场景页面
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
sqlCodeEditorHeight?: string;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:config', params: ExecuteConditionConfig): void;
|
(e: 'update:config', params: ExecuteConditionConfig): void;
|
||||||
|
|
|
@ -202,4 +202,6 @@ export default {
|
||||||
'apiTestDebug.testSuccess': 'Test success',
|
'apiTestDebug.testSuccess': 'Test success',
|
||||||
'apiTestDebug.searchByDataBaseName': 'Search by data source name',
|
'apiTestDebug.searchByDataBaseName': 'Search by data source name',
|
||||||
'apiTestDebug.regexMatchRules': 'Expression matching rules',
|
'apiTestDebug.regexMatchRules': 'Expression matching rules',
|
||||||
|
'apiTestDebug.extractValueTitleTip':
|
||||||
|
'Enter the column name and corresponding value in column storage. If you want to extract the first value of the name column, enter name_1',
|
||||||
};
|
};
|
||||||
|
|
|
@ -189,4 +189,5 @@ export default {
|
||||||
'apiTestDebug.testSuccess': '测试成功',
|
'apiTestDebug.testSuccess': '测试成功',
|
||||||
'apiTestDebug.searchByDataBaseName': '按数据源名称搜索',
|
'apiTestDebug.searchByDataBaseName': '按数据源名称搜索',
|
||||||
'apiTestDebug.regexMatchRules': '表达式匹配规则',
|
'apiTestDebug.regexMatchRules': '表达式匹配规则',
|
||||||
|
'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取name列的第一个值则输入name_1',
|
||||||
};
|
};
|
||||||
|
|
|
@ -188,7 +188,7 @@
|
||||||
v-model:model-value="importForm.name"
|
v-model:model-value="importForm.name"
|
||||||
:placeholder="t('apiTestManagement.taskNamePlaceholder')"
|
:placeholder="t('apiTestManagement.taskNamePlaceholder')"
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
class="flex-1"
|
class="w-[550px]"
|
||||||
></a-input>
|
></a-input>
|
||||||
<MsButton type="text" @click="taskDrawerVisible = true">
|
<MsButton type="text" @click="taskDrawerVisible = true">
|
||||||
{{ t('apiTestManagement.timeTaskList') }}
|
{{ t('apiTestManagement.timeTaskList') }}
|
||||||
|
@ -204,7 +204,7 @@
|
||||||
<a-input
|
<a-input
|
||||||
v-model:model-value="importForm.swaggerUrl"
|
v-model:model-value="importForm.swaggerUrl"
|
||||||
:placeholder="t('apiTestManagement.urlImportPlaceholder')"
|
:placeholder="t('apiTestManagement.urlImportPlaceholder')"
|
||||||
class="w-[700px]"
|
class="w-[550px]"
|
||||||
allow-clear
|
allow-clear
|
||||||
></a-input>
|
></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -246,7 +246,7 @@
|
||||||
<a-tree-select
|
<a-tree-select
|
||||||
v-model:modelValue="importForm.moduleId"
|
v-model:modelValue="importForm.moduleId"
|
||||||
:data="moduleTree"
|
:data="moduleTree"
|
||||||
class="w-[436px]"
|
class="w-[500px]"
|
||||||
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||||
allow-search
|
allow-search
|
||||||
>
|
>
|
||||||
|
@ -370,6 +370,7 @@
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
moduleTree: ModuleTreeNode[];
|
moduleTree: ModuleTreeNode[];
|
||||||
popupContainer?: string;
|
popupContainer?: string;
|
||||||
|
activeModule: string;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits(['update:visible', 'done']);
|
const emit = defineEmits(['update:visible', 'done']);
|
||||||
|
|
||||||
|
@ -391,8 +392,8 @@
|
||||||
const defaultForm: ImportApiDefinitionRequest = {
|
const defaultForm: ImportApiDefinitionRequest = {
|
||||||
platform: RequestImportFormat.SWAGGER,
|
platform: RequestImportFormat.SWAGGER,
|
||||||
name: '',
|
name: '',
|
||||||
moduleId: '',
|
moduleId: 'root',
|
||||||
coverData: true,
|
coverData: false,
|
||||||
syncCase: true,
|
syncCase: true,
|
||||||
coverModule: false,
|
coverModule: false,
|
||||||
swaggerUrl: '',
|
swaggerUrl: '',
|
||||||
|
@ -406,6 +407,19 @@
|
||||||
};
|
};
|
||||||
const importForm = ref({ ...defaultForm });
|
const importForm = ref({ ...defaultForm });
|
||||||
const importFormRef = ref<FormInstance>();
|
const importFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => visible.value,
|
||||||
|
(val) => {
|
||||||
|
if (val) {
|
||||||
|
importForm.value.moduleId = props.activeModule !== 'all' ? props.activeModule : 'root';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const moreSettingActive = ref<number[]>([]);
|
const moreSettingActive = ref<number[]>([]);
|
||||||
const disabledConfirm = computed(() => {
|
const disabledConfirm = computed(() => {
|
||||||
if (importForm.value.type === RequestImportType.API) {
|
if (importForm.value.type === RequestImportType.API) {
|
||||||
|
|
|
@ -258,14 +258,18 @@
|
||||||
style="padding-top: 10px"
|
style="padding-top: 10px"
|
||||||
>
|
>
|
||||||
<a-switch v-model="batchForm.append" class="mr-1" size="small" type="line" />
|
<a-switch v-model="batchForm.append" class="mr-1" size="small" type="line" />
|
||||||
<a-tooltip :content="t('caseManagement.featureCase.enableTags')">
|
<span class="flex items-center">
|
||||||
<span class="flex items-center">
|
<span class="mr-1">{{ t('caseManagement.featureCase.appendTag') }}</span>
|
||||||
<span class="mr-1">{{ t('caseManagement.featureCase.appendTag') }}</span>
|
<span class="mt-[2px]">
|
||||||
<span class="mt-[2px]">
|
<a-tooltip>
|
||||||
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
|
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
|
||||||
</span>
|
<template #content>
|
||||||
|
<div>{{ t('caseManagement.featureCase.enableTags') }}</div>
|
||||||
|
<div>{{ t('caseManagement.featureCase.closeTags') }}</div>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<a-button type="secondary" :disabled="batchUpdateLoading" @click="cancelBatch">
|
<a-button type="secondary" :disabled="batchUpdateLoading" @click="cancelBatch">
|
||||||
|
|
|
@ -321,14 +321,18 @@
|
||||||
style="padding-top: 10px"
|
style="padding-top: 10px"
|
||||||
>
|
>
|
||||||
<a-switch v-model="batchForm.append" class="mr-1" size="small" type="line" />
|
<a-switch v-model="batchForm.append" class="mr-1" size="small" type="line" />
|
||||||
<a-tooltip :content="t('caseManagement.featureCase.enableTags')">
|
<span class="flex items-center">
|
||||||
<span class="flex items-center">
|
<span class="mr-1">{{ t('caseManagement.featureCase.appendTag') }}</span>
|
||||||
<span class="mr-1">{{ t('caseManagement.featureCase.appendTag') }}</span>
|
<span class="mt-[2px]">
|
||||||
<span class="mt-[2px]">
|
<a-tooltip>
|
||||||
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
|
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
|
||||||
</span>
|
<template #content>
|
||||||
|
<div>{{ t('caseManagement.featureCase.enableTags') }}</div>
|
||||||
|
<div>{{ t('caseManagement.featureCase.closeTags') }}</div>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<a-button type="secondary" :disabled="batchEditLoading" @click="cancelBatchEdit">
|
<a-button type="secondary" :disabled="batchEditLoading" @click="cancelBatchEdit">
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</MsEditableTab>
|
</MsEditableTab>
|
||||||
<environmentSelect ref="environmentSelectRef" />
|
<environmentSelect ref="environmentSelectRef" v-model:current-env="activeApiTab.environmentId" />
|
||||||
</div>
|
</div>
|
||||||
<api
|
<api
|
||||||
v-show="(activeApiTab.id === 'all' && currentTab === 'api') || activeApiTab.type === 'api'"
|
v-show="(activeApiTab.id === 'all' && currentTab === 'api') || activeApiTab.type === 'api'"
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<importApi
|
<importApi
|
||||||
v-model:visible="importDrawerVisible"
|
v-model:visible="importDrawerVisible"
|
||||||
:module-tree="folderTree"
|
:module-tree="folderTree"
|
||||||
|
:active-module="activeModule"
|
||||||
popup-container="#managementContainer"
|
popup-container="#managementContainer"
|
||||||
@done="handleImportDone"
|
@done="handleImportDone"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -43,9 +43,11 @@
|
||||||
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
||||||
class="customApiDrawer-title-right ml-auto flex items-center gap-[16px]"
|
class="customApiDrawer-title-right ml-auto flex items-center gap-[16px]"
|
||||||
>
|
>
|
||||||
<div class="text-[14px] font-normal text-[var(--color-text-4)]">
|
<a-tooltip :content="currentEnvConfig?.name" :disabled="!currentEnvConfig?.name">
|
||||||
{{ t('apiScenario.env', { name: currentEnvConfig?.name }) }}
|
<div class="one-line-text max-w-[250px] text-[14px] font-normal text-[var(--color-text-4)]">
|
||||||
</div>
|
{{ t('apiScenario.env', { name: currentEnvConfig?.name }) }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="requestVModel.customizeRequestEnvEnable"
|
v-model:model-value="requestVModel.customizeRequestEnvEnable"
|
||||||
class="w-[150px]"
|
class="w-[150px]"
|
||||||
|
@ -119,27 +121,39 @@
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</div>
|
</div>
|
||||||
<div v-permission="[props.permissionMap?.execute]">
|
<div v-permission="[props.permissionMap?.execute]">
|
||||||
<a-dropdown-button
|
<template v-if="hasLocalExec">
|
||||||
v-if="hasLocalExec"
|
<a-dropdown-button
|
||||||
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
|
v-if="!requestVModel.executeLoading"
|
||||||
class="exec-btn"
|
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
|
||||||
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
class="exec-btn"
|
||||||
@select="execute"
|
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
||||||
|
@select="execute"
|
||||||
|
>
|
||||||
|
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
|
||||||
|
<template #icon>
|
||||||
|
<icon-down />
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
|
||||||
|
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown-button>
|
||||||
|
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
|
||||||
|
{{ t('common.stop') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
v-else-if="!requestVModel.executeLoading"
|
||||||
|
class="mr-[12px]"
|
||||||
|
type="primary"
|
||||||
|
@click="() => execute('serverExec')"
|
||||||
>
|
>
|
||||||
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
|
|
||||||
<template #icon>
|
|
||||||
<icon-down />
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
|
|
||||||
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
|
|
||||||
</a-doption>
|
|
||||||
</template>
|
|
||||||
</a-dropdown-button>
|
|
||||||
<a-button v-else-if="!requestVModel.executeLoading" type="primary" @click="() => execute('serverExec')">
|
|
||||||
{{ t('apiTestDebug.serverExec') }}
|
{{ t('apiTestDebug.serverExec') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">{{ t('common.stop') }}</a-button>
|
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
|
||||||
|
{{ t('common.stop') }}
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-input
|
<a-input
|
||||||
|
@ -201,7 +215,7 @@
|
||||||
<httpHeader
|
<httpHeader
|
||||||
v-if="requestVModel.activeTab === RequestComposition.HEADER"
|
v-if="requestVModel.activeTab === RequestComposition.HEADER"
|
||||||
v-model:params="requestVModel.headers"
|
v-model:params="requestVModel.headers"
|
||||||
:disabled-param-value="!isEditableApi"
|
:disabled-param-value="!isEditableApi && !isEditableParamValue"
|
||||||
:disabled-except-param="!isEditableApi"
|
:disabled-except-param="!isEditableApi"
|
||||||
:layout="activeLayout"
|
:layout="activeLayout"
|
||||||
:second-box-height="secondBoxHeight"
|
:second-box-height="secondBoxHeight"
|
||||||
|
@ -211,7 +225,7 @@
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
|
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
|
||||||
v-model:params="requestVModel.body"
|
v-model:params="requestVModel.body"
|
||||||
:layout="activeLayout"
|
:layout="activeLayout"
|
||||||
:disabled-param-value="!isEditableApi"
|
:disabled-param-value="!isEditableApi && !isEditableParamValue"
|
||||||
:disabled-except-param="!isEditableApi"
|
:disabled-except-param="!isEditableApi"
|
||||||
:second-box-height="secondBoxHeight"
|
:second-box-height="secondBoxHeight"
|
||||||
:upload-temp-file-api="uploadTempFile"
|
:upload-temp-file-api="uploadTempFile"
|
||||||
|
@ -224,7 +238,7 @@
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
|
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
|
||||||
v-model:params="requestVModel.query"
|
v-model:params="requestVModel.query"
|
||||||
:layout="activeLayout"
|
:layout="activeLayout"
|
||||||
:disabled-param-value="!isEditableApi"
|
:disabled-param-value="!isEditableApi && !isEditableParamValue"
|
||||||
:disabled-except-param="!isEditableApi"
|
:disabled-except-param="!isEditableApi"
|
||||||
:second-box-height="secondBoxHeight"
|
:second-box-height="secondBoxHeight"
|
||||||
@change="handleActiveDebugChange"
|
@change="handleActiveDebugChange"
|
||||||
|
@ -233,7 +247,7 @@
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.REST"
|
v-else-if="requestVModel.activeTab === RequestComposition.REST"
|
||||||
v-model:params="requestVModel.rest"
|
v-model:params="requestVModel.rest"
|
||||||
:layout="activeLayout"
|
:layout="activeLayout"
|
||||||
:disabled-param-value="!isEditableApi"
|
:disabled-param-value="!isEditableApi && !isEditableParamValue"
|
||||||
:disabled-except-param="!isEditableApi"
|
:disabled-except-param="!isEditableApi"
|
||||||
:second-box-height="secondBoxHeight"
|
:second-box-height="secondBoxHeight"
|
||||||
@change="handleActiveDebugChange"
|
@change="handleActiveDebugChange"
|
||||||
|
@ -554,11 +568,15 @@
|
||||||
|
|
||||||
// 复制 api 只要加载过一次后就会保存,所以 props.request 是不为空的
|
// 复制 api 只要加载过一次后就会保存,所以 props.request 是不为空的
|
||||||
const isCopyApiNeedInit = computed(() => _stepType.value.isCopyApi && props.request === undefined);
|
const isCopyApiNeedInit = computed(() => _stepType.value.isCopyApi && props.request === undefined);
|
||||||
|
// 全可编辑接口
|
||||||
const isEditableApi = computed(
|
const isEditableApi = computed(
|
||||||
() =>
|
() =>
|
||||||
!props.step?.isQuoteScenarioStep &&
|
!props.step?.isQuoteScenarioStep &&
|
||||||
(_stepType.value.isCopyApi || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST || !props.step)
|
(_stepType.value.isCopyApi || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST || !props.step)
|
||||||
);
|
);
|
||||||
|
// 非引用场景下的引用 api只可更改参数值接口
|
||||||
|
const isEditableParamValue = computed(() => !props.step?.isQuoteScenarioStep && _stepType.value.isQuoteApi);
|
||||||
|
// 是否是 HTTP 协议
|
||||||
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
|
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
|
||||||
|
|
||||||
const isInitPluginForm = ref(false);
|
const isInitPluginForm = ref(false);
|
||||||
|
@ -999,7 +1017,7 @@
|
||||||
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
|
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
|
||||||
name: requestVModel.value.name,
|
name: requestVModel.value.name,
|
||||||
unSaved: requestVModel.value.unSaved,
|
unSaved: requestVModel.value.unSaved,
|
||||||
customizeRequest: props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST || !props.request,
|
customizeRequest: requestVModel.value.customizeRequest,
|
||||||
customizeRequestEnvEnable: requestVModel.value.customizeRequestEnvEnable,
|
customizeRequestEnvEnable: requestVModel.value.customizeRequestEnvEnable,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,27 +67,39 @@
|
||||||
allow-clear
|
allow-clear
|
||||||
/>
|
/>
|
||||||
<div v-permission="[props.permissionMap?.execute]">
|
<div v-permission="[props.permissionMap?.execute]">
|
||||||
<a-dropdown-button
|
<template v-if="hasLocalExec">
|
||||||
v-if="hasLocalExec"
|
<a-dropdown-button
|
||||||
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
|
v-if="!requestVModel.executeLoading"
|
||||||
class="exec-btn"
|
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
|
||||||
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
class="exec-btn"
|
||||||
@select="execute"
|
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
||||||
|
@select="execute"
|
||||||
|
>
|
||||||
|
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
|
||||||
|
<template #icon>
|
||||||
|
<icon-down />
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
|
||||||
|
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown-button>
|
||||||
|
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
|
||||||
|
{{ t('common.stop') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
v-else-if="!requestVModel.executeLoading"
|
||||||
|
class="mr-[12px]"
|
||||||
|
type="primary"
|
||||||
|
@click="() => execute('serverExec')"
|
||||||
>
|
>
|
||||||
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
|
|
||||||
<template #icon>
|
|
||||||
<icon-down />
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
|
|
||||||
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
|
|
||||||
</a-doption>
|
|
||||||
</template>
|
|
||||||
</a-dropdown-button>
|
|
||||||
<a-button v-else-if="!requestVModel.executeLoading" type="primary" @click="() => execute('serverExec')">
|
|
||||||
{{ t('apiTestDebug.serverExec') }}
|
{{ t('apiTestDebug.serverExec') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">{{ t('common.stop') }}</a-button>
|
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
|
||||||
|
{{ t('common.stop') }}
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-[16px]">
|
<div class="px-[16px]">
|
||||||
|
@ -443,8 +455,8 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 复制 api 只要加载过一次后就会保存,所以 props.request 是不为空的
|
// 非引用场景下的复制 case 可更改
|
||||||
const isEditableApi = computed(() => _stepType.value.isCopyCase);
|
const isEditableApi = computed(() => !activeStep.value?.isQuoteScenarioStep && _stepType.value.isCopyCase);
|
||||||
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
|
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
|
||||||
const isInitPluginForm = ref(false);
|
const isInitPluginForm = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
|
@ -255,6 +255,7 @@
|
||||||
fullScenarioArr.push(...res);
|
fullScenarioArr.push(...res);
|
||||||
});
|
});
|
||||||
if (refType === ScenarioStepRefType.COPY) {
|
if (refType === ScenarioStepRefType.COPY) {
|
||||||
|
// 复制需要递归给每个节点生成新的uniqueId,并记录copyFromStepId
|
||||||
fullScenarioArr = mapTree<MsTableDataItem<ApiScenarioTableItem>>(fullScenarioArr, (node) => {
|
fullScenarioArr = mapTree<MsTableDataItem<ApiScenarioTableItem>>(fullScenarioArr, (node) => {
|
||||||
const id = getGenerateId();
|
const id = getGenerateId();
|
||||||
return {
|
return {
|
||||||
|
@ -275,6 +276,7 @@
|
||||||
);
|
);
|
||||||
handleCancel();
|
handleCancel();
|
||||||
} else {
|
} else {
|
||||||
|
// 引用只需要给场景节点生成新的步骤 id,内部步骤只需要生成uniqueId作为前端渲染 id
|
||||||
fullScenarioArr = fullScenarioArr.map((e) => {
|
fullScenarioArr = fullScenarioArr.map((e) => {
|
||||||
const id = getGenerateId();
|
const id = getGenerateId();
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -79,6 +79,9 @@
|
||||||
<template #status="{ record }">
|
<template #status="{ record }">
|
||||||
<apiStatus :status="record.status" size="small" />
|
<apiStatus :status="record.status" size="small" />
|
||||||
</template>
|
</template>
|
||||||
|
<template #priority="{ record }">
|
||||||
|
<caseLevel :case-level="record.priority" />
|
||||||
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -90,6 +93,7 @@
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import { MsTableDataItem } from '@/components/pure/ms-table/type';
|
import { MsTableDataItem } from '@/components/pure/ms-table/type';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||||
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||||
|
@ -475,7 +479,7 @@
|
||||||
case 'scenario':
|
case 'scenario':
|
||||||
default:
|
default:
|
||||||
routeName = ApiTestRouteEnum.API_TEST_SCENARIO;
|
routeName = ApiTestRouteEnum.API_TEST_SCENARIO;
|
||||||
query.sId = id;
|
query.id = id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
openNewPage(routeName, query);
|
openNewPage(routeName, query);
|
||||||
|
|
|
@ -98,7 +98,8 @@
|
||||||
step.stepType === ScenarioStepType.LOOP_CONTROLLER &&
|
step.stepType === ScenarioStepType.LOOP_CONTROLLER &&
|
||||||
step.config.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT &&
|
step.config.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT &&
|
||||||
step.config.msCountController &&
|
step.config.msCountController &&
|
||||||
step.config.msCountController.loops > 0
|
!Number.isNaN(step.config.msCountController.loops) &&
|
||||||
|
Number(step.config.msCountController.loops) > 0
|
||||||
) {
|
) {
|
||||||
// 循环控制器展示当前执行次数/总次数
|
// 循环控制器展示当前执行次数/总次数
|
||||||
const firstHasResultChild = step.children?.find((child) => {
|
const firstHasResultChild = step.children?.find((child) => {
|
||||||
|
|
|
@ -19,11 +19,12 @@
|
||||||
v-model="scriptName"
|
v-model="scriptName"
|
||||||
:placeholder="t('apiScenario.scriptOperationNamePlaceholder')"
|
:placeholder="t('apiScenario.scriptOperationNamePlaceholder')"
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
|
:disabled="isReadonly"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[10px] flex flex-1 gap-[8px]">
|
<div class="mt-[10px] flex flex-1 gap-[8px]">
|
||||||
<conditionContent v-if="visible" v-model:data="activeItem" :is-build-in="true" />
|
<conditionContent v-if="visible" v-model:data="activeItem" :disabled="isReadonly" :is-build-in="true" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="currentResponse?.console" class="p-[8px]">
|
<div v-if="currentResponse?.console" class="p-[8px]">
|
||||||
<div class="mb-[8px] font-medium text-[var(--color-text-1)]">{{ t('apiScenario.executionResult') }}</div>
|
<div class="mb-[8px] font-medium text-[var(--color-text-1)]">{{ t('apiScenario.executionResult') }}</div>
|
||||||
|
@ -88,6 +89,7 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', { required: true });
|
const visible = defineModel<boolean>('visible', { required: true });
|
||||||
|
const isReadonly = computed(() => props.step?.isQuoteScenarioStep);
|
||||||
const currentLoop = ref(1);
|
const currentLoop = ref(1);
|
||||||
const currentResponse = computed(() => {
|
const currentResponse = computed(() => {
|
||||||
if (props.step?.uniqueId) {
|
if (props.step?.uniqueId) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const defaultLoopController = {
|
||||||
variable: '', // 变量名
|
variable: '', // 变量名
|
||||||
},
|
},
|
||||||
msCountController: {
|
msCountController: {
|
||||||
loops: 1, // 循环次数
|
loops: '1', // 循环次数
|
||||||
loopTime: 0, // 循环间隔时间
|
loopTime: 0, // 循环间隔时间
|
||||||
},
|
},
|
||||||
whileController: {
|
whileController: {
|
||||||
|
|
|
@ -10,14 +10,7 @@
|
||||||
@press-enter="loadExecuteHistoryList"
|
@press-enter="loadExecuteHistoryList"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ms-base-table
|
<ms-base-table v-bind="propsRes" no-disable filter-icon-align-left v-on="propsEvent">
|
||||||
v-bind="propsRes"
|
|
||||||
:first-column-width="44"
|
|
||||||
:secnario-id="props.scenarioId"
|
|
||||||
no-disable
|
|
||||||
filter-icon-align-left
|
|
||||||
v-on="propsEvent"
|
|
||||||
>
|
|
||||||
<template #num="{ record }">
|
<template #num="{ record }">
|
||||||
<span type="text" class="px-0">{{ record.num }}</span>
|
<span type="text" class="px-0">{{ record.num }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -27,12 +20,12 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
@popup-visible-change="handleFilterHidden"
|
@popup-visible-change="handleFilterHidden"
|
||||||
>
|
>
|
||||||
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="triggerModeFilterVisible = true">
|
<MsButton type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="triggerModeFilterVisible = true">
|
||||||
<div class="font-medium">
|
<div class="font-medium">
|
||||||
{{ t(columnConfig.title as string) }}
|
{{ t(columnConfig.title as string) }}
|
||||||
</div>
|
</div>
|
||||||
<icon-down :class="triggerModeFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
<icon-down :class="triggerModeFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||||
</a-button>
|
</MsButton>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="arco-table-filters-content">
|
<div class="arco-table-filters-content">
|
||||||
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
||||||
|
@ -146,10 +139,6 @@
|
||||||
slotName: 'triggerMode',
|
slotName: 'triggerMode',
|
||||||
showTooltip: true,
|
showTooltip: true,
|
||||||
titleSlotName: 'triggerModeFilter',
|
titleSlotName: 'triggerModeFilter',
|
||||||
sortable: {
|
|
||||||
sortDirections: ['ascend', 'descend'],
|
|
||||||
sorter: true,
|
|
||||||
},
|
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -157,10 +146,6 @@
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
slotName: 'status',
|
slotName: 'status',
|
||||||
titleSlotName: 'statusFilter',
|
titleSlotName: 'statusFilter',
|
||||||
sortable: {
|
|
||||||
sortDirections: ['ascend', 'descend'],
|
|
||||||
sorter: true,
|
|
||||||
},
|
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
v-model:selected-keys="selectedKeys"
|
v-model:selected-keys="selectedKeys"
|
||||||
:data="folderTree"
|
:data="folderTree"
|
||||||
:keyword="moduleKeyword"
|
:keyword="moduleKeyword"
|
||||||
:node-more-actions="folderMoreActions"
|
|
||||||
:default-expand-all="isExpandAll"
|
:default-expand-all="isExpandAll"
|
||||||
:expand-all="isExpandAll"
|
:expand-all="isExpandAll"
|
||||||
:empty-text="t('apiScenario.tree.noMatchModule')"
|
:empty-text="t('apiScenario.tree.noMatchModule')"
|
||||||
|
@ -48,7 +47,6 @@
|
||||||
|
|
||||||
import { getModuleCount, getModuleTree } from '@/api/modules/api-test/scenario';
|
import { getModuleCount, getModuleTree } from '@/api/modules/api-test/scenario';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
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';
|
||||||
|
|
||||||
|
@ -65,27 +63,10 @@
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
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(() => {
|
const virtualListProps = computed(() => {
|
||||||
return {
|
return {
|
||||||
height: 'calc(100vh - 343px)',
|
height: '40vh',
|
||||||
threshold: 200,
|
threshold: 200,
|
||||||
fixedSize: true,
|
fixedSize: true,
|
||||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
|
@ -96,30 +77,10 @@
|
||||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
const focusNodeKey = ref<string | number>('');
|
const focusNodeKey = ref<string | number>('');
|
||||||
const selectedKeys = ref<Array<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 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 modulesCount = ref<Record<string, number>>({});
|
||||||
const allScenarioCount = computed(() => modulesCount.value.all || 0);
|
|
||||||
const isExpandAll = ref(props.isExpandAll);
|
const isExpandAll = ref(props.isExpandAll);
|
||||||
const rootModulesName = ref<string[]>([]); // 根模块名称列表
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化模块树
|
* 初始化模块树
|
||||||
|
@ -164,10 +125,6 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
function changeExpand() {
|
|
||||||
isExpandAll.value = !isExpandAll.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理文件夹树节点选中事件
|
* 处理文件夹树节点选中事件
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -92,6 +92,7 @@
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
titleSlotName: 'typeTitle',
|
titleSlotName: 'typeTitle',
|
||||||
|
typeTitleTooltip: t('apiScenario.params.typeTooltip'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'apiScenario.params.paramValue',
|
title: 'apiScenario.params.paramValue',
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="condition">
|
<div class="condition">
|
||||||
<div>
|
<div>
|
||||||
<precondition v-model:config="preProcessorConfig" :is-definition="false" @change="emit('change')" />
|
<precondition
|
||||||
|
v-model:config="preProcessorConfig"
|
||||||
|
:is-definition="false"
|
||||||
|
sql-code-editor-height="300px"
|
||||||
|
is-scenario
|
||||||
|
@change="emit('change')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<a-divider class="my-[8px]" type="dashed" />
|
<a-divider class="my-[8px]" type="dashed" />
|
||||||
<div>
|
<div>
|
||||||
|
@ -9,6 +15,8 @@
|
||||||
v-model:config="postProcessorConfig"
|
v-model:config="postProcessorConfig"
|
||||||
:is-definition="false"
|
:is-definition="false"
|
||||||
:layout="activeLayout"
|
:layout="activeLayout"
|
||||||
|
sql-code-editor-height="300px"
|
||||||
|
is-scenario
|
||||||
@change="emit('change')"
|
@change="emit('change')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,10 +45,6 @@
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.condition {
|
.condition {
|
||||||
.ms-scroll-bar();
|
.ms-scroll-bar();
|
||||||
|
@apply h-full overflow-auto;
|
||||||
overflow: auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 700px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -242,6 +242,9 @@
|
||||||
</template>
|
</template>
|
||||||
</TableFilter>
|
</TableFilter>
|
||||||
</template>
|
</template>
|
||||||
|
<template #stepTotal="{ record }">
|
||||||
|
{{ record.stepTotal }}
|
||||||
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsButton
|
<MsButton
|
||||||
v-permission="['PROJECT_API_SCENARIO:READ+UPDATE']"
|
v-permission="['PROJECT_API_SCENARIO:READ+UPDATE']"
|
||||||
|
@ -505,14 +508,18 @@
|
||||||
style="padding-top: 10px"
|
style="padding-top: 10px"
|
||||||
>
|
>
|
||||||
<a-switch v-model="batchForm.append" class="mr-1" size="small" type="line" />
|
<a-switch v-model="batchForm.append" class="mr-1" size="small" type="line" />
|
||||||
<a-tooltip :content="t('caseManagement.featureCase.enableTags')">
|
<span class="flex items-center">
|
||||||
<span class="flex items-center">
|
<span class="mr-1">{{ t('caseManagement.featureCase.appendTag') }}</span>
|
||||||
<span class="mr-1">{{ t('caseManagement.featureCase.appendTag') }}</span>
|
<span class="mt-[2px]">
|
||||||
<span class="mt-[2px]">
|
<a-tooltip>
|
||||||
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
|
<IconQuestionCircle class="h-[16px] w-[16px] text-[rgb(var(--primary-5))]" />
|
||||||
</span>
|
<template #content>
|
||||||
|
<div>{{ t('caseManagement.featureCase.enableTags') }}</div>
|
||||||
|
<div>{{ t('caseManagement.featureCase.closeTags') }}</div>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<a-button type="secondary" :disabled="batchUpdateLoading" @click="cancelBatch">
|
<a-button type="secondary" :disabled="batchUpdateLoading" @click="cancelBatch">
|
||||||
|
@ -553,7 +560,7 @@
|
||||||
{{ t('api_scenario.table.batchModalSubTitle', { count: tableSelected.length }) }}
|
{{ t('api_scenario.table.batchModalSubTitle', { count: tableSelected.length }) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="isBatchMove">
|
<div v-else-if="isBatchMove" class="flex items-center">
|
||||||
{{ t('common.batchMove') }}
|
{{ t('common.batchMove') }}
|
||||||
<div
|
<div
|
||||||
class="one-line-text ml-[4px] max-w-[100%] text-[var(--color-text-4)]"
|
class="one-line-text ml-[4px] max-w-[100%] text-[var(--color-text-4)]"
|
||||||
|
@ -795,6 +802,7 @@
|
||||||
{
|
{
|
||||||
title: 'apiScenario.table.columns.steps',
|
title: 'apiScenario.table.columns.steps',
|
||||||
dataIndex: 'stepTotal',
|
dataIndex: 'stepTotal',
|
||||||
|
slotName: 'stepTotal',
|
||||||
showInTable: false,
|
showInTable: false,
|
||||||
showDrag: true,
|
showDrag: true,
|
||||||
width: 100,
|
width: 100,
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
| ScenarioAddStepActionType.SCRIPT_OPERATION,
|
| ScenarioAddStepActionType.SCRIPT_OPERATION,
|
||||||
step?: ScenarioStepItem
|
step?: ScenarioStepItem
|
||||||
);
|
);
|
||||||
(e: 'addDone');
|
(e: 'addDone', newStep: ScenarioStepItem);
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
} else {
|
} else {
|
||||||
steps.value.push(defaultLoopStep);
|
steps.value.push(defaultLoopStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone', defaultLoopStep);
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.CONDITION_CONTROL:
|
case ScenarioAddStepActionType.CONDITION_CONTROL:
|
||||||
const defaultConditionStep = buildInsertStepInfos(
|
const defaultConditionStep = buildInsertStepInfos(
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
} else {
|
} else {
|
||||||
steps.value.push(defaultConditionStep);
|
steps.value.push(defaultConditionStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone', defaultConditionStep);
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.ONLY_ONCE_CONTROL:
|
case ScenarioAddStepActionType.ONLY_ONCE_CONTROL:
|
||||||
const defaultOnlyOnceStep = buildInsertStepInfos(
|
const defaultOnlyOnceStep = buildInsertStepInfos(
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
} else {
|
} else {
|
||||||
steps.value.push(defaultOnlyOnceStep);
|
steps.value.push(defaultOnlyOnceStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone', defaultOnlyOnceStep);
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.WAIT_TIME:
|
case ScenarioAddStepActionType.WAIT_TIME:
|
||||||
const defaultWaitTimeStep = buildInsertStepInfos(
|
const defaultWaitTimeStep = buildInsertStepInfos(
|
||||||
|
@ -162,7 +162,7 @@
|
||||||
} else {
|
} else {
|
||||||
steps.value.push(defaultWaitTimeStep);
|
steps.value.push(defaultWaitTimeStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone', defaultWaitTimeStep);
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.IMPORT_SYSTEM_API:
|
case ScenarioAddStepActionType.IMPORT_SYSTEM_API:
|
||||||
case ScenarioAddStepActionType.CUSTOM_API:
|
case ScenarioAddStepActionType.CUSTOM_API:
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
:popup-translate="[-7, -10]"
|
:popup-translate="[-7, -10]"
|
||||||
@other-create="(type, step) => emit('otherCreate', type, step, activeCreateAction)"
|
@other-create="(type, step) => emit('otherCreate', type, step, activeCreateAction)"
|
||||||
@close="handleActionsClose"
|
@close="handleActionsClose"
|
||||||
@add-done="emit('addDone')"
|
@add-done="emit('addDone', $event)"
|
||||||
>
|
>
|
||||||
<span></span>
|
<span></span>
|
||||||
</createStepActions>
|
</createStepActions>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
step?: ScenarioStepItem,
|
step?: ScenarioStepItem,
|
||||||
activeCreateAction?: CreateStepAction
|
activeCreateAction?: CreateStepAction
|
||||||
);
|
);
|
||||||
(e: 'addDone');
|
(e: 'addDone', newStep: ScenarioStepItem);
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-[4px]">
|
<div class="flex items-center gap-[4px]">
|
||||||
{{ t('apiScenario.sum') }}
|
{{ t('apiScenario.sum') }}
|
||||||
<div class="text-[rgb(var(--primary-5))]">{{ totalStepCount }}</div>
|
<div class="text-[rgb(var(--primary-5))]">{{ topLevelStepCount }}</div>
|
||||||
{{ t('apiScenario.steps') }}
|
{{ t('apiScenario.steps') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -171,6 +171,7 @@
|
||||||
const stepTreeRef = ref<InstanceType<typeof stepTree>>();
|
const stepTreeRef = ref<InstanceType<typeof stepTree>>();
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
|
|
||||||
|
const topLevelStepCount = computed(() => scenario.value.steps.length);
|
||||||
const totalStepCount = computed(() => countNodes(scenario.value.steps));
|
const totalStepCount = computed(() => countNodes(scenario.value.steps));
|
||||||
|
|
||||||
function handleChangeAll(value: boolean | (string | number | boolean)[]) {
|
function handleChangeAll(value: boolean | (string | number | boolean)[]) {
|
||||||
|
@ -267,7 +268,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function batchDelete() {
|
function batchDelete() {
|
||||||
deleteNodes(scenario.value.steps, checkedKeys.value, 'uniqueId');
|
deleteNodes(scenario.value.steps, checkedKeys.value, (node) => !node.isQuoteScenarioStep, 'uniqueId');
|
||||||
Message.success(t('common.deleteSuccess'));
|
Message.success(t('common.deleteSuccess'));
|
||||||
if (scenario.value.steps.length === 0) {
|
if (scenario.value.steps.length === 0) {
|
||||||
checkedAll.value = false;
|
checkedAll.value = false;
|
||||||
|
@ -357,13 +358,16 @@
|
||||||
if (!node.enable) {
|
if (!node.enable) {
|
||||||
// 如果步骤未开启,则删除已选 uniqueId,方便下面waitingDebugStepDetails详情判断是否携带
|
// 如果步骤未开启,则删除已选 uniqueId,方便下面waitingDebugStepDetails详情判断是否携带
|
||||||
checkedKeysSet.delete(node.uniqueId);
|
checkedKeysSet.delete(node.uniqueId);
|
||||||
node.executeStatus = undefined;
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE; // 已选中但未开启的步骤显示未执行状态
|
||||||
} else {
|
} else {
|
||||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
node.executeStatus = ScenarioExecuteStatus.EXECUTING; // 已选中且已开启的步骤显示执行中状态
|
||||||
}
|
}
|
||||||
return !!node.enable;
|
return !!node.enable;
|
||||||
}
|
}
|
||||||
node.executeStatus = undefined; // 未选中的步骤不显示执行状态
|
node.executeStatus = undefined; // 未选中的步骤不显示执行状态
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
return true; // 因为存在选中了子步骤但是没有选中父步骤,所以需要递归完整树
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
const waitingDebugStepDetails = {};
|
const waitingDebugStepDetails = {};
|
||||||
|
|
|
@ -14,14 +14,11 @@
|
||||||
:content="innerData.msCountController.loops?.toString()"
|
:content="innerData.msCountController.loops?.toString()"
|
||||||
:disabled="!innerData.msCountController.loops"
|
:disabled="!innerData.msCountController.loops"
|
||||||
>
|
>
|
||||||
<a-input-number
|
<a-input
|
||||||
v-model:model-value="innerData.msCountController.loops"
|
v-model:model-value="innerData.msCountController.loops"
|
||||||
class="w-[80px] px-[8px]"
|
class="w-[80px] px-[8px]"
|
||||||
size="mini"
|
size="mini"
|
||||||
:step="1"
|
|
||||||
:min="1"
|
|
||||||
hide-button
|
hide-button
|
||||||
:precision="0"
|
|
||||||
model-event="input"
|
model-event="input"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
@blur="handleInputChange"
|
@blur="handleInputChange"
|
||||||
|
@ -29,7 +26,7 @@
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<div class="text-[12px] text-[var(--color-text-4)]">{{ t('apiScenario.num') }}:</div>
|
<div class="text-[12px] text-[var(--color-text-4)]">{{ t('apiScenario.num') }}:</div>
|
||||||
</template>
|
</template>
|
||||||
</a-input-number>
|
</a-input>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
<template v-if="innerData.loopType === ScenarioStepLoopTypeEnum.FOREACH">
|
<template v-if="innerData.loopType === ScenarioStepLoopTypeEnum.FOREACH">
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.belongProject') }}</div>
|
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.belongProject') }}</div>
|
||||||
<div class="text-[14px] text-[var(--color-text-1)]">
|
<div class="text-[14px] text-[var(--color-text-1)]">
|
||||||
{{ originProjectName }}
|
{{ originProjectInfo?.projectName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.detailName') }}</div>
|
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.detailName') }}</div>
|
||||||
<div class="cursor-pointer text-[14px] text-[rgb(var(--primary-5))]" @click="goDetail">
|
<div class="cursor-pointer text-[14px] text-[rgb(var(--primary-5))]" @click="goDetail">
|
||||||
{{ `【${props.data.resourceNum}】${props.data.resourceName}` }}
|
{{ `【${originProjectInfo?.num}】${originProjectInfo?.name}` }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepItem, ScenarioStepResourceInfo } from '@/models/apiTest/scenario';
|
||||||
import { ScenarioStepType } from '@/enums/apiEnum';
|
import { ScenarioStepType } from '@/enums/apiEnum';
|
||||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
@ -67,12 +67,11 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openNewPage } = useOpenNewPage();
|
const { openNewPage } = useOpenNewPage();
|
||||||
|
|
||||||
const originProjectName = ref('');
|
const originProjectInfo = ref<ScenarioStepResourceInfo>();
|
||||||
|
|
||||||
async function handleVisibleChange(val: boolean) {
|
async function handleVisibleChange(val: boolean) {
|
||||||
if (val && props.data.originProjectId) {
|
if (val && props.data.originProjectId) {
|
||||||
const res = await getStepProjectInfo(props.data.originProjectId);
|
originProjectInfo.value = await getStepProjectInfo(props.data.resourceId || '', props.data.stepType);
|
||||||
originProjectName.value = res.name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +89,7 @@
|
||||||
case _stepType.isQuoteScenario:
|
case _stepType.isQuoteScenario:
|
||||||
openNewPage(ApiTestRouteEnum.API_TEST_SCENARIO, {
|
openNewPage(ApiTestRouteEnum.API_TEST_SCENARIO, {
|
||||||
pId: props.data.originProjectId,
|
pId: props.data.originProjectId,
|
||||||
sId: props.data.resourceId,
|
id: props.data.resourceId,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case _stepType.isQuoteCase:
|
case _stepType.isQuoteCase:
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
:field-names="{ title: 'name', key: 'uniqueId', children: 'children' }"
|
:field-names="{ title: 'name', key: 'uniqueId', children: 'children' }"
|
||||||
:virtual-list-props="{
|
:virtual-list-props="{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
threshold: 20,
|
threshold: 200,
|
||||||
fixedSize: true,
|
fixedSize: true,
|
||||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
}"
|
}"
|
||||||
|
@ -50,10 +50,7 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-1)]">
|
<div class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-1)]">
|
||||||
<MsIcon
|
<MsIcon type="icon-icon_split_turn-down_arrow" :size="14" />
|
||||||
:type="step.expanded ? 'icon-icon_split-turn-down-left' : 'icon-icon_split_turn-down_arrow'"
|
|
||||||
:size="14"
|
|
||||||
/>
|
|
||||||
{{ step.children?.length || 0 }}
|
{{ step.children?.length || 0 }}
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
@ -67,14 +64,14 @@
|
||||||
></a-switch>
|
></a-switch>
|
||||||
<!-- 步骤执行 -->
|
<!-- 步骤执行 -->
|
||||||
<MsIcon
|
<MsIcon
|
||||||
v-show="!step.isExecuting"
|
v-show="!step.isExecuting && step.enable"
|
||||||
type="icon-icon_play-round_filled"
|
type="icon-icon_play-round_filled"
|
||||||
:size="18"
|
:size="18"
|
||||||
class="cursor-pointer text-[rgb(var(--link-6))]"
|
class="cursor-pointer text-[rgb(var(--link-6))]"
|
||||||
@click.stop="executeStep(step)"
|
@click.stop="executeStep(step)"
|
||||||
/>
|
/>
|
||||||
<MsIcon
|
<MsIcon
|
||||||
v-show="step.isExecuting"
|
v-show="step.isExecuting && step.enable"
|
||||||
type="icon-icon_stop"
|
type="icon-icon_stop"
|
||||||
:size="20"
|
:size="20"
|
||||||
class="cursor-pointer text-[rgb(var(--link-6))]"
|
class="cursor-pointer text-[rgb(var(--link-6))]"
|
||||||
|
@ -171,13 +168,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #extra="step">
|
<template #extra="step">
|
||||||
<stepInsertStepTrigger
|
<stepInsertStepTrigger
|
||||||
v-if="
|
v-if="!step.isQuoteScenarioStep"
|
||||||
!step.isQuoteScenarioStep &&
|
|
||||||
!(
|
|
||||||
step.stepType === ScenarioStepType.API_SCENARIO &&
|
|
||||||
[ScenarioStepRefType.REF, ScenarioStepRefType.PARTIAL_REF].includes(step.refType)
|
|
||||||
)
|
|
||||||
"
|
|
||||||
v-model:selected-keys="selectedKeys"
|
v-model:selected-keys="selectedKeys"
|
||||||
v-model:steps="steps"
|
v-model:steps="steps"
|
||||||
v-permission="['PROJECT_API_DEBUG:READ+ADD', 'PROJECT_API_DEFINITION:READ+UPDATE']"
|
v-permission="['PROJECT_API_DEBUG:READ+ADD', 'PROJECT_API_DEFINITION:READ+UPDATE']"
|
||||||
|
@ -331,7 +322,13 @@
|
||||||
<a-radio :value="false">{{ t('apiScenario.sourceScenario') }}</a-radio>
|
<a-radio :value="false">{{ t('apiScenario.sourceScenario') }}</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('apiScenario.currentScenarioTip')">
|
<a-form-item
|
||||||
|
:label="
|
||||||
|
scenarioConfigForm.useCurrentScenarioParam
|
||||||
|
? t('apiScenario.currentScenarioTip')
|
||||||
|
: t('apiScenario.sourceScenarioTip')
|
||||||
|
"
|
||||||
|
>
|
||||||
<a-radio-group v-model:model-value="scenarioConfigForm.useBothScenarioParam">
|
<a-radio-group v-model:model-value="scenarioConfigForm.useBothScenarioParam">
|
||||||
<a-radio :value="false">{{ t('apiScenario.empty') }}</a-radio>
|
<a-radio :value="false">{{ t('apiScenario.empty') }}</a-radio>
|
||||||
<a-radio :value="true">
|
<a-radio :value="true">
|
||||||
|
@ -1076,7 +1073,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAddStepDone() {
|
function handleAddStepDone(newStep: ScenarioStepItem) {
|
||||||
|
selectedKeys.value = [newStep.uniqueId]; // 选中新添加的步骤
|
||||||
emit('stepAdd');
|
emit('stepAdd');
|
||||||
scenario.value.unSaved = true;
|
scenario.value.unSaved = true;
|
||||||
}
|
}
|
||||||
|
@ -1098,7 +1096,7 @@
|
||||||
// 复制 api、引用 api、自定义 api打开抽屉
|
// 复制 api、引用 api、自定义 api打开抽屉
|
||||||
activeStep.value = step;
|
activeStep.value = step;
|
||||||
if (
|
if (
|
||||||
(stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
(stepDetails.value[step.id] === undefined && step.copyFromStepId && !step.isNew) ||
|
||||||
(stepDetails.value[step.id] === undefined && !step.isNew)
|
(stepDetails.value[step.id] === undefined && !step.isNew)
|
||||||
) {
|
) {
|
||||||
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||||
|
@ -1109,7 +1107,7 @@
|
||||||
activeStep.value = step;
|
activeStep.value = step;
|
||||||
if (
|
if (
|
||||||
_stepType.isCopyCase &&
|
_stepType.isCopyCase &&
|
||||||
((stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
((stepDetails.value[step.id] === undefined && step.copyFromStepId && !step.isNew) ||
|
||||||
(stepDetails.value[step.id] === undefined && !step.isNew))
|
(stepDetails.value[step.id] === undefined && !step.isNew))
|
||||||
) {
|
) {
|
||||||
// 只有复制的 case 需要查看步骤详情,引用的无法更改所以不需要在此初始化详情
|
// 只有复制的 case 需要查看步骤详情,引用的无法更改所以不需要在此初始化详情
|
||||||
|
@ -1444,11 +1442,7 @@
|
||||||
*/
|
*/
|
||||||
function addCustomApiStep(request: RequestParam) {
|
function addCustomApiStep(request: RequestParam) {
|
||||||
request.isNew = false;
|
request.isNew = false;
|
||||||
stepDetails.value[request.stepId] = {
|
stepDetails.value[request.stepId] = request;
|
||||||
...request,
|
|
||||||
customizeRequest: true,
|
|
||||||
customizeRequestEnvEnable: request.customizeRequestEnvEnable,
|
|
||||||
};
|
|
||||||
scenario.value.stepFileParam[request.stepId] = {
|
scenario.value.stepFileParam[request.stepId] = {
|
||||||
linkFileIds: request.linkFileIds,
|
linkFileIds: request.linkFileIds,
|
||||||
uploadFileIds: request.uploadFileIds,
|
uploadFileIds: request.uploadFileIds,
|
||||||
|
@ -1490,6 +1484,7 @@
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
selectedKeys.value = [request.stepId]; // 选中新添加的步骤
|
||||||
emit('stepAdd');
|
emit('stepAdd');
|
||||||
scenario.value.unSaved = true;
|
scenario.value.unSaved = true;
|
||||||
}
|
}
|
||||||
|
@ -1572,6 +1567,7 @@
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
selectedKeys.value = [id]; // 选中新添加的步骤
|
||||||
emit('stepAdd');
|
emit('stepAdd');
|
||||||
scenario.value.unSaved = true;
|
scenario.value.unSaved = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,70 +12,77 @@ export default function updateStepStatus(
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < steps.length; i++) {
|
for (let i = 0; i < steps.length; i++) {
|
||||||
const node = steps[i];
|
const node = steps[i];
|
||||||
if (
|
if (node.enable) {
|
||||||
[
|
// 启用的步骤才计算
|
||||||
ScenarioStepType.LOOP_CONTROLLER,
|
if (
|
||||||
ScenarioStepType.IF_CONTROLLER,
|
[
|
||||||
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
ScenarioStepType.LOOP_CONTROLLER,
|
||||||
ScenarioStepType.API_SCENARIO,
|
ScenarioStepType.IF_CONTROLLER,
|
||||||
].includes(node.stepType)
|
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
||||||
) {
|
ScenarioStepType.API_SCENARIO,
|
||||||
if (!node.executeStatus) {
|
].includes(node.stepType)
|
||||||
// 没有执行状态,说明未参与执行,直接跳过
|
) {
|
||||||
break;
|
if (!node.executeStatus) {
|
||||||
}
|
// 没有执行状态,说明未参与执行,直接跳过
|
||||||
// 逻辑控制器和场景内部可以放入任意步骤,所以它的最终执行结果是根据内部步骤的执行结果来判断的
|
// eslint-disable-next-line no-continue
|
||||||
let hasNotExecuted = false;
|
continue;
|
||||||
let hasFailure = false;
|
}
|
||||||
let hasFakeError = false;
|
// 逻辑控制器和场景内部可以放入任意步骤,所以它的最终执行结果是根据内部步骤的执行结果来判断的
|
||||||
if (!node.children || node.children.length === 0) {
|
let hasNotExecuted = false;
|
||||||
// 逻辑控制器内无步骤,则直接是未执行
|
let hasFailure = false;
|
||||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
let hasFakeError = false;
|
||||||
} else {
|
if (!node.children || node.children.length === 0) {
|
||||||
for (let j = 0; j < node.children.length; j++) {
|
// 逻辑控制器内无步骤,则直接是未执行
|
||||||
const childNode = node.children[j];
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
updateStepStatus([childNode], stepResponses);
|
} else {
|
||||||
if (
|
for (let j = 0; j < node.children.length; j++) {
|
||||||
childNode.executeStatus &&
|
const childNode = node.children[j];
|
||||||
[ScenarioExecuteStatus.EXECUTING, ScenarioExecuteStatus.UN_EXECUTE].includes(childNode.executeStatus)
|
updateStepStatus([childNode], stepResponses);
|
||||||
) {
|
if (childNode.executeStatus === ScenarioExecuteStatus.FAILED) {
|
||||||
// 子节点未执行或正在执行,则逻辑控制器也是未执行
|
// 子节点有一个失败,逻辑控制器就是失败
|
||||||
hasNotExecuted = true;
|
hasFailure = true;
|
||||||
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAILED) {
|
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAKE_ERROR) {
|
||||||
// 子节点有一个失败,逻辑控制器就是失败
|
// 子节点有一个误报,逻辑控制器就是误报
|
||||||
hasFailure = true;
|
hasFakeError = true;
|
||||||
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAKE_ERROR) {
|
} else if (
|
||||||
// 子节点有一个误报,逻辑控制器就是误报
|
childNode.executeStatus &&
|
||||||
hasFakeError = true;
|
[ScenarioExecuteStatus.EXECUTING, ScenarioExecuteStatus.UN_EXECUTE].includes(childNode.executeStatus)
|
||||||
|
) {
|
||||||
|
// 子节点未执行或正在执行,则逻辑控制器也是未执行
|
||||||
|
hasNotExecuted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 递归完子节点后,判断当前逻辑控制器的状态
|
||||||
|
if (hasFailure) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
||||||
|
} else if (hasFakeError) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAKE_ERROR;
|
||||||
|
} else if (hasNotExecuted) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
} else {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 递归完子节点后,判断当前逻辑控制器的状态
|
} else if (node.stepType === ScenarioStepType.CONSTANT_TIMER) {
|
||||||
if (hasNotExecuted) {
|
// 等待时间直接设置为成功
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
|
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||||
|
// 非逻辑控制器直接更改本身状态
|
||||||
|
if (stepResponses[node.uniqueId] && stepResponses[node.uniqueId].length > 0) {
|
||||||
|
// 存在多个请求结果说明是循环控制器下的步骤,需要判断其子步骤的执行结果
|
||||||
|
if (stepResponses[node.uniqueId].some((report) => !report.isSuccessful)) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
||||||
|
} else if (
|
||||||
|
stepResponses[node.uniqueId].some((report) => report.status === ScenarioExecuteStatus.FAKE_ERROR)
|
||||||
|
) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAKE_ERROR;
|
||||||
|
} else {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
} else if (hasFailure) {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
|
||||||
} else if (hasFakeError) {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.FAKE_ERROR;
|
|
||||||
} else {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (node.stepType === ScenarioStepType.CONSTANT_TIMER) {
|
|
||||||
// 等待时间直接设置为成功
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
|
||||||
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
|
||||||
// 非逻辑控制器直接更改本身状态
|
|
||||||
if (stepResponses[node.uniqueId] && stepResponses[node.uniqueId].length > 0) {
|
|
||||||
if (stepResponses[node.uniqueId].some((report) => !report.isSuccessful)) {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
|
||||||
} else if (stepResponses[node.uniqueId].some((report) => report.status === ScenarioExecuteStatus.FAKE_ERROR)) {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.FAKE_ERROR;
|
|
||||||
} else {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,8 +200,8 @@
|
||||||
function share() {
|
function share() {
|
||||||
if (isSupported) {
|
if (isSupported) {
|
||||||
const url = window.location.href;
|
const url = window.location.href;
|
||||||
const dIdParam = `&sId=${scenario.value.id}`;
|
const dIdParam = `&id=${scenario.value.id}`;
|
||||||
const copyUrl = url.includes('sId') ? url.split('&sId')[0] : url;
|
const copyUrl = url.includes('id') ? url.split('&id')[0] : url;
|
||||||
copy(`${copyUrl}${dIdParam}`);
|
copy(`${copyUrl}${dIdParam}`);
|
||||||
Message.success(t('common.copySuccess'));
|
Message.success(t('common.copySuccess'));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -192,19 +192,22 @@
|
||||||
if (scenario.reportId === data.reportId) {
|
if (scenario.reportId === data.reportId) {
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
data.taskResult.requestResults.forEach((result) => {
|
data.taskResult.requestResults.forEach((result) => {
|
||||||
if (scenario.stepResponses[result.stepId] === undefined) {
|
if (result.stepId) {
|
||||||
scenario.stepResponses[result.stepId] = [];
|
// 过滤掉前后置配置的执行结果,没有步骤 id
|
||||||
}
|
if (scenario.stepResponses[result.stepId] === undefined) {
|
||||||
scenario.stepResponses[result.stepId].push({
|
scenario.stepResponses[result.stepId] = [];
|
||||||
...result,
|
}
|
||||||
console: data.taskResult.console,
|
scenario.stepResponses[result.stepId].push({
|
||||||
});
|
...result,
|
||||||
if (result.status === ScenarioExecuteStatus.FAKE_ERROR) {
|
console: data.taskResult.console,
|
||||||
scenario.executeFakeErrorCount += 1;
|
});
|
||||||
} else if (result.isSuccessful) {
|
if (result.status === ScenarioExecuteStatus.FAKE_ERROR) {
|
||||||
scenario.executeSuccessCount += 1;
|
scenario.executeFakeErrorCount += 1;
|
||||||
} else {
|
} else if (result.isSuccessful) {
|
||||||
scenario.executeFailCount += 1;
|
scenario.executeSuccessCount += 1;
|
||||||
|
} else {
|
||||||
|
scenario.executeFailCount += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -306,6 +309,8 @@
|
||||||
if (node.enable) {
|
if (node.enable) {
|
||||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||||
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
|
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
|
||||||
|
} else {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
}
|
}
|
||||||
return !!node.enable;
|
return !!node.enable;
|
||||||
});
|
});
|
||||||
|
@ -573,8 +578,8 @@
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
selectRecycleCount();
|
selectRecycleCount();
|
||||||
if (route.query.sId) {
|
if (route.query.id) {
|
||||||
openScenarioTab(route.query.sId as string);
|
openScenarioTab(route.query.id as string);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,244 +1,262 @@
|
||||||
export default {
|
export default {
|
||||||
'apiScenario.env': 'Environment: {name}',
|
'apiScenario.env': 'Current Environment: {name}',
|
||||||
'apiScenario.allScenario': 'All scenarios',
|
'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 module name',
|
||||||
'apiScenario.tree.folder.allScenario': 'All scenarios',
|
'apiScenario.tree.folder.allScenario': 'All 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/scenario found',
|
||||||
'apiScenario.createSubModule': 'Create sub-module',
|
'apiScenario.createSubModule': 'Create Submodule',
|
||||||
'apiScenario.module.deleteTipTitle': 'Delete {name} module?',
|
'apiScenario.module.deleteTipTitle': 'Delete Module {name}?',
|
||||||
'apiScenario.module.deleteTipContent':
|
'apiScenario.module.deleteTipContent':
|
||||||
'After deletion, all scenarios under the module will be deleted synchronously. Please operate with caution.',
|
'Deleting will also delete all scenarios under the module. Proceed with caution.',
|
||||||
'apiScenario.deleteConfirm': 'Confirm',
|
'apiScenario.deleteConfirm': 'Confirm Delete',
|
||||||
'apiScenario.deleteSuccess': 'Success',
|
'apiScenario.deleteSuccess': 'Delete Successful',
|
||||||
'apiScenario.moveSuccess': 'Success',
|
'apiScenario.moveSuccess': 'Move Successful',
|
||||||
'apiScenario.baseInfo': 'Base info',
|
'apiScenario.baseInfo': 'Basic Information',
|
||||||
'apiScenario.step': 'Step',
|
'apiScenario.step': 'Step',
|
||||||
'apiScenario.params': 'Params',
|
'apiScenario.params': 'Parameters',
|
||||||
'apiScenario.prePost': 'Pre/Post',
|
'apiScenario.prePost': 'Pre/Post',
|
||||||
'apiScenario.assertion': 'Assertion',
|
'apiScenario.assertion': 'Assertion',
|
||||||
'apiScenario.executeHistory': 'Execute history',
|
'apiScenario.executeHistory': 'Execution History',
|
||||||
'apiScenario.changeHistory': 'Change history',
|
'apiScenario.changeHistory': 'Change History',
|
||||||
'apiScenario.dependency': 'Dependencies',
|
'apiScenario.dependency': 'Dependency',
|
||||||
'apiScenario.quote': 'Reference',
|
'apiScenario.quote': 'Quote',
|
||||||
'apiScenario.params.convention': 'Convention parameter',
|
'apiScenario.params.convention': 'Conventional Parameters',
|
||||||
'apiScenario.params.searchPlaceholder': 'Search by name or tag',
|
'apiScenario.params.searchPlaceholder': 'Search by name or tag',
|
||||||
'apiScenario.params.priority':
|
'apiScenario.params.priority':
|
||||||
'Variable priority: Temporary parameters>Scene parameters>Environment parameters>Global parameters; Note: Avoid using variables with the same name. When using variables with the same name, scene level CSV has the highest priority',
|
'Variable Priority: Temporary Parameters > Scenario Parameters > Environment Parameters > Global Parameters; Note: Avoid using variables with the same name. In case of same name variables, scenario-level CSV has the highest priority.',
|
||||||
'apiScenario.params.name': 'Variable name',
|
'apiScenario.params.name': 'Variable Name',
|
||||||
'apiScenario.params.type': 'Type',
|
'apiScenario.params.type': 'Type',
|
||||||
'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.columns.name': 'Name',
|
'apiScenario.params.typeTooltip': 'Json: only supports UI testing',
|
||||||
'apiScenario.table.columns.level': 'Level',
|
'apiScenario.table.columns.name': 'Scenario Name',
|
||||||
'apiScenario.table.columns.status': 'status',
|
'apiScenario.table.columns.level': 'Scenario Level',
|
||||||
'apiScenario.table.columns.runResult': 'Run result',
|
'apiScenario.table.columns.status': 'Status',
|
||||||
|
'apiScenario.table.columns.runResult': 'Execution Result',
|
||||||
'apiScenario.table.columns.tags': 'Tags',
|
'apiScenario.table.columns.tags': 'Tags',
|
||||||
'apiScenario.table.columns.scenarioEnv': 'Scenario environment',
|
'apiScenario.table.columns.scenarioEnv': 'Scenario Environment',
|
||||||
'apiScenario.table.columns.steps': 'Steps',
|
'apiScenario.table.columns.steps': 'Number of Steps',
|
||||||
'apiScenario.table.columns.passRate': 'Pass rate',
|
'apiScenario.table.columns.passRate': 'Pass Rate',
|
||||||
'apiScenario.table.columns.module': 'Module',
|
'apiScenario.table.columns.module': 'Belongs to Module',
|
||||||
'apiScenario.table.columns.createUser': 'Create user',
|
'apiScenario.table.columns.createUser': 'Creator',
|
||||||
'apiScenario.table.columns.createTime': 'Create time',
|
'apiScenario.table.columns.createTime': 'Creation Time',
|
||||||
'apiScenario.table.columns.updateUser': 'Update user',
|
'apiScenario.table.columns.updateUser': 'Updater',
|
||||||
'apiScenario.table.columns.updateTime': 'Update time',
|
'apiScenario.table.columns.updateTime': 'Update Time',
|
||||||
'apiScenario.table.columns.deleteUser': 'Delete User',
|
'apiScenario.table.columns.operation': 'Operator',
|
||||||
'apiScenario.table.columns.operation': 'Operation',
|
'apiScenario.table.columns.deleteUser': 'Deleter',
|
||||||
'apiScenario.table.columns.deleteTime': 'Delete time',
|
'apiScenario.table.columns.deleteTime': 'Deletion Time',
|
||||||
'api_scenario.table.tableNoDataAndPlease': 'No data yet, please',
|
'api_scenario.table.searchPlaceholder': 'Search by ID/Name/Tag',
|
||||||
|
'api_scenario.table.batchModalSubTitle': '(Selected {count} scenarios)',
|
||||||
|
'api_scenario.table.chooseAttr': 'Choose Attribute',
|
||||||
|
'api_scenario.table.attrRequired': 'Attribute cannot be empty',
|
||||||
|
'api_scenario.table.batchUpdate': 'Batch Update to',
|
||||||
|
'api_scenario.table.valueRequired': 'Attribute value cannot be empty',
|
||||||
|
'api_scenario.table.deleteScenarioTipTitle': 'Confirm Delete {name}?',
|
||||||
|
'api_scenario.table.batchDeleteScenarioTip': 'Confirm delete {count} selected scenarios?',
|
||||||
|
'api_scenario.table.deleteScenarioTip':
|
||||||
|
'Deleting may cause failure in resources referencing this scenario. Proceed with caution!',
|
||||||
|
'api_scenario.table.apiStatus': 'Status',
|
||||||
|
'api_scenario.table.status.underway': 'Underway',
|
||||||
|
'api_scenario.table.status.completed': 'Completed',
|
||||||
|
'api_scenario.table.status.deprecate': 'Deprecated',
|
||||||
|
'api_scenario.table.tableNoDataAndPlease': 'No data, please',
|
||||||
'api_scenario.table.or': 'or',
|
'api_scenario.table.or': 'or',
|
||||||
'apiScenario.execute': 'Execute',
|
'apiScenario.execute': 'Execute',
|
||||||
'apiScenario.prev': 'Last time',
|
'apiScenario.prev': 'Previous',
|
||||||
'apiScenario.next': 'Next time',
|
'apiScenario.next': 'Next',
|
||||||
'apiScenario.sumLoop': 'A total of {count} loops',
|
'apiScenario.sumLoop': 'Total {count} loops',
|
||||||
'apiScenario.times': 'bout',
|
'apiScenario.times': 'times',
|
||||||
'apiScenario.executionResult': 'Execution result',
|
'apiScenario.executionResult': 'Execution Result',
|
||||||
// 批量操作文案
|
'apiScenario.refreshRefScenario': 'Refresh Referenced Scenario Data',
|
||||||
'api_scenario.batch_operation.success': 'Success {success} ,error {error} ',
|
'apiScenario.updateRefScenarioSuccess': 'Referenced scenario data has been updated',
|
||||||
'api_scenario.table.batchMoveConfirm': 'Ready to {opt} {count} scenarios',
|
'apiScenario.replaceSuccess': 'Step replacement successful',
|
||||||
'apiScenario.name': 'Scene name',
|
// Batch operation text
|
||||||
'apiScenario.nameRequired': 'Scene name cannot be empty',
|
'api_scenario.batch_operation.success': 'Successfully {opt} to {name}',
|
||||||
'apiScenario.namePlaceholder': 'Please enter scene name',
|
'api_scenario.table.batchMoveConfirm': '{opt} {count} scenarios to selected module',
|
||||||
'apiScenario.belongModule': 'Belonging module',
|
'apiScenario.name': 'Scenario Name',
|
||||||
'apiScenario.level': 'Scene level',
|
'apiScenario.nameRequired': 'Scenario name cannot be empty',
|
||||||
'apiScenario.status': 'Scene status',
|
'apiScenario.namePlaceholder': 'Please enter scenario name',
|
||||||
|
'apiScenario.belongModule': 'Belongs to Module',
|
||||||
|
'apiScenario.level': 'Scenario Level',
|
||||||
|
'apiScenario.status': 'Scenario Status',
|
||||||
'apiScenario.descPlaceholder': 'Please describe the scenario',
|
'apiScenario.descPlaceholder': 'Please describe the scenario',
|
||||||
'apiScenario.addStep': 'Add steps',
|
'apiScenario.addStep': 'Add Step',
|
||||||
'apiScenario.requestScenario': 'Request/Scenario',
|
'apiScenario.requestScenario': 'Request/Scenario',
|
||||||
'apiScenario.importSystemApi': 'Import system requests',
|
'apiScenario.importSystemApi': 'Import System Request',
|
||||||
'apiScenario.customApi': 'Custom request',
|
'apiScenario.customApi': 'Custom Request',
|
||||||
'apiScenario.logicControl': 'Logic control',
|
'apiScenario.logicControl': 'Logic Control',
|
||||||
'apiScenario.loopControl': 'Loop controller',
|
'apiScenario.loopControl': 'Loop Controller',
|
||||||
'apiScenario.tutorial': 'Tutorial',
|
'apiScenario.tutorial': 'Tutorial',
|
||||||
'apiScenario.conditionControl': 'Conditional controller',
|
'apiScenario.conditionControl': 'Condition Controller',
|
||||||
'apiScenario.onlyOnceControl': 'Only once controller',
|
'apiScenario.onlyOnceControl': 'Only Once Controller',
|
||||||
'apiScenario.other': 'Other',
|
'apiScenario.other': 'Other',
|
||||||
'apiScenario.scriptOperation': 'Script operation',
|
'apiScenario.scriptOperation': 'Script Operation',
|
||||||
'apiScenario.waitTime': 'Waiting time',
|
'apiScenario.waitTime': 'Wait Time',
|
||||||
'apiScenario.quoteApi': 'Reference API',
|
'apiScenario.quoteApi': 'Quote API',
|
||||||
'apiScenario.copyApi': 'Copy API',
|
'apiScenario.copyApi': 'Copy API',
|
||||||
'apiScenario.quoteCase': 'Reference CASE',
|
'apiScenario.quoteCase': 'Quote CASE',
|
||||||
'apiScenario.copyCase': 'Copy CASE',
|
'apiScenario.copyCase': 'Copy CASE',
|
||||||
'apiScenario.quoteScenario': 'Reference scene',
|
'apiScenario.quoteScenario': 'Quote Scenario',
|
||||||
'apiScenario.copyScenario': 'Copy scene',
|
'apiScenario.copyScenario': 'Copy Scenario',
|
||||||
'apiScenario.sum': 'Common',
|
'apiScenario.sum': 'Total',
|
||||||
'apiScenario.steps': 'steps',
|
'apiScenario.steps': 'steps',
|
||||||
'apiScenario.expandAllStep': 'Expand all substeps',
|
'apiScenario.expandAllStep': 'Expand All Substeps',
|
||||||
'apiScenario.collapseAllStep': 'Collapse all substeps',
|
'apiScenario.collapseAllStep': 'Collapse All Substeps',
|
||||||
'apiScenario.executeTime': 'Execution time',
|
'apiScenario.executeTime': 'Execution Time:',
|
||||||
'apiScenario.executeResult': 'Execution result',
|
'apiScenario.executeResult': 'Execution Result',
|
||||||
'apiScenario.checkReport': 'View report',
|
'apiScenario.checkReport': 'View Report',
|
||||||
'apiScenario.searchByName': 'Search by step name/description',
|
'apiScenario.searchByName': 'Search by step name/description',
|
||||||
'apiScenario.api': 'API',
|
'apiScenario.api': 'API',
|
||||||
'apiScenario.case': 'CASE',
|
'apiScenario.case': 'Case',
|
||||||
'apiScenario.scenario': 'Scenario',
|
'apiScenario.scenario': 'Scenario',
|
||||||
'apiScenario.sumSelected': 'Total selection',
|
'apiScenario.sumSelected': 'Selected',
|
||||||
'apiScenario.scenarioConfig': 'Scene configuration',
|
'apiScenario.scenarioConfig': 'Scenario Configuration',
|
||||||
'apiScenario.noMatchStep': 'No matching step data yet',
|
'apiScenario.noMatchStep': 'No matching step data',
|
||||||
'apiScenario.pleaseInputStepName': 'Please enter a step name',
|
'apiScenario.pleaseInputStepName': 'Please enter step name',
|
||||||
'apiScenario.belongProject': 'Project',
|
'apiScenario.belongProject': 'Belongs to Project',
|
||||||
'apiScenario.detailName': 'Name',
|
'apiScenario.detailName': 'Name',
|
||||||
'apiScenario.crossProject': 'Cross-project',
|
'apiScenario.crossProject': 'Cross Project',
|
||||||
'apiScenario.expandStepTip': 'Expand {count} substeps',
|
'apiScenario.expandStepTip': 'Expand {count} substeps',
|
||||||
'apiScenario.collapseStepTip': 'Collapse {count} substeps',
|
'apiScenario.collapseStepTip': 'Collapse {count} substeps',
|
||||||
'apiScenario.inside': 'Add substep',
|
'apiScenario.inside': 'Add Substep',
|
||||||
'apiScenario.before': 'Insert above',
|
'apiScenario.before': 'Insert Above',
|
||||||
'apiScenario.after': 'Insert below',
|
'apiScenario.after': 'Insert Below',
|
||||||
'apiScenario.num': 'frequency',
|
'apiScenario.num': 'Number',
|
||||||
'apiScenario.space': 'Interval(ms)',
|
'apiScenario.space': 'Interval (ms)',
|
||||||
'apiScenario.timeout': 'Timeout(ms)',
|
'apiScenario.timeout': 'Timeout (ms)',
|
||||||
'apiScenario.waitTimeMs': 'Wait(ms)',
|
'apiScenario.waitTimeMs': 'Wait (ms)',
|
||||||
'apiScenario.pleaseInputStepDesc': 'Please enter a step description',
|
'apiScenario.pleaseInputStepDesc': 'Please enter step description',
|
||||||
'apiScenario.variable': 'Variable name {suffix}',
|
'apiScenario.variable': 'Variable Name {suffix}',
|
||||||
'apiScenario.valuePrefix': 'variable prefix',
|
'apiScenario.valuePrefix': 'Variable Prefix',
|
||||||
'apiScenario.value': 'variable',
|
'apiScenario.value': 'Variable Value',
|
||||||
'apiScenario.conditionValue': 'variable',
|
'apiScenario.conditionValue': 'Variable Value',
|
||||||
'apiScenario.msWhileVariableValue': 'variable',
|
'apiScenario.msWhileVariableValue': 'Variable Value',
|
||||||
'apiScenario.msWhileVariableScriptValue': 'expression',
|
'apiScenario.msWhileVariableScriptValue': 'Expression',
|
||||||
'apiScenario.condition': 'condition',
|
'apiScenario.condition': 'Condition',
|
||||||
'apiScenario.expression': 'expression',
|
'apiScenario.expression': 'Expression',
|
||||||
'apiScenario.equal': 'equal',
|
'apiScenario.equal': 'Equal',
|
||||||
'apiScenario.notEqualTo': 'not equal to',
|
'apiScenario.notEqualTo': 'Not Equal',
|
||||||
'apiScenario.greater': 'more than the',
|
'apiScenario.greater': 'Greater',
|
||||||
'apiScenario.less': 'less than',
|
'apiScenario.less': 'Less',
|
||||||
'apiScenario.greaterOrEqual': 'greater or equal to',
|
'apiScenario.greaterOrEqual': 'Greater or Equal',
|
||||||
'apiScenario.lessOrEqual': 'less than or equal to',
|
'apiScenario.lessOrEqual': 'Less or Equal',
|
||||||
'apiScenario.include': 'Include',
|
'apiScenario.include': 'Include',
|
||||||
'apiScenario.notInclude': 'Not included',
|
'apiScenario.notInclude': 'Not Include',
|
||||||
'apiScenario.null': 'Null',
|
'apiScenario.null': 'Null',
|
||||||
'apiScenario.notNull': 'Not Null',
|
'apiScenario.notNull': 'Not Null',
|
||||||
'apiScenario.range': 'Scope',
|
'apiScenario.range': 'Range',
|
||||||
'apiScenario.topStep': 'First level steps',
|
'apiScenario.topStep': 'Top-level Step',
|
||||||
'apiScenario.allStep': 'All substeps',
|
'apiScenario.allStep': 'All Substeps',
|
||||||
'apiScenario.saveAsApi': 'Save as new interface',
|
'apiScenario.saveAsApi': 'Save as New API',
|
||||||
'apiScenario.scenarioLevel': 'Scene level',
|
'apiScenario.scenarioLevel': 'Scenario Level',
|
||||||
'apiScenario.running': 'Executing',
|
'apiScenario.running': 'Running',
|
||||||
'apiScenario.unExecute': 'Not performed',
|
'apiScenario.unExecute': 'Not Executed',
|
||||||
'apiScenario.response': 'Response content',
|
'apiScenario.response': 'Response Content',
|
||||||
'apiScenario.quoteMode': 'Reference mode',
|
'apiScenario.quoteMode': 'Quote Mode',
|
||||||
'apiScenario.fullQuote': 'Full quote',
|
'apiScenario.fullQuote': 'Full Quote',
|
||||||
'apiScenario.stepQuote': 'Step reference',
|
'apiScenario.stepQuote': 'Step Quote',
|
||||||
'apiScenario.runRule': 'Parameter value rules',
|
'apiScenario.runRule': 'Parameter Value Rule',
|
||||||
'apiScenario.currentScenario': 'Prioritize current scene parameters',
|
'apiScenario.currentScenario': 'Priority: Current Scenario Parameters',
|
||||||
'apiScenario.sourceScenario': 'Priority source scene parameters',
|
'apiScenario.sourceScenario': 'Priority: Source Scenario Parameters',
|
||||||
'apiScenario.currentScenarioTip': 'When the current scene parameter does not exist, take',
|
'apiScenario.currentScenarioTip': 'When current scenario parameters do not exist, take',
|
||||||
'apiScenario.sourceScenarioTip': 'When the source scene parameter does not exist, take',
|
'apiScenario.sourceScenarioTip': 'When source scenario parameters do not exist, take',
|
||||||
'apiScenario.empty': 'Null value',
|
'apiScenario.empty': 'Empty Value',
|
||||||
'apiScenario.currentScenarioParams': 'Current scene parameters',
|
'apiScenario.currentScenarioParams': 'Current Scenario Parameters',
|
||||||
'apiScenario.sourceScenarioParams': 'Source scene parameters',
|
'apiScenario.sourceScenarioParams': 'Source Scenario Parameters',
|
||||||
'apiScenario.sourceScenarioEnv': 'Source scene environment',
|
'apiScenario.sourceScenarioEnv': 'Source Scenario Environment',
|
||||||
'apiScenario.valuePriority': 'Value priority:',
|
'apiScenario.valuePriority': 'Value Priority:',
|
||||||
'apiScenario.currentScenarioAndNull':
|
'apiScenario.currentScenarioAndNull':
|
||||||
'Current step parameters > Current scene parameters > Current environment parameters > Null value',
|
'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Empty Value',
|
||||||
'apiScenario.currentScenarioAndNullAndSourceEnv':
|
'apiScenario.currentScenarioAndNullAndSourceEnv':
|
||||||
'Current step parameters > Current scene parameters > Source environment parameters > Null value',
|
'Current Step Parameters > Current Scenario Parameters > Source Environment Parameters > Empty Value',
|
||||||
'apiScenario.currentScenarioAndSourceScenario':
|
'apiScenario.currentScenarioAndSourceScenario':
|
||||||
'Current step parameters > Current scene parameters > Current environment parameters > Source scene parameters',
|
'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Source Scenario Parameters',
|
||||||
'apiScenario.currentScenarioAndSourceScenarioAndSourceEnv':
|
'apiScenario.currentScenarioAndSourceScenarioAndSourceEnv':
|
||||||
'Current step parameters > Current scene parameters > Source scene parameters > Source environment parameters',
|
'Current Step Parameters > Current Scenario Parameters > Source Scenario Parameters > Source Environment Parameters',
|
||||||
'apiScenario.sourceScenarioAndNull': 'Source Scene Parameters > Null',
|
'apiScenario.sourceScenarioAndNull': 'Source Scenario Parameters > Empty Value',
|
||||||
'apiScenario.sourceScenarioAndNullAndSourceEnv': 'Source Scene Parameters > Source Environment Parameters > Null',
|
'apiScenario.sourceScenarioAndNullAndSourceEnv':
|
||||||
|
'Source Scenario Parameters > Source Environment Parameters > Empty Value',
|
||||||
'apiScenario.sourceScenarioAndCurrentScenario':
|
'apiScenario.sourceScenarioAndCurrentScenario':
|
||||||
'Source scene parameters > Current step parameters > Current scene parameters > Current environment parameters',
|
'Source Scenario Parameters > Current Step Parameters > Current Scenario Parameters > Current Environment Parameters',
|
||||||
'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv':
|
'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv':
|
||||||
'Source scene parameters > Source environment parameters > Current step parameters > Current scene parameters',
|
'Source Scenario Parameters > Source Environment Parameters > Current Step Parameters > Current Scenario Parameters',
|
||||||
'apiScenario.fullQuoteTip':
|
'apiScenario.fullQuoteTip':
|
||||||
'Full quotation: Follow the source step content and step status changes, the step status cannot be adjusted',
|
'Full Quote: Follows the original step content and step status changes. Step status cannot be adjusted.',
|
||||||
'apiScenario.stepQuoteTip':
|
'apiScenario.stepQuoteTip':
|
||||||
'Step reference: only follow the content changes of the source step, and the step status can be adjusted',
|
'Step Quote: Only follows the original step content changes. Step status can be adjusted.',
|
||||||
'apiScenario.sourceScenarioEnvTip': 'Operating environment, including environmental parameters',
|
'apiScenario.sourceScenarioEnvTip': 'Runtime environment, including environment parameters',
|
||||||
'apiScenario.setSuccess': 'Setup successful',
|
'apiScenario.setSuccess': 'Set Successful',
|
||||||
'apiScenario.pleaseInputUrl': 'Please enter url',
|
'apiScenario.pleaseInputUrl': 'Please enter URL',
|
||||||
// 执行历史
|
// Execution History
|
||||||
'apiScenario.executeHistory.searchPlaceholder': 'Search by ID or name',
|
'apiScenario.executeHistory.searchPlaceholder': 'Search by ID or name',
|
||||||
'apiScenario.executeHistory.num': 'Number',
|
'apiScenario.executeHistory.num': 'No.',
|
||||||
'apiScenario.executeHistory.execution.triggerMode': 'Trigger mode',
|
'apiScenario.executeHistory.execution.triggerMode': 'Execution Mode',
|
||||||
'apiScenario.executeHistory.execution.status': 'Execution result',
|
'apiScenario.executeHistory.execution.status': 'Execution Result',
|
||||||
'apiScenario.executeHistory.execution.operator': 'Operator',
|
'apiScenario.executeHistory.execution.operator': 'Operator',
|
||||||
'apiScenario.executeHistory.execution.operatorTime': 'Operation time',
|
'apiScenario.executeHistory.execution.operatorTime': 'Operation Time',
|
||||||
'apiScenario.executeHistory.execution.operation': 'Execution result',
|
'apiScenario.executeHistory.execution.operation': 'Execution Result',
|
||||||
'apiScenario.executeHistory.status.pending': 'Pending',
|
'apiScenario.executeHistory.status.pending': 'Queued',
|
||||||
'apiScenario.executeHistory.status.running': 'Running',
|
'apiScenario.executeHistory.status.running': 'Running',
|
||||||
'apiScenario.executeHistory.status.rerunning': 'Rerunning',
|
'apiScenario.executeHistory.status.rerunning': 'Rerunning',
|
||||||
'apiScenario.executeHistory.status.error': 'Error',
|
'apiScenario.executeHistory.status.error': 'Failed',
|
||||||
'apiScenario.executeHistory.status.success': 'Success',
|
'apiScenario.executeHistory.status.success': 'Successful',
|
||||||
'apiScenario.executeHistory.status.fake.error': 'Fake error',
|
'apiScenario.executeHistory.status.fake.error': 'False Positive',
|
||||||
'apiScenario.executeHistory.status.fake.stopped': 'Stopped',
|
'apiScenario.executeHistory.status.fake.stopped': 'Stopped',
|
||||||
// 操作历史
|
// Operation History
|
||||||
'apiScenario.historyListTip':
|
'apiScenario.historyListTip':
|
||||||
'View and compare historical changes. According to the rules set by the administrator, the change history data will be automatically deleted.',
|
'View and compare historical modifications. According to administrator settings, change history data will be automatically deleted.',
|
||||||
'apiScenario.changeOrder': 'Change serial number',
|
'apiScenario.changeOrder': 'Change Order',
|
||||||
'apiScenario.type': 'Type',
|
'apiScenario.type': 'Type',
|
||||||
'apiScenario.operationUser': 'Operator',
|
'apiScenario.operationUser': 'Operator',
|
||||||
'apiScenario.updateTime': 'Update time',
|
'apiScenario.updateTime': 'Update Time',
|
||||||
|
|
||||||
// 回收站
|
// Recycle Bin
|
||||||
'api_scenario.recycle.recover': 'Recover',
|
'api_scenario.recycle.recover': 'Recover',
|
||||||
'api_scenario.recycle.recoveredSuccessfully': 'Recover success',
|
'api_scenario.recycle.recoveredSuccessfully': 'Recover Successful',
|
||||||
'api_scenario.recycle.list': 'Recycle list',
|
'api_scenario.recycle.list': 'Recycle Bin List',
|
||||||
'api_scenario.recycle.batchCleanOut': 'Delete',
|
'api_scenario.recycle.batchCleanOut': 'Permanently Delete',
|
||||||
'api_scenario.recycle.completedDeleteCaseTitle': 'Are you sure you want to completely delete {name}?',
|
'api_scenario.recycle.completedDeleteCaseTitle': 'Confirm Permanently Delete {name}?',
|
||||||
'api_scenario.recycle.cleanOutDeleteOnRecycleTip':
|
'api_scenario.recycle.cleanOutDeleteOnRecycleTip':
|
||||||
'After deletion, the scene cannot be restored, please operate with caution!',
|
'Deleting will permanently remove the scenario. Proceed with caution!',
|
||||||
'api_scenario.table.searchPlaceholder': 'Search by ID/Name/Tag',
|
'apiScenario.quoteTreeNoData': 'No quotable data available, switch projects to retrieve data',
|
||||||
'apiScenario.quoteTreeNoData': 'There is currently no reference data. You can switch projects to obtain data.',
|
|
||||||
'apiScenario.quoteTreeSearchTip': 'Enter module name to search',
|
'apiScenario.quoteTreeSearchTip': 'Enter module name to search',
|
||||||
'apiScenario.quoteTableSearchTip': 'Search by path or name',
|
'apiScenario.quoteTableSearchTip': 'Search by path or name',
|
||||||
'apiScenario.collapseAll': 'Collapse all submodules',
|
'apiScenario.collapseAll': 'Collapse All Submodules',
|
||||||
'apiScenario.expandAll': 'Expand all submodules',
|
'apiScenario.expandAll': 'Expand All Submodules',
|
||||||
|
|
||||||
'apiScenario.scriptOperationName': 'Script operation name',
|
'apiScenario.scriptOperationName': 'Script Operation Name',
|
||||||
'apiScenario.scriptOperationNamePlaceholder': 'Please enter the script operation name',
|
'apiScenario.scriptOperationNamePlaceholder': 'Please enter script operation name',
|
||||||
|
|
||||||
'apiScenario.setting.cookie.config': 'Cookie configuration',
|
'apiScenario.setting.cookie.config': 'Cookie Configuration',
|
||||||
'apiScenario.setting.environment.cookie': 'Environment Cookie',
|
'apiScenario.setting.environment.cookie': 'Environment Cookie',
|
||||||
'apiScenario.setting.share.cookie': 'Shared Cookie',
|
'apiScenario.setting.share.cookie': 'Shared Cookie',
|
||||||
'apiScenario.setting.run.config': 'Run configuration',
|
'apiScenario.setting.run.config': 'Run Configuration',
|
||||||
'apiScenario.setting.step.waitTime': 'Step wait time',
|
'apiScenario.setting.step.waitTime': 'Step Wait Time',
|
||||||
'apiScenario.setting.waitTime': 'Wait time',
|
'apiScenario.setting.waitTime': 'Wait Time',
|
||||||
'apiScenario.setting.step.rule': 'Step execution failure rule',
|
'apiScenario.setting.step.rule': 'Step Execution Failure Rule',
|
||||||
'apiScenario.setting.step.rule.ignore': 'Ignore error and continue execution',
|
'apiScenario.setting.step.rule.ignore': 'Ignore Error and Continue Execution',
|
||||||
'apiScenario.setting.step.rule.stop': 'Stop/end execution',
|
'apiScenario.setting.step.rule.stop': 'Stop/End Execution',
|
||||||
'apiScenario.setting.cookie.config.tip':
|
'apiScenario.setting.cookie.config.tip':
|
||||||
'When there are both global and scene variable cookies, shared cookies will overwrite both global and scene variable cookies',
|
'When both global variable cookie and scenario variable cookie exist, shared cookie will override global cookie and scenario variable cookie',
|
||||||
'apiScenario.setting.share.cookie.tip':
|
'apiScenario.setting.share.cookie.tip':
|
||||||
'As long as the system extracts the returned cookie information from the result of a certain step, the subsequent steps will use this cookie. If a cookie variable is added to the request, it will also be overwritten',
|
'If the system extracts cookie information from the result of a certain step, subsequent steps will use this cookie. If the request adds a Cookie variable, it will be overridden',
|
||||||
'apiScenario.setting.waitTime.tip':
|
'apiScenario.setting.waitTime.tip':
|
||||||
'When running a scenario, each step of the scenario will wait for a certain time after execution before triggering the next step to start execution',
|
'When running a scenario, each step of the scenario will wait for the specified time before triggering the next step',
|
||||||
// 定时任务
|
// Scheduled Task
|
||||||
'apiScenario.schedule.create': 'Create schedule',
|
'apiScenario.schedule.create': 'Create Scheduled Task',
|
||||||
'apiScenario.schedule.update': 'Update schedule',
|
'apiScenario.schedule.update': 'Update Scheduled Task',
|
||||||
'apiScenario.schedule.delete': 'Delete schedule',
|
'apiScenario.schedule.delete': 'Delete Scheduled Task',
|
||||||
'apiScenario.schedule.config.resource_pool': 'Resource pool',
|
'apiScenario.schedule.config.resource_pool': 'Run Resource Pool',
|
||||||
'apiScenario.schedule.task.status': 'Task status',
|
'apiScenario.schedule.task.status': 'Task Status',
|
||||||
'apiScenario.schedule.task.schedule': 'Trigger time',
|
'apiScenario.schedule.task.schedule': 'Task Trigger Time',
|
||||||
'apiScenario.schedule.abbreviation': 'SCHEDULE',
|
'apiScenario.schedule.abbreviation': 'Scheduled',
|
||||||
'apiScenario.schedule.task.status.tooltip.one': 'Open: execute schedule task',
|
'apiScenario.schedule.task.status.tooltip.one': 'Enabled: Execute the scheduled task',
|
||||||
'apiScenario.schedule.task.status.tooltip.two': 'Close: stop executing schedule task',
|
'apiScenario.schedule.task.status.tooltip.two': 'Disabled: Stop the scheduled task',
|
||||||
'apiScenario.schedule.table.tooltip.enable.one': 'Schedule is open',
|
'apiScenario.schedule.table.tooltip.enable.one': 'Scheduled task is enabled',
|
||||||
'apiScenario.schedule.table.tooltip.enable.two': 'Next trigger time:{time}',
|
'apiScenario.schedule.table.tooltip.enable.two': 'Next run time: {time}',
|
||||||
'apiScenario.schedule.table.tooltip.disable': 'Schedule is close',
|
'apiScenario.schedule.table.tooltip.disable': 'Scheduled task is disabled',
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,7 @@ export default {
|
||||||
'apiScenario.params.paramValue': '参数值',
|
'apiScenario.params.paramValue': '参数值',
|
||||||
'apiScenario.params.tag': '标签',
|
'apiScenario.params.tag': '标签',
|
||||||
'apiScenario.params.desc': '描述',
|
'apiScenario.params.desc': '描述',
|
||||||
|
'apiScenario.params.typeTooltip': 'Json:仅支持 UI 测试',
|
||||||
'apiScenario.table.columns.name': '场景名称',
|
'apiScenario.table.columns.name': '场景名称',
|
||||||
'apiScenario.table.columns.level': '场景等级',
|
'apiScenario.table.columns.level': '场景等级',
|
||||||
'apiScenario.table.columns.status': '状态',
|
'apiScenario.table.columns.status': '状态',
|
||||||
|
|
|
@ -72,7 +72,7 @@ export default {
|
||||||
'caseManagement.featureCase.fileName': '文件名',
|
'caseManagement.featureCase.fileName': '文件名',
|
||||||
'caseManagement.featureCase.description': '描述',
|
'caseManagement.featureCase.description': '描述',
|
||||||
'caseManagement.featureCase.tags': '标签',
|
'caseManagement.featureCase.tags': '标签',
|
||||||
'caseManagement.featureCase.enableTags': `开启:新增标签,关闭:覆盖原有标签`,
|
'caseManagement.featureCase.enableTags': '开启:新增标签',
|
||||||
'caseManagement.featureCase.closeTags': '关闭:覆盖原有标签',
|
'caseManagement.featureCase.closeTags': '关闭:覆盖原有标签',
|
||||||
'caseManagement.featureCase.appendTag': '追加标签',
|
'caseManagement.featureCase.appendTag': '追加标签',
|
||||||
'caseManagement.featureCase.batchEdit': '批量编辑 (已选 { number } 条用例)',
|
'caseManagement.featureCase.batchEdit': '批量编辑 (已选 { number } 条用例)',
|
||||||
|
|
Loading…
Reference in New Issue