feat(接口场景): 场景步骤 15%
This commit is contained in:
parent
393bcabc3e
commit
488a93ea2c
|
@ -167,11 +167,11 @@
|
||||||
</a-form>
|
</a-form>
|
||||||
<div
|
<div
|
||||||
v-if="paramSettingType === 'mock' && paramForm.type !== ''"
|
v-if="paramSettingType === 'mock' && paramForm.type !== ''"
|
||||||
class="mb-[16px] flex items-center gap-[16px] 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="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 gap-[8px]">
|
<a-spin :loading="previewLoading" class="flex flex-1 flex-wrap gap-[8px]">
|
||||||
<div class="text-[var(--color-text-1)]">{{ 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>
|
||||||
</div>
|
</div>
|
||||||
|
@ -625,6 +625,13 @@
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin-right: -6px;
|
margin-right: -6px;
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
|
.ms-params-input-setting-trigger-content-scroll-preview {
|
||||||
|
@apply w-full overflow-y-auto overflow-x-hidden break-all;
|
||||||
|
.ms-scroll-bar();
|
||||||
|
|
||||||
|
max-height: 100px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,15 @@
|
||||||
:position="props.titleTooltipPosition"
|
:position="props.titleTooltipPosition"
|
||||||
:disabled="props.disabledTitleTooltip"
|
:disabled="props.disabledTitleTooltip"
|
||||||
>
|
>
|
||||||
<slot name="title" v-bind="_props"></slot>
|
<span :class="props.titleClass || 'ms-tree-node-title'">
|
||||||
|
<slot name="title" v-bind="_props"></slot>
|
||||||
|
</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$slots['drag-icon']" #drag-icon="_props">
|
<template v-if="$slots['drag-icon']" #drag-icon="_props">
|
||||||
<slot name="title" v-bind="_props"></slot>
|
<slot name="title" v-bind="_props"></slot>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$slots['extra']" #extra="_props">
|
<template v-if="$slots['extra'] || props.nodeMoreActions" #extra="_props">
|
||||||
<div
|
<div
|
||||||
v-if="_props.hideMoreAction !== true"
|
v-if="_props.hideMoreAction !== true"
|
||||||
:class="[
|
:class="[
|
||||||
|
@ -91,6 +93,7 @@
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
keyword?: string; // 搜索关键字
|
keyword?: string; // 搜索关键字
|
||||||
|
titleClass?: string; // 标题样式类
|
||||||
searchDebounce?: number; // 搜索防抖 ms 数
|
searchDebounce?: number; // 搜索防抖 ms 数
|
||||||
draggable?: boolean; // 是否可拖拽
|
draggable?: boolean; // 是否可拖拽
|
||||||
blockNode?: boolean; // 是否块级节点
|
blockNode?: boolean; // 是否块级节点
|
||||||
|
@ -509,8 +512,11 @@
|
||||||
.arco-tree-node-title {
|
.arco-tree-node-title {
|
||||||
font-weight: 500 !important;
|
font-weight: 500 !important;
|
||||||
color: rgb(var(--primary-5));
|
color: rgb(var(--primary-5));
|
||||||
* {
|
.ms-tree-node-title {
|
||||||
color: rgb(var(--primary-5));
|
color: rgb(var(--primary-5));
|
||||||
|
* {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
type?: TagType; // tag类型
|
type?: TagType; // tag类型
|
||||||
size?: Size; // tag尺寸
|
size?: Size; // tag尺寸
|
||||||
theme?: Theme; // tag主题
|
theme?: Theme; // tag主题
|
||||||
selfStyle?: any; // 自定义样式
|
selfStyle?: Record<string, any>; // 自定义样式
|
||||||
width?: number; // tag宽度,不传入时绑定max-width
|
width?: number; // tag宽度,不传入时绑定max-width
|
||||||
maxWidth?: string;
|
maxWidth?: string;
|
||||||
noMargin?: boolean; // tag之间是否有间距
|
noMargin?: boolean; // tag之间是否有间距
|
||||||
|
|
|
@ -345,6 +345,68 @@ export function findNodePathByKey<T>(
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 在某个节点前插入新节点
|
||||||
|
* @param treeArr 目标树
|
||||||
|
* @param targetKey 目标节点唯一值
|
||||||
|
* @param newNode 新节点
|
||||||
|
* @param position 插入位置
|
||||||
|
* @param customKey 默认为 key,可自定义需要匹配的属性名
|
||||||
|
*/
|
||||||
|
export function insertNode<T>(
|
||||||
|
treeArr: TreeNode<T>[],
|
||||||
|
targetKey: string,
|
||||||
|
newNode: TreeNode<T>,
|
||||||
|
position: 'before' | 'after',
|
||||||
|
customKey = 'key'
|
||||||
|
): void {
|
||||||
|
function insertNodeInTree(tree: TreeNode<T>[], parent?: TreeNode<T>): boolean {
|
||||||
|
for (let i = 0; i < tree.length; i++) {
|
||||||
|
const node = tree[i];
|
||||||
|
if (node[customKey] === targetKey) {
|
||||||
|
// 如果当前节点的 customKey 与目标 customKey 匹配,则在当前节点前/后插入新节点
|
||||||
|
const childrenArray = parent ? parent.children || [] : treeArr; // 父节点没有 children 属性,说明是树的第一层,使用 treeArr
|
||||||
|
const index = childrenArray.findIndex((item) => item[customKey] === node[customKey]);
|
||||||
|
if (position === 'before') {
|
||||||
|
childrenArray.splice(index, 0, newNode);
|
||||||
|
} else if (position === 'after') {
|
||||||
|
childrenArray.splice(index + 1, 0, newNode);
|
||||||
|
}
|
||||||
|
// 插入后返回 true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Array.isArray(node.children) && insertNodeInTree(node.children, node)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertNodeInTree(treeArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除树形数组中的某个节点
|
||||||
|
* @param treeArr 目标树
|
||||||
|
* @param targetKey 目标节点唯一值
|
||||||
|
*/
|
||||||
|
export function deleteNode<T>(treeArr: TreeNode<T>[], targetKey: string, customKey = 'key'): void {
|
||||||
|
function deleteNodeInTree(tree: TreeNode<T>[]): void {
|
||||||
|
for (let i = 0; i < tree.length; i++) {
|
||||||
|
const node = tree[i];
|
||||||
|
if (node[customKey] === targetKey) {
|
||||||
|
tree.splice(i, 1); // 直接删除当前节点
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(node.children)) {
|
||||||
|
deleteNodeInTree(node.children); // 递归删除子节点
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteNodeInTree(treeArr);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 找出俩数组之间的差异项并返回
|
* 找出俩数组之间的差异项并返回
|
||||||
* @param targetMap 目标项
|
* @param targetMap 目标项
|
||||||
|
|
|
@ -1245,6 +1245,8 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存请求
|
* 保存请求
|
||||||
|
* @param fullParams 保存时传入的参数
|
||||||
|
* @param silence 是否静默保存(接口定义另存为用例时要先静默保存接口)
|
||||||
*/
|
*/
|
||||||
async function realSave(fullParams?: Record<string, any>, silence?: boolean) {
|
async function realSave(fullParams?: Record<string, any>, silence?: boolean) {
|
||||||
try {
|
try {
|
||||||
|
@ -1278,6 +1280,7 @@
|
||||||
requestVModel.value.label = res.name;
|
requestVModel.value.label = res.name;
|
||||||
requestVModel.value.url = res.path;
|
requestVModel.value.url = res.path;
|
||||||
requestVModel.value.path = res.path;
|
requestVModel.value.path = res.path;
|
||||||
|
requestVModel.value.moduleId = res.moduleId;
|
||||||
if (!props.isDefinition) {
|
if (!props.isDefinition) {
|
||||||
saveModalVisible.value = false;
|
saveModalVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-divider class="my-[8px]" />
|
<a-divider class="my-[8px]" />
|
||||||
<a-spin class="h-[calc(100%-98px)] w-full" :loading="loading">
|
<a-spin class="max-h-[calc(100%-98px)] w-full" :loading="loading">
|
||||||
<MsTree
|
<MsTree
|
||||||
v-model:selected-keys="selectedKeys"
|
v-model:selected-keys="selectedKeys"
|
||||||
v-model:focus-node-key="focusNodeKey"
|
v-model:focus-node-key="focusNodeKey"
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<a-dropdown class="scenario-action-dropdown" @select="(val) => emit('select', val as ScenarioAddStepActionType)">
|
<a-dropdown
|
||||||
|
v-model:popup-visible="visible"
|
||||||
|
:position="props.position || 'bottom'"
|
||||||
|
:popup-translate="props.popupTranslate"
|
||||||
|
class="scenario-action-dropdown"
|
||||||
|
@select="(val) => emit('select', val as ScenarioAddStepActionType)"
|
||||||
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-dgroup :title="t('apiScenario.requestScenario')">
|
<a-dgroup :title="t('apiScenario.requestScenario')">
|
||||||
|
@ -35,18 +41,29 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { TriggerPopupTranslate } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import { ScenarioAddStepActionType } from '@/enums/apiEnum';
|
import { ScenarioAddStepActionType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
import { DropdownPosition } from '@arco-design/web-vue/es/dropdown/interface';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
position?: DropdownPosition;
|
||||||
|
popupTranslate?: TriggerPopupTranslate;
|
||||||
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'select', val: ScenarioAddStepActionType): void;
|
(e: 'select', val: ScenarioAddStepActionType): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
function openTutorial() {
|
function openTutorial() {
|
||||||
window.open('https://zhuanlan.zhihu.com/p/597905464?utm_id=0', '_blank');
|
window.open('https://zhuanlan.zhihu.com/p/597905464?utm_id=0', '_blank');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-[16px]">
|
<div class="flex h-full flex-col gap-[16px]">
|
||||||
<div class="action-line">
|
<div class="action-line">
|
||||||
<div class="action-group">
|
<div class="action-group">
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="h-[calc(100%-48px)]">
|
||||||
<stepTree
|
<stepTree
|
||||||
ref="stepTreeRef"
|
ref="stepTreeRef"
|
||||||
v-model:steps="stepInfo.steps"
|
v-model:steps="stepInfo.steps"
|
||||||
|
@ -99,6 +99,7 @@
|
||||||
import stepTree, { ScenarioStepItem } from './stepTree.vue';
|
import stepTree, { ScenarioStepItem } from './stepTree.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { RequestMethods, ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
import { RequestMethods, ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
@ -114,6 +115,7 @@
|
||||||
isNew?: boolean; // 是否新建
|
isNew?: boolean; // 是否新建
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const checkedAll = ref(false); // 是否全选
|
const checkedAll = ref(false); // 是否全选
|
||||||
|
@ -127,6 +129,7 @@
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
num: 10086,
|
||||||
order: 1,
|
order: 1,
|
||||||
checked: false,
|
checked: false,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
|
@ -135,6 +138,9 @@
|
||||||
name: 'API1',
|
name: 'API1',
|
||||||
description: 'API1描述',
|
description: 'API1描述',
|
||||||
method: RequestMethods.GET,
|
method: RequestMethods.GET,
|
||||||
|
belongProjectId: appStore.currentProjectId,
|
||||||
|
belongProjectName: '项目名称',
|
||||||
|
actionDropdownVisible: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 11,
|
id: 11,
|
||||||
|
@ -146,6 +152,10 @@
|
||||||
name: 'API11',
|
name: 'API11',
|
||||||
description: 'API11描述',
|
description: 'API11描述',
|
||||||
status: ScenarioExecuteStatus.SUCCESS,
|
status: ScenarioExecuteStatus.SUCCESS,
|
||||||
|
num: 100861,
|
||||||
|
belongProjectId: '989d23d23d',
|
||||||
|
belongProjectName: '项目名称1',
|
||||||
|
actionDropdownVisible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12,
|
id: 12,
|
||||||
|
@ -157,6 +167,10 @@
|
||||||
name: 'API12',
|
name: 'API12',
|
||||||
description: 'API12描述',
|
description: 'API12描述',
|
||||||
status: ScenarioExecuteStatus.SUCCESS,
|
status: ScenarioExecuteStatus.SUCCESS,
|
||||||
|
num: 100862,
|
||||||
|
belongProjectId: '989d23d23d',
|
||||||
|
belongProjectName: '项目名称2',
|
||||||
|
actionDropdownVisible: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -170,6 +184,7 @@
|
||||||
name: 'API1',
|
name: 'API1',
|
||||||
description: 'API1描述',
|
description: 'API1描述',
|
||||||
status: ScenarioExecuteStatus.SUCCESS,
|
status: ScenarioExecuteStatus.SUCCESS,
|
||||||
|
actionDropdownVisible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
|
@ -181,6 +196,7 @@
|
||||||
name: 'API1',
|
name: 'API1',
|
||||||
description: 'API1描述',
|
description: 'API1描述',
|
||||||
status: ScenarioExecuteStatus.SUCCESS,
|
status: ScenarioExecuteStatus.SUCCESS,
|
||||||
|
actionDropdownVisible: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
executeTime: '',
|
executeTime: '',
|
||||||
|
|
|
@ -1,7 +1,86 @@
|
||||||
<template>
|
<template>
|
||||||
<div>quote </div>
|
<div class="flex items-center gap-[4px]">
|
||||||
|
<a-popover position="bl" content-class="detail-popover" arrow-class="hidden">
|
||||||
|
<MsIcon type="icon-icon-draft" class="text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]" />
|
||||||
|
<template #content>
|
||||||
|
<div class="flex flex-col gap-[16px]">
|
||||||
|
<div>
|
||||||
|
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.belongProject') }}</div>
|
||||||
|
<div class="text-[14px] text-[var(--color-text-1)]">
|
||||||
|
{{ props.data.belongProjectName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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">
|
||||||
|
{{ `【${props.data.num}】${props.data.name}` }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
<MsTag
|
||||||
|
v-if="props.data.belongProjectId !== props.data.currentProjectId"
|
||||||
|
theme="outline"
|
||||||
|
size="small"
|
||||||
|
:self-style="{
|
||||||
|
color: 'var(--color-text-4)',
|
||||||
|
border: '1px solid var(--color-text-input-border)',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ t('apiScenario.crossProject') }}
|
||||||
|
</MsTag>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
||||||
|
|
||||||
|
import { ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: {
|
||||||
|
id: string | number;
|
||||||
|
belongProjectId: string;
|
||||||
|
belongProjectName: string;
|
||||||
|
num: number;
|
||||||
|
name: string;
|
||||||
|
type: ScenarioStepType;
|
||||||
|
currentProjectId: string;
|
||||||
|
};
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openNewPage } = useOpenNewPage();
|
||||||
|
|
||||||
|
function goDetail() {
|
||||||
|
switch (props.data.type) {
|
||||||
|
case ScenarioStepType.COPY_API:
|
||||||
|
case ScenarioStepType.QUOTE_API:
|
||||||
|
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, { dId: props.data.id });
|
||||||
|
break;
|
||||||
|
case ScenarioStepType.QUOTE_SCENARIO:
|
||||||
|
case ScenarioStepType.COPY_SCENARIO:
|
||||||
|
openNewPage(ApiTestRouteEnum.API_TEST_SCENARIO, { sId: props.data.id });
|
||||||
|
break;
|
||||||
|
case ScenarioStepType.COPY_CASE:
|
||||||
|
case ScenarioStepType.QUOTE_CASE:
|
||||||
|
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, { cId: props.data.id });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.detail-popover {
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-[16px]">
|
<div class="flex h-full flex-col gap-[16px]">
|
||||||
<div class="max-h-[calc(100vh-305px)]">
|
<a-spin class="max-h-[calc(100%-46px)] w-full" :loading="loading">
|
||||||
<MsTree
|
<MsTree
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
|
v-model:selected-keys="selectedKeys"
|
||||||
v-model:checked-keys="checkedKeys"
|
v-model:checked-keys="checkedKeys"
|
||||||
v-model:focus-node-key="focusStepKey"
|
v-model:focus-node-key="focusStepKey"
|
||||||
v-model:data="steps"
|
v-model:data="steps"
|
||||||
|
@ -10,20 +11,23 @@
|
||||||
:expand-all="props.expandAll"
|
:expand-all="props.expandAll"
|
||||||
:node-more-actions="stepMoreActions"
|
:node-more-actions="stepMoreActions"
|
||||||
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||||
:selectable="false"
|
|
||||||
:virtual-list-props="{
|
:virtual-list-props="{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
threshold: 200,
|
threshold: 20,
|
||||||
fixedSize: true,
|
fixedSize: true,
|
||||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
}"
|
}"
|
||||||
|
title-class="step-tree-node-title"
|
||||||
node-highlight-background-color="var(--color-text-n9)"
|
node-highlight-background-color="var(--color-text-n9)"
|
||||||
action-on-node-click="expand"
|
action-on-node-click="expand"
|
||||||
disabled-title-tooltip
|
disabled-title-tooltip
|
||||||
checkable
|
checkable
|
||||||
block-node
|
block-node
|
||||||
draggable
|
draggable
|
||||||
|
@select="handleStepSelect"
|
||||||
@expand="handleStepExpand"
|
@expand="handleStepExpand"
|
||||||
|
@more-actions-close="() => setFocusNodeKey('')"
|
||||||
|
@more-action-select="handleStepMoreActionSelect"
|
||||||
>
|
>
|
||||||
<template #title="step">
|
<template #title="step">
|
||||||
<div class="flex w-full items-center gap-[8px]">
|
<div class="flex w-full items-center gap-[8px]">
|
||||||
|
@ -35,16 +39,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="step-node-content">
|
<div class="step-node-content">
|
||||||
<!-- 步骤展开折叠按钮 -->
|
<!-- 步骤展开折叠按钮 -->
|
||||||
<div
|
<a-tooltip
|
||||||
v-if="step.children?.length > 0"
|
v-if="step.children?.length > 0"
|
||||||
class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-1)]"
|
:content="t('apiScenario.expandStepTip', { count: step.children.length })"
|
||||||
>
|
>
|
||||||
<MsIcon
|
<div class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-1)]">
|
||||||
:type="step.expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'"
|
<MsIcon
|
||||||
:size="14"
|
:type="step.expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'"
|
||||||
/>
|
:size="14"
|
||||||
{{ step.children?.length || 0 }}
|
/>
|
||||||
</div>
|
{{ step.children?.length || 0 }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
<div class="mr-[8px] flex items-center gap-[8px]">
|
<div class="mr-[8px] flex items-center gap-[8px]">
|
||||||
<!-- 步骤启用/禁用 -->
|
<!-- 步骤启用/禁用 -->
|
||||||
<a-switch
|
<a-switch
|
||||||
|
@ -62,46 +68,105 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- 步骤类型 -->
|
<!-- 步骤类型 -->
|
||||||
<stepType :type="step.type" />
|
<stepType :type="step.type" />
|
||||||
<apiMethodName v-if="checkStepIsApi(step)" :method="step.method" />
|
<!-- 步骤整体内容 -->
|
||||||
<!-- 步骤名称 -->
|
<div class="relative flex flex-1 items-center gap-[4px]">
|
||||||
<div v-if="checkStepIsApi(step)" class="relative flex flex-1 items-center">
|
<!-- 步骤差异内容,按步骤类型展示不同组件 -->
|
||||||
<div
|
<component :is="getStepContent(step)" :data="getStepContentData(step)" />
|
||||||
v-if="step.id === showStepNameEditInputStepId"
|
<!-- API、CASE、场景步骤名称 -->
|
||||||
class="absolute left-0 top-[-2px] z-10 w-[calc(100%-24px)]"
|
<template v-if="checkStepIsApi(step)">
|
||||||
@click.stop
|
<apiMethodName v-if="checkStepShowMethod(step)" :method="step.method" />
|
||||||
>
|
<div
|
||||||
<a-input
|
v-if="step.id === showStepNameEditInputStepId"
|
||||||
:id="step.id"
|
class="absolute left-0 top-[-2px] z-10 w-[calc(100%-24px)]"
|
||||||
v-model:model-value="tempStepName"
|
@click.stop
|
||||||
:placeholder="t('apiScenario.pleaseInputStepName')"
|
>
|
||||||
:max-length="255"
|
<a-input
|
||||||
size="small"
|
:id="step.id"
|
||||||
@press-enter="applyStepChange(step)"
|
v-model:model-value="tempStepName"
|
||||||
@blur="applyStepChange(step)"
|
:placeholder="t('apiScenario.pleaseInputStepName')"
|
||||||
/>
|
:max-length="255"
|
||||||
</div>
|
size="small"
|
||||||
<a-tooltip :content="step.name">
|
@press-enter="applyStepChange(step)"
|
||||||
<div class="step-name-container">
|
@blur="applyStepChange(step)"
|
||||||
<div class="one-line-text mr-[4px] max-w-[250px] font-medium text-[var(--color-text-1)]">
|
|
||||||
{{ step.name }}
|
|
||||||
</div>
|
|
||||||
<MsIcon
|
|
||||||
type="icon-icon_edit_outlined"
|
|
||||||
class="edit-script-name-icon"
|
|
||||||
@click.stop="handleStepNameClick(step)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
<a-tooltip :content="step.name">
|
||||||
|
<div class="step-name-container">
|
||||||
|
<div class="one-line-text mr-[4px] max-w-[250px] font-medium text-[var(--color-text-1)]">
|
||||||
|
{{ step.name }}
|
||||||
|
</div>
|
||||||
|
<MsIcon
|
||||||
|
type="icon-icon_edit_outlined"
|
||||||
|
class="edit-script-name-icon"
|
||||||
|
@click.stop="handleStepNameClick(step)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<!-- 步骤内容,按步骤类型展示不同组件 -->
|
|
||||||
<component :is="getStepContent(step)" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #extra="step">
|
<template #extra="step">
|
||||||
<MsButton :id="step.id" type="icon" class="ms-tree-node-extra__btn !mr-[4px]" @click="setFocusNodeKey(step)">
|
<a-trigger
|
||||||
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
|
trigger="click"
|
||||||
</MsButton>
|
class="arco-trigger-menu absolute"
|
||||||
|
content-class="w-[160px]"
|
||||||
|
position="br"
|
||||||
|
@popup-visible-change="handleActionTriggerChange($event, step)"
|
||||||
|
>
|
||||||
|
<MsButton
|
||||||
|
:id="step.id"
|
||||||
|
type="icon"
|
||||||
|
class="ms-tree-node-extra__btn !mr-[4px]"
|
||||||
|
@click="setFocusNodeKey(step.id)"
|
||||||
|
>
|
||||||
|
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
|
||||||
|
</MsButton>
|
||||||
|
<template #content>
|
||||||
|
<actionDropdown
|
||||||
|
v-model:visible="step.actionDropdownVisible"
|
||||||
|
position="br"
|
||||||
|
class="scenario-action-dropdown"
|
||||||
|
:popup-translate="[-7, -10]"
|
||||||
|
@select="(val) => handleActionSelect(val as ScenarioAddStepActionType, step)"
|
||||||
|
>
|
||||||
|
<span></span>
|
||||||
|
</actionDropdown>
|
||||||
|
<div class="arco-trigger-menu-inner">
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'arco-trigger-menu-item !mx-0 !w-full',
|
||||||
|
activeAction === 'addChildStep' ? 'step-tree-active-action' : '',
|
||||||
|
]"
|
||||||
|
@click="handleTriggerActionClick(step, 'addChildStep')"
|
||||||
|
>
|
||||||
|
<icon-plus size="12" />
|
||||||
|
{{ t('apiScenario.addChildStep') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'arco-trigger-menu-item !mx-0 !w-full',
|
||||||
|
activeAction === 'insertBefore' ? 'step-tree-active-action' : '',
|
||||||
|
]"
|
||||||
|
@click="handleTriggerActionClick(step, 'insertBefore')"
|
||||||
|
>
|
||||||
|
<icon-left size="12" />
|
||||||
|
{{ t('apiScenario.insertBefore') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'arco-trigger-menu-item !mx-0 !w-full',
|
||||||
|
activeAction === 'insertAfter' ? 'step-tree-active-action' : '',
|
||||||
|
]"
|
||||||
|
@click="handleTriggerActionClick(step, 'insertAfter')"
|
||||||
|
>
|
||||||
|
<icon-left size="12" />
|
||||||
|
{{ t('apiScenario.insertAfter') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-trigger>
|
||||||
</template>
|
</template>
|
||||||
<template #extraEnd="step">
|
<template #extraEnd="step">
|
||||||
<executeStatus v-if="step.status" :status="step.status" size="small" />
|
<executeStatus v-if="step.status" :status="step.status" size="small" />
|
||||||
|
@ -114,7 +179,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MsTree>
|
</MsTree>
|
||||||
</div>
|
</a-spin>
|
||||||
<actionDropdown
|
<actionDropdown
|
||||||
class="scenario-action-dropdown"
|
class="scenario-action-dropdown"
|
||||||
@select="(val) => handleActionSelect(val as ScenarioAddStepActionType)"
|
@select="(val) => handleActionSelect(val as ScenarioAddStepActionType)"
|
||||||
|
@ -133,6 +198,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
@ -153,7 +220,8 @@
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { findNodeByKey } from '@/utils';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
import { deleteNode, findNodeByKey, getGenerateId, insertNode, mapTree } from '@/utils';
|
||||||
|
|
||||||
import { RequestMethods, ScenarioAddStepActionType, ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
import { RequestMethods, ScenarioAddStepActionType, ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
@ -168,11 +236,16 @@
|
||||||
description: string;
|
description: string;
|
||||||
method?: RequestMethods;
|
method?: RequestMethods;
|
||||||
status?: ScenarioExecuteStatus;
|
status?: ScenarioExecuteStatus;
|
||||||
projectId?: string;
|
num?: number; // 详情或者引用的类型才有
|
||||||
|
// 引用类型专有字段
|
||||||
|
belongProjectId?: string;
|
||||||
|
belongProjectName?: string;
|
||||||
children?: ScenarioStepItem[];
|
children?: ScenarioStepItem[];
|
||||||
// 页面渲染以及交互需要字段
|
// 页面渲染以及交互需要字段
|
||||||
|
// renderId: string; // 渲染id
|
||||||
checked: boolean; // 是否选中
|
checked: boolean; // 是否选中
|
||||||
expanded: boolean; // 是否展开
|
expanded: boolean; // 是否展开
|
||||||
|
actionDropdownVisible?: boolean; // 是否展示操作下拉
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -180,6 +253,7 @@
|
||||||
expandAll?: boolean;
|
expandAll?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const steps = defineModel<ScenarioStepItem[]>('steps', {
|
const steps = defineModel<ScenarioStepItem[]>('steps', {
|
||||||
|
@ -189,27 +263,10 @@
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedKeys = ref<string[]>([]); // 没啥用,目前用来展示选中样式
|
||||||
|
const loading = ref(false);
|
||||||
const treeRef = ref<InstanceType<typeof MsTree>>();
|
const treeRef = ref<InstanceType<typeof MsTree>>();
|
||||||
const focusStepKey = ref<string>(''); // 聚焦的key
|
const focusStepKey = ref<string>(''); // 聚焦的key
|
||||||
const stepMoreActions: ActionsItem[] = [
|
|
||||||
{
|
|
||||||
label: 'common.execute',
|
|
||||||
eventTag: 'execute',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'common.copy',
|
|
||||||
eventTag: 'copy',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'apiScenario.scenarioConfig',
|
|
||||||
eventTag: 'config',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'common.delete',
|
|
||||||
eventTag: 'delete',
|
|
||||||
danger: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function getStepContent(step: ScenarioStepItem) {
|
function getStepContent(step: ScenarioStepItem) {
|
||||||
switch (step.type) {
|
switch (step.type) {
|
||||||
|
@ -232,12 +289,138 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFocusNodeKey(node: MsTreeNodeData) {
|
function getStepContentData(step: ScenarioStepItem) {
|
||||||
focusStepKey.value = node.id || '';
|
switch (step.type) {
|
||||||
|
case ScenarioStepType.QUOTE_API:
|
||||||
|
case ScenarioStepType.QUOTE_CASE:
|
||||||
|
case ScenarioStepType.QUOTE_SCENARIO:
|
||||||
|
return {
|
||||||
|
id: step.id,
|
||||||
|
belongProjectId: step.belongProjectId,
|
||||||
|
belongProjectName: step.belongProjectName,
|
||||||
|
num: step.num,
|
||||||
|
name: step.name,
|
||||||
|
type: step.type,
|
||||||
|
currentProjectId: appStore.currentProjectId,
|
||||||
|
};
|
||||||
|
case ScenarioStepType.CUSTOM_API:
|
||||||
|
return {};
|
||||||
|
case ScenarioStepType.LOOP_CONTROL:
|
||||||
|
return {};
|
||||||
|
case ScenarioStepType.CONDITION_CONTROL:
|
||||||
|
return {};
|
||||||
|
case ScenarioStepType.ONLY_ONCE_CONTROL:
|
||||||
|
return {};
|
||||||
|
case ScenarioStepType.WAIT_TIME:
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFocusNodeKey(id: string) {
|
||||||
|
focusStepKey.value = id || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkStepIsApi(step: ScenarioStepItem) {
|
function checkStepIsApi(step: ScenarioStepItem) {
|
||||||
return [ScenarioStepType.QUOTE_API, ScenarioStepType.COPY_API, ScenarioStepType.CUSTOM_API].includes(step.type);
|
return [
|
||||||
|
ScenarioStepType.QUOTE_API,
|
||||||
|
ScenarioStepType.COPY_API,
|
||||||
|
ScenarioStepType.QUOTE_CASE,
|
||||||
|
ScenarioStepType.COPY_CASE,
|
||||||
|
ScenarioStepType.CUSTOM_API,
|
||||||
|
].includes(step.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkStepShowMethod(step: ScenarioStepItem) {
|
||||||
|
return [
|
||||||
|
ScenarioStepType.QUOTE_API,
|
||||||
|
ScenarioStepType.COPY_API,
|
||||||
|
ScenarioStepType.QUOTE_CASE,
|
||||||
|
ScenarioStepType.COPY_CASE,
|
||||||
|
ScenarioStepType.CUSTOM_API,
|
||||||
|
ScenarioStepType.QUOTE_SCENARIO,
|
||||||
|
ScenarioStepType.COPY_SCENARIO,
|
||||||
|
].includes(step.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeAction = ref('');
|
||||||
|
function handleTriggerActionClick(step: ScenarioStepItem, action: string) {
|
||||||
|
step.actionDropdownVisible = true;
|
||||||
|
activeAction.value = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActionTriggerChange(val: boolean, step: ScenarioStepItem) {
|
||||||
|
if (!val) {
|
||||||
|
activeAction.value = '';
|
||||||
|
step.actionDropdownVisible = false;
|
||||||
|
setFocusNodeKey('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stepMoreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'common.execute',
|
||||||
|
eventTag: 'execute',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'common.copy',
|
||||||
|
eventTag: 'copy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'apiScenario.scenarioConfig',
|
||||||
|
eventTag: 'config',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'common.delete',
|
||||||
|
eventTag: 'delete',
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
function handleStepMoreActionSelect(item: ActionsItem, node: MsTreeNodeData) {
|
||||||
|
switch (item.eventTag) {
|
||||||
|
case 'execute':
|
||||||
|
console.log('执行步骤', node);
|
||||||
|
break;
|
||||||
|
case 'copy':
|
||||||
|
const id = getGenerateId();
|
||||||
|
insertNode<ScenarioStepItem>(
|
||||||
|
steps.value,
|
||||||
|
node.id,
|
||||||
|
{
|
||||||
|
...cloneDeep(
|
||||||
|
mapTree<ScenarioStepItem>(node, (childNode) => {
|
||||||
|
const childId = getGenerateId();
|
||||||
|
if (selectedKeys.value.includes(node.id)) {
|
||||||
|
// 复制一个已选中的节点,需要把新节点也需要选中(因为父级选中子级也会展示选中状态)
|
||||||
|
selectedKeys.value.push(childId);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...childNode,
|
||||||
|
id: childId,
|
||||||
|
};
|
||||||
|
})[0]
|
||||||
|
),
|
||||||
|
name: `copy-${node.name}`,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
'after',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
if (selectedKeys.value.includes(node.id)) {
|
||||||
|
// 复制一个已选中的节点,需要把新节点也需要选中(因为父级选中子级也会展示选中状态)
|
||||||
|
selectedKeys.value.push(id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'config':
|
||||||
|
console.log('config', node);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
deleteNode(steps.value, node.id, 'id');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAll(val: boolean) {
|
function checkAll(val: boolean) {
|
||||||
|
@ -276,6 +459,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleStepSelect(_selectedKeys: Array<string | number>, node: MsTreeNodeData) {
|
||||||
|
const offspringIds: string[] = [];
|
||||||
|
mapTree(node.children || [], (e) => {
|
||||||
|
offspringIds.push(e.id);
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
selectedKeys.value = [node.id, ...offspringIds];
|
||||||
|
}
|
||||||
|
|
||||||
function executeStep(node: MsTreeNodeData) {
|
function executeStep(node: MsTreeNodeData) {
|
||||||
console.log('执行步骤', node);
|
console.log('执行步骤', node);
|
||||||
}
|
}
|
||||||
|
@ -284,7 +476,7 @@
|
||||||
const customApiDrawerVisible = ref(false);
|
const customApiDrawerVisible = ref(false);
|
||||||
const scriptOperationDrawerVisible = ref(false);
|
const scriptOperationDrawerVisible = ref(false);
|
||||||
|
|
||||||
function handleActionSelect(val: ScenarioAddStepActionType) {
|
function handleActionSelect(val: ScenarioAddStepActionType, step?: ScenarioStepItem) {
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case ScenarioAddStepActionType.IMPORT_SYSTEM_API:
|
case ScenarioAddStepActionType.IMPORT_SYSTEM_API:
|
||||||
importApiDrawerVisible.value = true;
|
importApiDrawerVisible.value = true;
|
||||||
|
@ -338,6 +530,9 @@
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (step) {
|
||||||
|
document.getElementById(step.id.toString())?.click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -345,6 +540,13 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.step-tree-active-action {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
background-color: rgb(var(--primary-1));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.add-step-btn {
|
.add-step-btn {
|
||||||
@apply bg-white;
|
@apply bg-white;
|
||||||
|
|
|
@ -159,5 +159,13 @@
|
||||||
}
|
}
|
||||||
:deep(.arco-tabs-content) {
|
:deep(.arco-tabs-content) {
|
||||||
@apply pt-0;
|
@apply pt-0;
|
||||||
|
|
||||||
|
height: calc(100% - 49px);
|
||||||
|
.arco-tabs-content-list {
|
||||||
|
@apply h-full;
|
||||||
|
.arco-tabs-pane {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -101,6 +101,13 @@ export default {
|
||||||
'apiScenario.scenarioConfig': '场景配置',
|
'apiScenario.scenarioConfig': '场景配置',
|
||||||
'apiScenario.noMatchStep': '暂无匹配的步骤数据',
|
'apiScenario.noMatchStep': '暂无匹配的步骤数据',
|
||||||
'apiScenario.pleaseInputStepName': '请输入步骤名称',
|
'apiScenario.pleaseInputStepName': '请输入步骤名称',
|
||||||
|
'apiScenario.belongProject': '所属项目',
|
||||||
|
'apiScenario.detailName': '名称',
|
||||||
|
'apiScenario.crossProject': '跨项目',
|
||||||
|
'apiScenario.expandStepTip': '展开 {count} 个子步骤',
|
||||||
|
'apiScenario.addChildStep': '添加子步骤',
|
||||||
|
'apiScenario.insertBefore': '在之前插入步骤',
|
||||||
|
'apiScenario.insertAfter': '在之后插入步骤',
|
||||||
// 执行历史
|
// 执行历史
|
||||||
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
|
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
|
||||||
'apiScenario.executeHistory.num': '序号',
|
'apiScenario.executeHistory.num': '序号',
|
||||||
|
|
Loading…
Reference in New Issue