fix(场景步骤): 场景配置&样式优化
This commit is contained in:
parent
f69672938f
commit
f04bd1c0da
|
@ -14,6 +14,7 @@ import {
|
|||
DeleteScenarioUrl,
|
||||
ExecuteHistoryUrl,
|
||||
ExecuteScenarioUrl,
|
||||
FollowScenarioUrl,
|
||||
GetModuleCountUrl,
|
||||
GetModuleTreeUrl,
|
||||
GetScenarioStepUrl,
|
||||
|
@ -238,3 +239,8 @@ export function executeScenario(data: ApiScenarioDebugRequest) {
|
|||
export function getSystemRequest(data: GetSystemRequestParams) {
|
||||
return MSR.post<ApiScenarioTableItem[]>({ url: GetSystemRequestUrl, data });
|
||||
}
|
||||
|
||||
// 关注/取消关注接口场景
|
||||
export function followScenario(id: string | number) {
|
||||
return MSR.get({ url: FollowScenarioUrl, params: id });
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export const ScenarioTransferModuleOptionsUrl = '/api/scenario/transfer/options'
|
|||
export const DebugScenarioUrl = '/api/scenario/debug'; // 接口场景调试(不保存报告)
|
||||
export const ExecuteScenarioUrl = '/api/scenario/run'; // 接口场景执行(保存报告)
|
||||
export const GetSystemRequestUrl = '/api/scenario/get/system-request'; // 获取导入的系统请求数据
|
||||
export const FollowScenarioUrl = '/api/scenario/follow'; // 关注/取消关注接口场景
|
||||
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
|
||||
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
|
||||
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景
|
||||
|
|
|
@ -82,14 +82,18 @@
|
|||
<a-input
|
||||
v-model:model-value="requestVModel.url"
|
||||
:max-length="255"
|
||||
:placeholder="t('apiTestDebug.urlPlaceholder')"
|
||||
:placeholder="showEnvPrefix ? t('apiScenario.pleaseInputUrl') : t('apiTestDebug.urlPlaceholder')"
|
||||
allow-clear
|
||||
class="hover:z-10"
|
||||
:style="isUrlError ? 'border: 1px solid rgb(var(--danger-6);z-index: 10' : ''"
|
||||
:disabled="_stepType.isQuoteApi"
|
||||
@input="() => (isUrlError = false)"
|
||||
@change="handleUrlChange"
|
||||
/>
|
||||
>
|
||||
<template v-if="showEnvPrefix" #prefix>
|
||||
{{ currentEnvConfig?.httpConfig.find((e) => e.type === 'NONE')?.url }}
|
||||
</template>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -128,6 +132,7 @@
|
|||
</div>
|
||||
<div class="px-[16px]">
|
||||
<MsTab
|
||||
v-if="requestVModel.activeTab"
|
||||
v-model:active-key="requestVModel.activeTab"
|
||||
:content-tab-list="contentTabList"
|
||||
:get-text-func="getTabBadge"
|
||||
|
@ -472,6 +477,11 @@
|
|||
}
|
||||
return t('apiScenario.customApi');
|
||||
});
|
||||
const showEnvPrefix = computed(
|
||||
() =>
|
||||
requestVModel.value.customizeRequestEnvEnable &&
|
||||
currentEnvConfig?.value.httpConfig.find((e) => e.type === 'NONE')?.url
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.stepResponses,
|
||||
|
@ -1007,7 +1017,7 @@
|
|||
}
|
||||
requestVModel.value = {
|
||||
executeLoading: false,
|
||||
activeTab: res.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
||||
activeTab: contentTabList.value[0].value,
|
||||
unSaved: false,
|
||||
isNew: false,
|
||||
label: res.name,
|
||||
|
@ -1018,6 +1028,7 @@
|
|||
name: res.name, // request里面还有个name但是是null
|
||||
resourceId: res.id,
|
||||
stepId: props.step?.id || '',
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
...parseRequestBodyResult,
|
||||
};
|
||||
if (_stepType.value.isQuoteApi && props.request && isHttpProtocol.value) {
|
||||
|
@ -1050,8 +1061,8 @@
|
|||
}
|
||||
}
|
||||
nextTick(() => {
|
||||
requestVModel.value.activeTab = contentTabList.value[0].value;
|
||||
// 等待内容渲染出来再隐藏loading
|
||||
requestVModel.value.activeTab = contentTabList.value[0].value;
|
||||
loading.value = false;
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -1073,6 +1084,8 @@
|
|||
requestVModel.value = cloneDeep({
|
||||
...defaultApiParams,
|
||||
...props.request,
|
||||
activeTab: contentTabList.value[0].value,
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
isNew: false,
|
||||
});
|
||||
if (_stepType.value.isQuoteApi) {
|
||||
|
|
|
@ -261,6 +261,7 @@
|
|||
...node,
|
||||
copyFromStepId: node.id,
|
||||
config: {
|
||||
...node.config,
|
||||
isRefScenarioStep: true, // 默认是完全引用的
|
||||
},
|
||||
id: getGenerateId(),
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
||||
type="text"
|
||||
class="!mr-0"
|
||||
@click="openScenarioTab(record)"
|
||||
@click="openScenarioTab(record, 'execute')"
|
||||
>
|
||||
{{ t('apiScenario.execute') }}
|
||||
</MsButton>
|
||||
|
@ -140,7 +140,7 @@
|
|||
v-permission="['PROJECT_API_SCENARIO:READ+ADD']"
|
||||
type="text"
|
||||
class="!mr-0"
|
||||
@click="openScenarioTab(record, true)"
|
||||
@click="openScenarioTab(record, 'copy')"
|
||||
>
|
||||
{{ t('common.copy') }}
|
||||
</MsButton>
|
||||
|
@ -353,7 +353,7 @@
|
|||
readOnly?: boolean; // 是否是只读模式
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'openScenario', record: ApiScenarioTableItem, isCopy?: boolean): void;
|
||||
(e: 'openScenario', record: ApiScenarioTableItem, action?: 'copy' | 'execute'): void;
|
||||
(e: 'refreshModuleTree', params: any): void;
|
||||
}>();
|
||||
|
||||
|
@ -943,8 +943,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
function openScenarioTab(record: ApiScenarioTableItem, isCopy = false) {
|
||||
emit('openScenario', record, isCopy);
|
||||
function openScenarioTab(record: ApiScenarioTableItem, action?: 'copy' | 'execute') {
|
||||
emit('openScenario', record, action);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
|
|
@ -325,10 +325,18 @@
|
|||
<a-radio :value="false">{{ t('apiScenario.sourceScenario') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="" class="hidden-item">
|
||||
<a-form-item :label="t('apiScenario.currentScenarioTip')">
|
||||
<a-radio-group v-model:model-value="scenarioConfigForm.useBothScenarioParam">
|
||||
<a-radio :value="false">{{ t('apiScenario.empty') }}</a-radio>
|
||||
<a-radio :value="true">{{ t('apiScenario.sourceScenarioEnv') }}</a-radio>
|
||||
<a-radio :value="true">
|
||||
{{
|
||||
t(
|
||||
scenarioConfigForm.useCurrentScenarioParam
|
||||
? 'apiScenario.sourceScenarioParams'
|
||||
: 'apiScenario.currentScenarioParams'
|
||||
)
|
||||
}}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="" class="hidden-item !mb-0">
|
||||
|
@ -352,11 +360,8 @@
|
|||
<div class="text-[var(--color-text-4)]">
|
||||
{{ t('apiScenario.valuePriority') }}
|
||||
</div>
|
||||
<div v-if="scenarioConfigForm.useCurrentScenarioParam" class="text-[var(--color-text-1)]">
|
||||
{{ t('apiScenario.currentScenarioAndNull') }}
|
||||
</div>
|
||||
<div v-else class="text-[var(--color-text-1)]">
|
||||
{{ t('apiScenario.sourceScenarioAndNull') }}
|
||||
<div v-if="scenarioConfigParamTip" class="text-[var(--color-text-1)]">
|
||||
{{ scenarioConfigParamTip }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-[12px]">
|
||||
|
@ -614,7 +619,74 @@
|
|||
useCurrentScenarioParam: true,
|
||||
});
|
||||
const showScenarioConfig = ref(false);
|
||||
const scenarioConfigParamTip = computed(() => {
|
||||
if (
|
||||
scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
!scenarioConfigForm.value.useBothScenarioParam &&
|
||||
!scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用当前场景参数-空值
|
||||
return t('apiScenario.currentScenarioAndNull');
|
||||
}
|
||||
if (
|
||||
scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
!scenarioConfigForm.value.useBothScenarioParam &&
|
||||
scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用当前场景参数-空值-且选择源场景环境
|
||||
return t('apiScenario.currentScenarioAndNullAndSourceEnv');
|
||||
}
|
||||
if (
|
||||
scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
scenarioConfigForm.value.useBothScenarioParam &&
|
||||
!scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用当前场景参数-原场景参数
|
||||
return t('apiScenario.currentScenarioAndSourceScenario');
|
||||
}
|
||||
if (
|
||||
scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
scenarioConfigForm.value.useBothScenarioParam &&
|
||||
scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用当前场景参数-原场景参数-且选择源场景环境
|
||||
return t('apiScenario.currentScenarioAndSourceScenarioAndSourceEnv');
|
||||
}
|
||||
if (
|
||||
!scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
!scenarioConfigForm.value.useBothScenarioParam &&
|
||||
!scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用原场景参数-空值
|
||||
return t('apiScenario.sourceScenarioAndNull');
|
||||
}
|
||||
if (
|
||||
!scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
!scenarioConfigForm.value.useBothScenarioParam &&
|
||||
scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用原场景参数-空值-且选择源场景环境
|
||||
return t('apiScenario.sourceScenarioAndNullAndSourceEnv');
|
||||
}
|
||||
if (
|
||||
!scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
scenarioConfigForm.value.useBothScenarioParam &&
|
||||
!scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用原场景参数-当前场景参数
|
||||
return t('apiScenario.sourceScenarioAndCurrentScenario');
|
||||
}
|
||||
if (
|
||||
!scenarioConfigForm.value.useCurrentScenarioParam &&
|
||||
scenarioConfigForm.value.useBothScenarioParam &&
|
||||
scenarioConfigForm.value.enableScenarioEnv
|
||||
) {
|
||||
// 使用原场景参数-当前场景参数-且选择源场景环境
|
||||
return t('apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv');
|
||||
}
|
||||
});
|
||||
|
||||
// 关闭场景配置弹窗
|
||||
function cancelScenarioConfig() {
|
||||
showScenarioConfig.value = false;
|
||||
scenarioConfigForm.value = {
|
||||
|
@ -625,11 +697,12 @@
|
|||
};
|
||||
}
|
||||
|
||||
// 应用场景配置
|
||||
function saveScenarioConfig() {
|
||||
if (activeStep.value) {
|
||||
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, activeStep.value.id, 'id');
|
||||
if (realStep) {
|
||||
realStep.refType = scenarioConfigForm.value.refType;
|
||||
realStep.refType = scenarioConfigForm.value.refType; // 更新场景引用类型
|
||||
realStep.config = {
|
||||
...realStep.config,
|
||||
...scenarioConfigForm.value,
|
||||
|
@ -1443,7 +1516,7 @@
|
|||
.arco-tree-node-title {
|
||||
@apply !cursor-pointer bg-white;
|
||||
|
||||
padding: 12px 4px;
|
||||
padding: 8px 4px;
|
||||
&:hover {
|
||||
background-color: var(--color-text-n9) !important;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="h-full w-full overflow-hidden">
|
||||
<div class="px-[24px] pt-[16px]">
|
||||
<MsDetailCard :title="`【${scenario.num}】${scenario.name}`" :description="description">
|
||||
<MsDetailCard :title="`【${scenario.num}】${scenario.name}`" :description="description" class="!py-[8px]">
|
||||
<template #titleAppend>
|
||||
<apiStatus :status="scenario.status" size="small" />
|
||||
</template>
|
||||
|
@ -137,6 +137,8 @@
|
|||
import step from '../components/step/index.vue';
|
||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
|
||||
import { followScenario } from '@/api/modules/api-test/scenario';
|
||||
|
||||
import { ApiScenarioDebugRequest, Scenario, ScenarioDetail } from '@/models/apiTest/scenario';
|
||||
import { ScenarioDetailComposition } from '@/enums/apiEnum';
|
||||
|
||||
|
@ -184,6 +186,8 @@
|
|||
async function toggleFollowReview() {
|
||||
try {
|
||||
followLoading.value = true;
|
||||
await followScenario(scenario.value.id || '');
|
||||
scenario.value.follow = !scenario.value.follow;
|
||||
Message.success(scenario.value.follow ? t('common.unFollowSuccess') : t('common.followSuccess'));
|
||||
emit('updateFollow');
|
||||
} catch (error) {
|
||||
|
@ -196,7 +200,7 @@
|
|||
|
||||
function share() {
|
||||
if (isSupported) {
|
||||
copy(`${window.location.href}&dId=${scenario.value.id}`);
|
||||
copy(`${window.location.href}&sId=${scenario.value.id}`);
|
||||
Message.success(t('common.copySuccess'));
|
||||
} else {
|
||||
Message.error(t('common.copyNotSupport'));
|
||||
|
|
|
@ -147,13 +147,27 @@
|
|||
]);
|
||||
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
||||
|
||||
function newTab(defaultScenarioInfo?: Scenario, isCopy = false) {
|
||||
function newTab(defaultScenarioInfo?: Scenario, action?: 'copy' | 'execute') {
|
||||
if (defaultScenarioInfo) {
|
||||
const isCopy = action === 'copy';
|
||||
let copySteps = defaultScenarioInfo.steps;
|
||||
if (isCopy) {
|
||||
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
|
||||
return {
|
||||
...node,
|
||||
copyFromStepId: node.id,
|
||||
id: getGenerateId(),
|
||||
};
|
||||
});
|
||||
}
|
||||
scenarioTabs.value.push({
|
||||
...defaultScenarioInfo,
|
||||
steps: copySteps,
|
||||
id: isCopy ? getGenerateId() : defaultScenarioInfo.id || '',
|
||||
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||
isNew: false,
|
||||
name: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||
isNew: isCopy,
|
||||
isExecute: action === 'execute',
|
||||
stepResponses: {},
|
||||
});
|
||||
} else {
|
||||
|
@ -279,7 +293,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function openScenarioTab(record: ApiScenarioTableItem | string, isCopy?: boolean) {
|
||||
async function openScenarioTab(record: ApiScenarioTableItem | string, action?: 'copy' | 'execute') {
|
||||
try {
|
||||
appStore.showLoading();
|
||||
const res = await getScenarioDetail(typeof record === 'string' ? record : record.id);
|
||||
|
@ -287,7 +301,7 @@
|
|||
if (!res.steps) {
|
||||
res.steps = [];
|
||||
}
|
||||
newTab(res, isCopy);
|
||||
newTab(res, action);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
|
|
@ -142,18 +142,29 @@ export default {
|
|||
'apiScenario.quoteMode': '引用模式',
|
||||
'apiScenario.fullQuote': '完全引用',
|
||||
'apiScenario.stepQuote': '步骤引用',
|
||||
'apiScenario.runRule': '运行取值规则设置',
|
||||
'apiScenario.currentScenario': '当前场景参数',
|
||||
'apiScenario.sourceScenario': '源场景参数',
|
||||
'apiScenario.runRule': '参数取值规则',
|
||||
'apiScenario.currentScenario': '优先当前场景参数',
|
||||
'apiScenario.sourceScenario': '优先源场景参数',
|
||||
'apiScenario.currentScenarioTip': '当前场景参数不存在时,取',
|
||||
'apiScenario.sourceScenarioTip': '源场景参数不存在时,取',
|
||||
'apiScenario.empty': '空值',
|
||||
'apiScenario.currentScenarioParams': '当前场景参数',
|
||||
'apiScenario.sourceScenarioParams': '源场景参数',
|
||||
'apiScenario.sourceScenarioEnv': '源场景环境',
|
||||
'apiScenario.valuePriority': '取值优先级:',
|
||||
'apiScenario.currentScenarioAndNull': '步骤参数>场景参数>环境参数>空值',
|
||||
'apiScenario.sourceScenarioAndNull': '源场景参数>源场景环境>步骤参数>当前场景参数>当前环境参数',
|
||||
'apiScenario.currentScenarioAndNull': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 空值',
|
||||
'apiScenario.currentScenarioAndNullAndSourceEnv': '当前步骤参数 > 当前场景参数 > 源环境参数 > 空值',
|
||||
'apiScenario.currentScenarioAndSourceScenario': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 源场景参数',
|
||||
'apiScenario.currentScenarioAndSourceScenarioAndSourceEnv': '当前步骤参数 > 当前场景参数 > 源场景参数> 源环境参数',
|
||||
'apiScenario.sourceScenarioAndNull': '源场景参数 > 空值',
|
||||
'apiScenario.sourceScenarioAndNullAndSourceEnv': '源场景参数 > 源环境参数 > 空值',
|
||||
'apiScenario.sourceScenarioAndCurrentScenario': '源场景参数 > 当前步骤参数 > 当前场景参数 > 当前环境参数',
|
||||
'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv': '源场景参数 > 源环境参数 > 当前步骤参数 > 当前场景参数',
|
||||
'apiScenario.fullQuoteTip': '完全引用:跟随源步骤内容及步骤状态变化,步骤状态不可调整',
|
||||
'apiScenario.stepQuoteTip': '步骤引用:仅跟随源步骤内容变化,步骤状态可调整',
|
||||
'apiScenario.sourceScenarioEnvTip': '运行环境,含环境参数',
|
||||
'apiScenario.setSuccess': '设置成功',
|
||||
'apiScenario.pleaseInputUrl': '请输入 url',
|
||||
// 执行历史
|
||||
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
|
||||
'apiScenario.executeHistory.num': '序号',
|
||||
|
|
Loading…
Reference in New Issue