fix(all): 修复bugs

This commit is contained in:
baiqi 2024-04-11 16:00:10 +08:00 committed by 刘瑞斌
parent b094ebf8eb
commit 2f5e5ada03
31 changed files with 279 additions and 165 deletions

View File

@ -431,7 +431,7 @@
} }
} }
.arco-checkbox-indeterminate .arco-checkbox-icon { .arco-checkbox-indeterminate .arco-checkbox-icon {
border-color: rgba(var(--primary-7)); border: 1px solid rgba(var(--primary-7)) !important;
background-color: rgba(var(--primary-1)); background-color: rgba(var(--primary-1));
&::after { &::after {
background-color: rgb(var(--primary-7)); background-color: rgb(var(--primary-7));

View File

@ -32,6 +32,7 @@
:read-only="props.disabled" :read-only="props.disabled"
:show-full-screen="false" :show-full-screen="false"
:show-theme-change="false" :show-theme-change="false"
@change="() => emit('change')"
> >
<template #rightBox> <template #rightBox>
<MsScriptMenu <MsScriptMenu
@ -99,6 +100,9 @@
showHeader: true, showHeader: true,
} }
); );
const emit = defineEmits<{
(e: 'change'): void;
}>();
const { t } = useI18n(); const { t } = useI18n();

View File

@ -33,8 +33,7 @@
v-if="attrs.selectorType === 'checkbox'" v-if="attrs.selectorType === 'checkbox'"
:total="attrs.showPagination ? (attrs.msPagination as MsPaginationI).total : (attrs.data as MsTableDataItem<TableData>[]).length" :total="attrs.showPagination ? (attrs.msPagination as MsPaginationI).total : (attrs.data as MsTableDataItem<TableData>[]).length"
:selected-keys="props.selectedKeys" :selected-keys="props.selectedKeys"
:selector-status="props.selectorStatus" :exclude-keys="Array.from(props.excludeKeys)"
:exclude-keys="[...props.excludeKeys]"
:current-data="attrs.data as Record<string,any>[]" :current-data="attrs.data as Record<string,any>[]"
:show-select-all="!!attrs.showPagination && props.showSelectorAll" :show-select-all="!!attrs.showPagination && props.showSelectorAll"
:disabled="(attrs.data as []).length === 0" :disabled="(attrs.data as []).length === 0"
@ -785,3 +784,20 @@
} }
} }
</style> </style>
<style lang="less">
.arco-table-filters-content {
@apply overflow-hidden;
max-width: 300px;
.arco-checkbox-group {
@apply flex w-full flex-col;
.arco-checkbox {
@apply w-full;
.arco-checkbox-label {
@apply flex-1 overflow-hidden;
}
}
}
}
</style>

View File

@ -20,7 +20,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import MsIcon from '../ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
@ -41,7 +41,6 @@
currentData: MsTableDataItem<Record<string, any>>[]; currentData: MsTableDataItem<Record<string, any>>[];
showSelectAll: boolean; showSelectAll: boolean;
disabled: boolean; disabled: boolean;
selectorStatus: SelectAllEnum;
excludeKeys: string[]; excludeKeys: string[];
}>(), }>(),
{ {
@ -52,32 +51,31 @@
} }
); );
const selectAllStatus = ref<SelectAllEnum>(SelectAllEnum.NONE);
const checked = computed({ const checked = computed({
get: () => { get: () => {
if (props.selectorStatus !== 'all') { //
return props.selectedKeys.size > 0 && props.selectedKeys.size === props.total; return (
} selectAllStatus.value === SelectAllEnum.ALL ||
if (props.selectorStatus === 'all') { (props.selectedKeys.size > 0 && props.selectedKeys.size === props.total)
return !props.excludeKeys?.length );
? true
: props.selectedKeys.size > 0 && props.selectedKeys.size === props.total;
}
}, },
set: (value) => { set: (value) => {
return value; return value;
}, },
}); });
const indeterminate = computed(() => { const indeterminate = computed(() => {
// 0 // key 0
if (props.selectorStatus === 'current') { return (
return props.selectedKeys.size > 0 && props.selectedKeys.size < props.total; props.excludeKeys.length > 0 ||
} (selectAllStatus.value !== SelectAllEnum.ALL &&
if (props.selectorStatus === 'all') { props.selectedKeys.size > 0 &&
return !props.excludeKeys?.length ? false : props.selectedKeys.size > 0 && props.selectedKeys.size < props.total; props.selectedKeys.size < props.total)
} );
}); });
const handleSelect = (v: string | number | Record<string, any> | undefined) => { const handleSelect = (v: string | number | Record<string, any> | undefined) => {
selectAllStatus.value = v as SelectAllEnum;
emit('change', v as SelectAllEnum); emit('change', v as SelectAllEnum);
}; };

View File

@ -415,7 +415,13 @@ export default function useTableProps<T>(
if (v === SelectAllEnum.NONE) { if (v === SelectAllEnum.NONE) {
// 清空选中项 // 清空选中项
resetSelector(); resetSelector();
} else { } else if (v === SelectAllEnum.CURRENT) {
// 先清空选中项,再选中当前页面所有数据
resetSelector();
collectIds(data as MsTableDataItem<T>[], rowKey);
} else if (v === SelectAllEnum.ALL) {
// 全选所有页的时候先清空排除项,再选中所有数据
propsRes.value.excludeKeys.clear();
collectIds(data as MsTableDataItem<T>[], rowKey); collectIds(data as MsTableDataItem<T>[], rowKey);
} }
}, },

View File

@ -151,6 +151,7 @@
innerInputValue.value = ''; innerInputValue.value = '';
tagsLength.value += 1; tagsLength.value += 1;
emit('update:modelValue', innerModelValue.value); emit('update:modelValue', innerModelValue.value);
emit('change', innerModelValue.value);
} }
emit('blur'); emit('blur');
} }

View File

@ -317,8 +317,8 @@ export interface LoopStepDetail extends StepDetailsCommon {
} }
// 场景引用配置 // 场景引用配置
export interface ScenarioStepConfig { export interface ScenarioStepConfig {
useCurrentScenarioParam: boolean; // 是否优先使用当前场景参数 useOriginScenarioParam: boolean; // 是否优先使用当前场景参数
useBothScenarioParam: boolean; // 是否当前场景参数和源场景参数都应用(勾选非空值时为 true useOriginScenarioParamPreferential: boolean; // 是否当前场景参数和源场景参数都应用(勾选非空值时为 true
enableScenarioEnv: boolean; // 是否应用源场景环境 enableScenarioEnv: boolean; // 是否应用源场景环境
} }
// 场景步骤详情 // 场景步骤详情
@ -436,8 +436,7 @@ export interface ApiScenarioDebugRequest {
reportId: string | number; reportId: string | number;
steps: ScenarioStepItem[]; steps: ScenarioStepItem[];
projectId: string; projectId: string;
uploadFileIds: string[]; stepFileParam: Record<string, ScenarioStepFileParams>;
linkFileIds: string[];
frontendDebug?: boolean; frontendDebug?: boolean;
} }

View File

@ -89,6 +89,7 @@
size="small" size="small"
@press-enter="isShowEditScriptNameInput = false" @press-enter="isShowEditScriptNameInput = false"
@blur="isShowEditScriptNameInput = false" @blur="isShowEditScriptNameInput = false"
@change="() => emit('change')"
/> />
</div> </div>
<div class="flex items-center justify-between px-[12px] pt-[12px]"> <div class="flex items-center justify-between px-[12px] pt-[12px]">
@ -195,6 +196,7 @@
:disabled="props.disabled" :disabled="props.disabled"
show-type="commonScript" show-type="commonScript"
:show-header="false" :show-header="false"
@change="() => emit('change')"
/> />
</div> </div>
</div> </div>
@ -230,6 +232,7 @@
:columns="scriptColumns" :columns="scriptColumns"
:height-used="heightUsed" :height-used="heightUsed"
:selectable="false" :selectable="false"
@change="() => emit('change')"
/> />
<MsCodeEditor <MsCodeEditor
v-else-if="commonScriptShowType === 'scriptContent' && condition.commonScriptInfo" v-else-if="commonScriptShowType === 'scriptContent' && condition.commonScriptInfo"
@ -703,6 +706,7 @@ if (!result){
}; };
scriptParams.value = (condition.value.commonScriptInfo?.params as any[]) || []; scriptParams.value = (condition.value.commonScriptInfo?.params as any[]) || [];
showQuoteDrawer.value = false; showQuoteDrawer.value = false;
Message.success(t('apiTestDebug.introduceSourceApplySuccess'));
} }
const showAddScriptDrawer = ref(false); const showAddScriptDrawer = ref(false);

View File

@ -798,6 +798,16 @@
// 便 // 便
if (key) { if (key) {
nextLine[key] = lastLineData[key]; nextLine[key] = lastLineData[key];
// Content-Type contentType
if (nextLine.contentType) {
if (lastLineData[key] === 'file') {
nextLine.contentType = RequestContentTypeEnum.OCTET_STREAM;
} else if (lastLineData[key] === 'json') {
nextLine.contentType = RequestContentTypeEnum.JSON;
} else {
nextLine.contentType = RequestContentTypeEnum.TEXT;
}
}
} }
}); });
paramsData.value.push(nextLine); paramsData.value.push(nextLine);

View File

@ -136,6 +136,7 @@
disabledParamValue?: boolean; // disabledParamValue?: boolean; //
disabledExceptParam?: boolean; // disabledExceptParam?: boolean; //
secondBoxHeight: number; secondBoxHeight: number;
isDrawer?: boolean;
uploadTempFileApi?: (file: File) => Promise<any>; // uploadTempFileApi?: (file: File) => Promise<any>; //
fileSaveAsSourceId?: string | number; // id fileSaveAsSourceId?: string | number; // id
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; // fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; //
@ -267,7 +268,8 @@
watch( watch(
() => props.layout, () => props.layout,
(val) => { (val) => {
heightUsed.value = val === 'horizontal' ? 428 : 430 + props.secondBoxHeight; const otherHeight = props.isDrawer ? 328 : 430;
heightUsed.value = val === 'horizontal' ? otherHeight : otherHeight + props.secondBoxHeight;
}, },
{ {
immediate: true, immediate: true,
@ -278,7 +280,7 @@
() => props.secondBoxHeight, () => props.secondBoxHeight,
(val) => { (val) => {
if (props.layout === 'vertical') { if (props.layout === 'vertical') {
heightUsed.value = 430 + val; heightUsed.value = (props.isDrawer ? 328 : 430) + val;
} }
}, },
{ {

View File

@ -43,6 +43,7 @@
disabledParamValue?: boolean; // disabledParamValue?: boolean; //
disabledExceptParam?: boolean; // disabledExceptParam?: boolean; //
typeTitle?: string; typeTitle?: string;
isDrawer?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:selectedKeys', value: string[]): void; (e: 'update:selectedKeys', value: string[]): void;
@ -83,9 +84,9 @@
const heightUsed = computed(() => { const heightUsed = computed(() => {
if (props.layout === 'horizontal') { if (props.layout === 'horizontal') {
return 428; return props.isDrawer ? 328 : 428;
} }
return 428 + props.secondBoxHeight; return (props.isDrawer ? 328 : 428) + props.secondBoxHeight;
}); });
const scroll = computed(() => (props.layout === 'horizontal' ? { x: '700px' } : { x: '100%' })); const scroll = computed(() => (props.layout === 'horizontal' ? { x: '700px' } : { x: '100%' }));

View File

@ -548,10 +548,11 @@
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP'); const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
const temporaryResponseMap = {}; // websockettab const temporaryResponseMap = {}; // websockettab
const isInitPluginForm = ref(false); const isInitPluginForm = ref(false);
const isSwitchingContent = ref(false); //
function handleActiveDebugChange() { function handleActiveDebugChange() {
if (!loading.value || (!isHttpProtocol.value && isInitPluginForm.value)) { if ((!isSwitchingContent.value && !loading.value) || (!isHttpProtocol.value && isInitPluginForm.value)) {
// change // change
requestVModel.value.unSaved = true; requestVModel.value.unSaved = true;
} }
} }
@ -1225,6 +1226,10 @@
watch( watch(
() => requestVModel.value.id, () => requestVModel.value.id,
async () => { async () => {
isSwitchingContent.value = true; //
nextTick(() => {
isSwitchingContent.value = false; //
});
if (requestVModel.value.protocol !== 'HTTP') { if (requestVModel.value.protocol !== 'HTTP') {
requestVModel.value.activeTab = RequestComposition.PLUGIN; requestVModel.value.activeTab = RequestComposition.PLUGIN;
if (protocolOptions.value.length === 0) { if (protocolOptions.value.length === 0) {

View File

@ -57,7 +57,7 @@
} }
// //
if (props.isScenario) { if (props.isScenario) {
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL]; return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL, RequestConditionProcessor.TIME_WAITING];
} }
// //
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.TIME_WAITING]; return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.TIME_WAITING];

View File

@ -53,6 +53,7 @@
disabledParamValue?: boolean; // disabledParamValue?: boolean; //
disabledExceptParam?: boolean; // disabledExceptParam?: boolean; //
secondBoxHeight: number; secondBoxHeight: number;
isDrawer?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:params', value: any[]): void; (e: 'update:params', value: any[]): void;
@ -122,7 +123,8 @@
watch( watch(
() => props.layout, () => props.layout,
(val) => { (val) => {
heightUsed.value = val === 'horizontal' ? 428 : 428 + props.secondBoxHeight; const otherHeight = props.isDrawer ? 328 : 430;
heightUsed.value = val === 'horizontal' ? otherHeight : otherHeight + props.secondBoxHeight;
}, },
{ {
immediate: true, immediate: true,
@ -133,7 +135,7 @@
() => props.secondBoxHeight, () => props.secondBoxHeight,
(val) => { (val) => {
if (props.layout === 'vertical') { if (props.layout === 'vertical') {
heightUsed.value = 428 + val; heightUsed.value = (props.isDrawer ? 328 : 430) + val;
} }
}, },
{ {

View File

@ -4,7 +4,7 @@
class="flex items-center justify-between gap-[24px] text-[14px]" class="flex items-center justify-between gap-[24px] text-[14px]"
> >
<a-tooltip :content="props.requestResult.fakeErrorCode"> <a-tooltip :content="props.requestResult.fakeErrorCode">
<executeStatus :status="props.requestResult.status" size="small" class="ml-[4px]" /> <executeStatus :status="finalStatus" size="small" class="ml-[4px]" />
</a-tooltip> </a-tooltip>
<a-popover position="left" content-class="response-popover-content"> <a-popover position="left" content-class="response-popover-content">
<div class="one-line-text max-w-[200px]" :style="{ color: statusCodeColor }"> <div class="one-line-text max-w-[200px]" :style="{ color: statusCodeColor }">
@ -52,6 +52,7 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { RequestResult } from '@/models/apiTest/common'; import { RequestResult } from '@/models/apiTest/common';
import { ScenarioExecuteStatus } from '@/enums/apiEnum';
const props = defineProps<{ const props = defineProps<{
requestResult?: RequestResult; requestResult?: RequestResult;
@ -99,6 +100,12 @@
} }
return ''; return '';
}); });
const finalStatus = computed(() => {
if (props.requestResult?.fakeErrorCode) {
return ScenarioExecuteStatus.FAKE_ERROR;
}
return props.requestResult?.isSuccessful ? ScenarioExecuteStatus.SUCCESS : ScenarioExecuteStatus.FAILED;
});
</script> </script>
<style lang="less"> <style lang="less">

View File

@ -53,6 +53,7 @@
secondBoxHeight: number; secondBoxHeight: number;
disabledParamValue?: boolean; // disabledParamValue?: boolean; //
disabledExceptParam?: boolean; // disabledExceptParam?: boolean; //
isDrawer?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:params', value: any[]): void; (e: 'update:params', value: any[]): void;
@ -123,7 +124,8 @@
watch( watch(
() => props.layout, () => props.layout,
(val) => { (val) => {
heightUsed.value = val === 'horizontal' ? 428 : 428 + props.secondBoxHeight; const otherHeight = props.isDrawer ? 328 : 430;
heightUsed.value = val === 'horizontal' ? otherHeight : otherHeight + props.secondBoxHeight;
}, },
{ {
immediate: true, immediate: true,
@ -134,7 +136,7 @@
() => props.secondBoxHeight, () => props.secondBoxHeight,
(val) => { (val) => {
if (props.layout === 'vertical') { if (props.layout === 'vertical') {
heightUsed.value = 428 + val; heightUsed.value = (props.isDrawer ? 328 : 430) + val;
} }
}, },
{ {

View File

@ -1,7 +1,5 @@
import { cloneDeep, isEqual } from 'lodash-es'; import { cloneDeep, isEqual } from 'lodash-es';
import { RequestParam } from './requestComposition/index.vue';
import { ExecuteBody } from '@/models/apiTest/common'; import { ExecuteBody } from '@/models/apiTest/common';
import { RequestParamsType } from '@/enums/apiEnum'; import { RequestParamsType } from '@/enums/apiEnum';
@ -11,6 +9,7 @@ import {
defaultKeyValueParamItem, defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
} from './config'; } from './config';
import type { RequestParam } from './requestComposition/index.vue';
export interface ParseResult { export interface ParseResult {
uploadFileIds: string[]; uploadFileIds: string[];

View File

@ -348,7 +348,6 @@
sortDefinition, sortDefinition,
updateDefinition, updateDefinition,
} from '@/api/modules/api-test/management'; } from '@/api/modules/api-test/management';
import { getProjectOptions } from '@/api/modules/project-management/projectMember';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import useTableStore from '@/hooks/useTableStore'; import useTableStore from '@/hooks/useTableStore';

View File

@ -23,13 +23,31 @@
v-if="props.step && [ScenarioStepType.API, ScenarioStepType.CUSTOM_REQUEST].includes(props.step?.stepType)" v-if="props.step && [ScenarioStepType.API, ScenarioStepType.CUSTOM_REQUEST].includes(props.step?.stepType)"
:step="props.step" :step="props.step"
/> />
<a-tooltip :content="title" position="bottom"> <a-tooltip v-if="!isShowEditStepNameInput" :content="title" position="bottom">
<div class="one-line-text"> <div class="flex items-center gap-[4px]">
{{ title }} <div class="one-line-text">
{{ title }}
</div>
<MsIcon
type="icon-icon_edit_outlined"
class="cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="isShowEditStepNameInput = true"
/>
</div> </div>
</a-tooltip> </a-tooltip>
</div> </div>
<div class="ml-auto flex items-center gap-[16px]"> <a-input
v-if="isShowEditStepNameInput"
ref="stepNameInputRef"
v-model:model-value="requestVModel.name"
class="flex-1"
:placeholder="t('apiScenario.pleaseInputStepName')"
:max-length="255"
show-word-limit
@press-enter="updateStepName"
@blur="updateStepName"
/>
<div v-show="!isShowEditStepNameInput" class="ml-auto flex items-center gap-[16px]">
<div <div
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST" v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
class="customApiDrawer-title-right flex items-center gap-[16px]" class="customApiDrawer-title-right flex items-center gap-[16px]"
@ -184,7 +202,7 @@
class="no-content relative mt-[8px] border-b" class="no-content relative mt-[8px] border-b"
/> />
</div> </div>
<div ref="splitContainerRef" class="h-[calc(100%-87px)]"> <div ref="splitContainerRef" class="h-[calc(100%-97px)]">
<MsSplitBox <MsSplitBox
ref="verticalSplitBoxRef" ref="verticalSplitBoxRef"
v-model:size="splitBoxSize" v-model:size="splitBoxSize"
@ -228,6 +246,7 @@
:disabled-except-param="!isEditableApi" :disabled-except-param="!isEditableApi"
:layout="activeLayout" :layout="activeLayout"
:second-box-height="secondBoxHeight" :second-box-height="secondBoxHeight"
is-drawer
@change="handleActiveDebugChange" @change="handleActiveDebugChange"
/> />
<httpBody <httpBody
@ -241,6 +260,7 @@
:file-save-as-source-id="scenarioId" :file-save-as-source-id="scenarioId"
:file-save-as-api="transferFile" :file-save-as-api="transferFile"
:file-module-options-api="getTransferOptions" :file-module-options-api="getTransferOptions"
is-drawer
@change="handleActiveDebugChange" @change="handleActiveDebugChange"
/> />
<httpQuery <httpQuery
@ -250,6 +270,7 @@
:disabled-param-value="!isEditableApi && !isEditableParamValue" :disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi" :disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight" :second-box-height="secondBoxHeight"
is-drawer
@change="handleActiveDebugChange" @change="handleActiveDebugChange"
/> />
<httpRest <httpRest
@ -259,6 +280,7 @@
:disabled-param-value="!isEditableApi && !isEditableParamValue" :disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi" :disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight" :second-box-height="secondBoxHeight"
is-drawer
@change="handleActiveDebugChange" @change="handleActiveDebugChange"
/> />
<precondition <precondition
@ -542,9 +564,9 @@
_stepType.value.isQuoteApi || _stepType.value.isQuoteApi ||
props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST
) { ) {
return props.step?.name; return requestVModel.value.name || props.step?.name;
} }
return t('apiScenario.customApi'); return requestVModel.value.name || t('apiScenario.customApi');
}); });
// //
const showEnvPrefix = computed( const showEnvPrefix = computed(
@ -589,16 +611,22 @@
const isEditableParamValue = computed(() => !props.step?.isQuoteScenarioStep && _stepType.value.isQuoteApi); const isEditableParamValue = computed(() => !props.step?.isQuoteScenarioStep && _stepType.value.isQuoteApi);
// HTTP // HTTP
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP'); const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
const isInitPluginForm = ref(false); const isInitPluginForm = ref(false);
const isSwitchingContent = ref(false); //
function handleActiveDebugChange() { function handleActiveDebugChange() {
if (!loading.value || (!isHttpProtocol.value && isInitPluginForm.value)) { if ((!isSwitchingContent.value && !loading.value) || (!isHttpProtocol.value && isInitPluginForm.value)) {
// change // change
requestVModel.value.unSaved = true; requestVModel.value.unSaved = true;
} }
} }
const isShowEditStepNameInput = ref(false);
function updateStepName() {
isShowEditStepNameInput.value = false;
}
// tabKey // tabKey
const commonContentTabKey = [ const commonContentTabKey = [
RequestComposition.PRECONDITION, RequestComposition.PRECONDITION,
@ -1159,6 +1187,16 @@
emit('replace', newStep); emit('replace', newStep);
} }
watch(
() => props.request?.stepId,
() => {
isSwitchingContent.value = true;
},
{
immediate: true,
}
);
watch( watch(
() => visible.value, () => visible.value,
async (val) => { async (val) => {
@ -1194,6 +1232,9 @@
}); });
} }
requestVModel.value.activeTab = contentTabList.value[0].value; requestVModel.value.activeTab = contentTabList.value[0].value;
nextTick(() => {
isSwitchingContent.value = false;
});
} }
}, },
{ {

View File

@ -32,7 +32,11 @@
<a-tooltip :content="activeStep?.name"> <a-tooltip :content="activeStep?.name">
<div class="one-line-text max-w-[300px]"> {{ characterLimit(activeStep?.name) }}</div> <div class="one-line-text max-w-[300px]"> {{ characterLimit(activeStep?.name) }}</div>
</a-tooltip> </a-tooltip>
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" /> <MsIcon
type="icon-icon_edit_outlined"
class="cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="showEditScriptNameInput"
/>
</div> </div>
<div <div
v-if="activeStep && !activeStep.isQuoteScenarioStep && activeStep.resourceId" v-if="activeStep && !activeStep.isQuoteScenarioStep && activeStep.resourceId"
@ -117,7 +121,7 @@
class="relative mt-[8px] border-b" class="relative mt-[8px] border-b"
/> />
</div> </div>
<div ref="splitContainerRef" class="h-[calc(100%-87px)]"> <div ref="splitContainerRef" class="h-[calc(100%-97px)]">
<MsSplitBox <MsSplitBox
ref="verticalSplitBoxRef" ref="verticalSplitBoxRef"
v-model:size="splitBoxSize" v-model:size="splitBoxSize"
@ -466,6 +470,7 @@
const isEditableApi = computed(() => !activeStep.value?.isQuoteScenarioStep && _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 isSwitchingContent = ref(false); //
const loading = ref(false); const loading = ref(false);
function handleActiveDebugChange() { function handleActiveDebugChange() {
@ -991,6 +996,16 @@
emit('replace', newStep); emit('replace', newStep);
} }
watch(
() => props.request?.stepId,
() => {
isSwitchingContent.value = true;
},
{
immediate: true,
}
);
watch( watch(
() => visible.value, () => visible.value,
async (val) => { async (val) => {
@ -1010,6 +1025,9 @@
await initQuoteCaseDetail(); await initQuoteCaseDetail();
} }
handleActiveDebugProtocolChange(requestVModel.value.protocol); handleActiveDebugProtocolChange(requestVModel.value.protocol);
nextTick(() => {
isSwitchingContent.value = false;
});
} }
} }
); );

View File

@ -24,7 +24,13 @@
/> />
</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" :disabled="isReadonly" :is-build-in="true" /> <conditionContent
v-if="visible"
v-model:data="activeItem"
:disabled="isReadonly"
:is-build-in="true"
@change="unSaved = 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>
@ -72,7 +78,7 @@
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'add', name: string, scriptProcessor: ExecuteConditionProcessor): void; (e: 'add', name: string, scriptProcessor: ExecuteConditionProcessor): void;
(e: 'save', name: string, scriptProcessor: ExecuteConditionProcessor): void; (e: 'save', name: string, scriptProcessor: ExecuteConditionProcessor, unSaved: boolean): void;
}>(); }>();
const defaultScript = { const defaultScript = {
@ -90,6 +96,7 @@
const visible = defineModel<boolean>('visible', { required: true }); const visible = defineModel<boolean>('visible', { required: true });
const isReadonly = computed(() => props.step?.isQuoteScenarioStep); const isReadonly = computed(() => props.step?.isQuoteScenarioStep);
const unSaved = ref(false);
const currentLoop = ref(1); const currentLoop = ref(1);
const currentResponse = computed(() => { const currentResponse = computed(() => {
if (props.step?.uniqueId) { if (props.step?.uniqueId) {
@ -131,7 +138,7 @@
function handleClose() { function handleClose() {
if (props.detail) { if (props.detail) {
emit('save', scriptName.value, activeItem.value); emit('save', scriptName.value, activeItem.value, unSaved.value);
} }
scriptName.value = ''; scriptName.value = '';
activeItem.value = defaultScript as unknown as ExecuteConditionProcessor; activeItem.value = defaultScript as unknown as ExecuteConditionProcessor;

View File

@ -54,8 +54,8 @@ export const defaultTimeController = {
// 场景配置 // 场景配置
export const defaultScenarioStepConfig: ScenarioStepConfig = { export const defaultScenarioStepConfig: ScenarioStepConfig = {
enableScenarioEnv: false, enableScenarioEnv: false,
useBothScenarioParam: false, useOriginScenarioParamPreferential: true,
useCurrentScenarioParam: true, useOriginScenarioParam: false,
}; };
export const defaultStepItemCommon = { export const defaultStepItemCommon = {

View File

@ -1142,7 +1142,7 @@
scheduleConfig.value = { scheduleConfig.value = {
scenarioId: record.id, scenarioId: record.id,
enable: true, enable: true,
cron: '0 0 0/1 * * ? ', cron: '0 0 0/1 * * ?',
config: { config: {
poolId: defaultPoolId?.value, poolId: defaultPoolId?.value,
grouped: false, grouped: false,

View File

@ -27,40 +27,6 @@
</div> </div>
</a-tooltip> </a-tooltip>
</div> </div>
<div style="font-weight: bold">
{{ t('apiScenario.setting.run.config') }}
</div>
<div class="mb-[16px] mt-[10px] flex items-center gap-[8px]">
<a-switch v-model:model-value="form.enableStepWait" type="line" size="small" @change="emit('change')" />
{{ t('apiScenario.setting.step.waitTime') }}
<a-tooltip :content="t('apiScenario.setting.waitTime.tip')">
<div>
<MsIcon
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
type="icon-icon-maybe_outlined"
/>
</div>
</a-tooltip>
</div>
<a-form-item v-if="form.stepWaitTime !== null || form.stepWaitTime !== undefined" class="flex-1">
<template #label>
<div class="flex items-center">
{{ t('apiScenario.setting.waitTime') }}
<div class="text-[var(--color-text-brand)]">(ms)</div>
</div>
</template>
<a-input-number
v-model:model-value="form.stepWaitTime"
mode="button"
:step="100"
:min="0"
:max="600000"
model-event="input"
class="w-[160px]"
@blur="handleInputChange"
/>
</a-form-item>
<a-form-item class="flex-1"> <a-form-item class="flex-1">
<template #label> <template #label>

View File

@ -317,35 +317,49 @@
<a-radio :value="ScenarioStepRefType.PARTIAL_REF">{{ t('apiScenario.stepQuote') }}</a-radio> <a-radio :value="ScenarioStepRefType.PARTIAL_REF">{{ t('apiScenario.stepQuote') }}</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item :label="t('apiScenario.runRule')"> <a-form-item label="" class="hidden-item">
<a-radio-group v-model:model-value="scenarioConfigForm.useCurrentScenarioParam" type="button"> <div class="flex items-center gap-[8px]">
<a-radio :value="true">{{ t('apiScenario.currentScenario') }}</a-radio> <a-switch
<a-radio :value="false">{{ t('apiScenario.sourceScenario') }}</a-radio> v-model:model-value="scenarioConfigForm.useOriginScenarioParam"
</a-radio-group> class="ml-[6px]"
size="small"
></a-switch>
{{ t('apiScenario.sourceScenarioParams') }}
</div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item v-show="scenarioConfigForm.useOriginScenarioParam" class="hidden-item">
:label=" <a-radio-group v-model:model-value="scenarioConfigForm.useOriginScenarioParamPreferential" type="button">
scenarioConfigForm.useCurrentScenarioParam
? t('apiScenario.currentScenarioTip')
: t('apiScenario.sourceScenarioTip')
"
>
<a-radio-group v-model:model-value="scenarioConfigForm.useBothScenarioParam">
<a-radio :value="false">{{ t('apiScenario.empty') }}</a-radio>
<a-radio :value="true"> <a-radio :value="true">
{{ <div class="flex items-center gap-[4px]">
t( {{ t('apiScenario.sourceScenario') }}
scenarioConfigForm.useCurrentScenarioParam <a-tooltip :content="t('apiScenario.sourceScenarioTip')" position="right">
? 'apiScenario.sourceScenarioParams' <icon-question-circle
: 'apiScenario.currentScenarioParams' class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
) size="16"
}} />
</a-tooltip>
</div>
</a-radio>
<a-radio :value="false">
<div class="flex items-center gap-[4px]">
{{ t('apiScenario.currentScenario') }}
<a-tooltip :content="t('apiScenario.currentScenarioTip')" position="right">
<icon-question-circle
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
size="16"
/>
</a-tooltip>
</div>
</a-radio> </a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item label="" class="hidden-item !mb-0"> <a-form-item label="" class="hidden-item !mb-0">
<div class="flex items-center gap-[8px]"> <div class="flex items-center gap-[8px]">
<a-checkbox v-model:model-value="scenarioConfigForm.enableScenarioEnv" class="ml-[6px]"></a-checkbox> <a-switch
v-model:model-value="scenarioConfigForm.enableScenarioEnv"
class="ml-[6px]"
size="small"
></a-switch>
<div class="flex items-center gap-[4px]"> <div class="flex items-center gap-[4px]">
{{ t('apiScenario.sourceScenarioEnv') }} {{ t('apiScenario.sourceScenarioEnv') }}
<a-tooltip :content="t('apiScenario.sourceScenarioEnvTip')" position="right"> <a-tooltip :content="t('apiScenario.sourceScenarioEnvTip')" position="right">
@ -760,70 +774,70 @@
>({ >({
refType: ScenarioStepRefType.REF, refType: ScenarioStepRefType.REF,
enableScenarioEnv: false, enableScenarioEnv: false,
useBothScenarioParam: false, useOriginScenarioParamPreferential: true,
useCurrentScenarioParam: true, useOriginScenarioParam: false,
}); });
const showScenarioConfig = ref(false); const showScenarioConfig = ref(false);
const scenarioConfigParamTip = computed(() => { const scenarioConfigParamTip = computed(() => {
if ( if (
scenarioConfigForm.value.useCurrentScenarioParam && scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useBothScenarioParam && !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv !scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使- // 使-
return t('apiScenario.currentScenarioAndNull'); return t('apiScenario.currentScenarioAndNull');
} }
if ( if (
scenarioConfigForm.value.useCurrentScenarioParam && scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useBothScenarioParam && !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使-- // 使--
return t('apiScenario.currentScenarioAndNullAndSourceEnv'); return t('apiScenario.currentScenarioAndNullAndSourceEnv');
} }
if ( if (
scenarioConfigForm.value.useCurrentScenarioParam && scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useBothScenarioParam && scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv !scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使- // 使-
return t('apiScenario.currentScenarioAndSourceScenario'); return t('apiScenario.currentScenarioAndSourceScenario');
} }
if ( if (
scenarioConfigForm.value.useCurrentScenarioParam && scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useBothScenarioParam && scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使-- // 使--
return t('apiScenario.currentScenarioAndSourceScenarioAndSourceEnv'); return t('apiScenario.currentScenarioAndSourceScenarioAndSourceEnv');
} }
if ( if (
!scenarioConfigForm.value.useCurrentScenarioParam && !scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useBothScenarioParam && !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv !scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使- // 使-
return t('apiScenario.sourceScenarioAndNull'); return t('apiScenario.sourceScenarioAndNull');
} }
if ( if (
!scenarioConfigForm.value.useCurrentScenarioParam && !scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useBothScenarioParam && !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使-- // 使--
return t('apiScenario.sourceScenarioAndNullAndSourceEnv'); return t('apiScenario.sourceScenarioAndNullAndSourceEnv');
} }
if ( if (
!scenarioConfigForm.value.useCurrentScenarioParam && !scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useBothScenarioParam && scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv !scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使- // 使-
return t('apiScenario.sourceScenarioAndCurrentScenario'); return t('apiScenario.sourceScenarioAndCurrentScenario');
} }
if ( if (
!scenarioConfigForm.value.useCurrentScenarioParam && !scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useBothScenarioParam && scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv scenarioConfigForm.value.enableScenarioEnv
) { ) {
// 使-- // 使--
@ -837,8 +851,8 @@
scenarioConfigForm.value = { scenarioConfigForm.value = {
refType: ScenarioStepRefType.REF, refType: ScenarioStepRefType.REF,
enableScenarioEnv: false, enableScenarioEnv: false,
useBothScenarioParam: false, useOriginScenarioParamPreferential: true,
useCurrentScenarioParam: true, useOriginScenarioParam: false,
}; };
} }
@ -1382,10 +1396,7 @@
} }
async function realExecute( async function realExecute(
executeParams: Pick< executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId' | 'stepFileParam'>,
ApiScenarioDebugRequest,
'steps' | 'stepDetails' | 'reportId' | 'uploadFileIds' | 'linkFileIds'
>,
executeType?: 'localExec' | 'serverExec' executeType?: 'localExec' | 'serverExec'
) { ) {
const [currentStep] = executeParams.steps; const [currentStep] = executeParams.steps;
@ -1455,8 +1466,9 @@
steps: [realStep as ScenarioStepItem], steps: [realStep as ScenarioStepItem],
stepDetails: _stepDetails, stepDetails: _stepDetails,
reportId: realStep.reportId, reportId: realStep.reportId,
uploadFileIds: stepFileParam?.uploadFileIds || [], stepFileParam: {
linkFileIds: stepFileParam?.linkFileIds || [], [realStep.id]: stepFileParam,
},
}, },
isPriorityLocalExec?.value ? 'localExec' : 'serverExec' isPriorityLocalExec?.value ? 'localExec' : 'serverExec'
); );
@ -1474,6 +1486,7 @@
delete scenario.value.stepResponses[realStep.uniqueId]; // delete scenario.value.stepResponses[realStep.uniqueId]; //
realStep.reportId = getGenerateId(); realStep.reportId = getGenerateId();
realStep.executeStatus = ScenarioExecuteStatus.EXECUTING; realStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
const stepFileParam = scenario.value.stepFileParam[realStep.id];
request.executeLoading = true; request.executeLoading = true;
realExecute( realExecute(
{ {
@ -1482,15 +1495,15 @@
[realStep.id]: request, [realStep.id]: request,
}, },
reportId: realStep.reportId, reportId: realStep.reportId,
uploadFileIds: request.uploadFileIds || [], stepFileParam: {
linkFileIds: request.linkFileIds || [], [realStep.uniqueId]: stepFileParam,
},
}, },
executeType executeType
); );
} else { } else {
// //
const reportId = getGenerateId(); const reportId = getGenerateId();
delete scenario.value.stepResponses[request.stepId]; //
request.executeLoading = true; request.executeLoading = true;
activeStep.value = { activeStep.value = {
id: request.stepId, id: request.stepId,
@ -1513,8 +1526,12 @@
[request.stepId]: request, [request.stepId]: request,
}, },
reportId, reportId,
uploadFileIds: request.uploadFileIds || [], stepFileParam: {
linkFileIds: request.linkFileIds || [], [request.stepId]: {
uploadFileIds: request.uploadFileIds || [],
linkFileIds: request.linkFileIds || [],
},
},
}, },
executeType executeType
); );
@ -1668,7 +1685,7 @@
handleCreateStep( handleCreateStep(
{ {
stepType: ScenarioStepType.CUSTOM_REQUEST, stepType: ScenarioStepType.CUSTOM_REQUEST,
name: t('apiScenario.customApi'), name: request.name || t('apiScenario.customApi'),
config: { config: {
protocol: request.protocol, protocol: request.protocol,
method: request.method, method: request.method,
@ -1694,7 +1711,7 @@
sort: steps.value.length + 1, sort: steps.value.length + 1,
stepType: ScenarioStepType.CUSTOM_REQUEST, stepType: ScenarioStepType.CUSTOM_REQUEST,
refType: ScenarioStepRefType.DIRECT, refType: ScenarioStepRefType.DIRECT,
name: t('apiScenario.customApi'), name: request.name || t('apiScenario.customApi'),
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
}); });
} }
@ -1731,6 +1748,7 @@
...activeStep.value.config, ...activeStep.value.config,
method: request.method, method: request.method,
}; };
activeStep.value.name = request.name;
emit('updateResource', request.uploadFileIds, request.linkFileIds); emit('updateResource', request.uploadFileIds, request.linkFileIds);
activeStep.value = undefined; activeStep.value = undefined;
} }
@ -1788,11 +1806,14 @@
scenario.value.unSaved = true; scenario.value.unSaved = true;
} }
function saveScriptStep(name: string, scriptProcessor: ExecuteConditionProcessor) { function saveScriptStep(name: string, scriptProcessor: ExecuteConditionProcessor, unSaved = false) {
if (activeStep.value) { if (activeStep.value) {
stepDetails.value[activeStep.value.id] = cloneDeep(scriptProcessor); stepDetails.value[activeStep.value.id] = cloneDeep(scriptProcessor);
activeStep.value.name = name; activeStep.value.name = name;
activeStep.value = undefined; activeStep.value = undefined;
if (unSaved) {
scenario.value.unSaved = true;
}
} }
} }

View File

@ -32,6 +32,7 @@
:scenario="scenario as ScenarioDetail" :scenario="scenario as ScenarioDetail"
:module-tree="props.moduleTree" :module-tree="props.moduleTree"
class="w-[30%]" class="w-[30%]"
@change="scenario.unSaved = true"
/> />
</a-tab-pane> </a-tab-pane>
<a-tab-pane <a-tab-pane

View File

@ -256,9 +256,8 @@
environmentId: currentEnvConfig.value?.id || '', environmentId: currentEnvConfig.value?.id || '',
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
scenarioConfig: activeScenarioTab.value.scenarioConfig, scenarioConfig: activeScenarioTab.value.scenarioConfig,
uploadFileIds: activeScenarioTab.value.uploadFileIds,
linkFileIds: activeScenarioTab.value.linkFileIds,
...executeParams, ...executeParams,
stepFileParam: activeScenarioTab.value.stepFileParam,
steps: mapTree(executeParams.steps, (node) => { steps: mapTree(executeParams.steps, (node) => {
return { return {
...node, ...node,
@ -274,8 +273,7 @@
environmentId: currentEnvConfig.value?.id || '', environmentId: currentEnvConfig.value?.id || '',
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
scenarioConfig: activeScenarioTab.value.scenarioConfig, scenarioConfig: activeScenarioTab.value.scenarioConfig,
uploadFileIds: activeScenarioTab.value.uploadFileIds, stepFileParam: activeScenarioTab.value.stepFileParam,
linkFileIds: activeScenarioTab.value.linkFileIds,
frontendDebug: executeType === 'localExec', frontendDebug: executeType === 'localExec',
...executeParams, ...executeParams,
steps: mapTree(executeParams.steps, (node) => { steps: mapTree(executeParams.steps, (node) => {

View File

@ -167,7 +167,7 @@ export default {
'apiScenario.empty': 'Empty Value', 'apiScenario.empty': 'Empty Value',
'apiScenario.currentScenarioParams': 'Current Scenario Parameters', 'apiScenario.currentScenarioParams': 'Current Scenario Parameters',
'apiScenario.sourceScenarioParams': 'Source Scenario Parameters', 'apiScenario.sourceScenarioParams': 'Source Scenario Parameters',
'apiScenario.sourceScenarioEnv': 'Source Scenario Environment', 'apiScenario.sourceScenarioEnv': 'Use source Scenario Environment',
'apiScenario.valuePriority': 'Value Priority:', 'apiScenario.valuePriority': 'Value Priority:',
'apiScenario.currentScenarioAndNull': 'apiScenario.currentScenarioAndNull':
'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Empty Value', 'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Empty Value',

View File

@ -160,12 +160,12 @@ export default {
'apiScenario.runRule': '参数取值规则', 'apiScenario.runRule': '参数取值规则',
'apiScenario.currentScenario': '优先当前场景参数', 'apiScenario.currentScenario': '优先当前场景参数',
'apiScenario.sourceScenario': '优先原场景参数', 'apiScenario.sourceScenario': '优先原场景参数',
'apiScenario.currentScenarioTip': '当前场景参数不存在时,取', 'apiScenario.sourceScenarioTip': '优先使用原场景参数,没有则取当前场景参数',
'apiScenario.sourceScenarioTip': '原场景参数不存在时,取', 'apiScenario.currentScenarioTip': '优先使用当前场景参数,没有则取原场景参数',
'apiScenario.empty': '空值', 'apiScenario.empty': '空值',
'apiScenario.currentScenarioParams': '当前场景参数', 'apiScenario.currentScenarioParams': '当前场景参数',
'apiScenario.sourceScenarioParams': '原场景参数', 'apiScenario.sourceScenarioParams': '使用原场景参数',
'apiScenario.sourceScenarioEnv': '原场景环境', 'apiScenario.sourceScenarioEnv': '使用原场景环境',
'apiScenario.valuePriority': '取值优先级:', 'apiScenario.valuePriority': '取值优先级:',
'apiScenario.currentScenarioAndNull': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 空值', 'apiScenario.currentScenarioAndNull': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 空值',
'apiScenario.currentScenarioAndNullAndSourceEnv': '当前步骤参数 > 当前场景参数 > 原环境参数 > 空值', 'apiScenario.currentScenarioAndNullAndSourceEnv': '当前步骤参数 > 当前场景参数 > 原环境参数 > 空值',
@ -177,7 +177,7 @@ export default {
'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv': '原场景参数 > 原环境参数 > 当前步骤参数 > 当前场景参数', 'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv': '原场景参数 > 原环境参数 > 当前步骤参数 > 当前场景参数',
'apiScenario.fullQuoteTip': '完全引用:跟随原步骤内容及步骤状态变化,步骤状态不可调整', 'apiScenario.fullQuoteTip': '完全引用:跟随原步骤内容及步骤状态变化,步骤状态不可调整',
'apiScenario.stepQuoteTip': '步骤引用:仅跟随原步骤内容变化,步骤状态可调整', 'apiScenario.stepQuoteTip': '步骤引用:仅跟随原步骤内容变化,步骤状态可调整',
'apiScenario.sourceScenarioEnvTip': '运行环境,含环境参数', 'apiScenario.sourceScenarioEnvTip': '使用原场景运行环境,含环境参数',
'apiScenario.setSuccess': '设置成功', 'apiScenario.setSuccess': '设置成功',
'apiScenario.pleaseInputUrl': '请输入 url', 'apiScenario.pleaseInputUrl': '请输入 url',
'apiScenario.syncSaveAsCase': '同步添加测试接口用例', 'apiScenario.syncSaveAsCase': '同步添加测试接口用例',

View File

@ -1,23 +1,25 @@
<template> <template>
<a-trigger v-model:popup-visible="innerVisible" trigger="click" @popup-visible-change="handleFilterHidden"> <a-trigger v-model:popup-visible="innerVisible" trigger="click" @popup-visible-change="handleFilterHidden">
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click.stop="innerVisible = true"> <a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" size="small" @click.stop="innerVisible = true">
<div class="font-medium"> <div class="font-medium">
{{ t(props.title) }} {{ t(props.title) }}
</div> </div>
<icon-down :class="innerVisible ? 'text-[rgb(var(--primary-5))]' : ''" /> <icon-down :class="innerVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
</a-button> </a-button>
<template #content> <template #content>
<div class="arco-table-filters-content max-w-[400px]"> <div class="arco-table-filters-content">
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]"> <div class="ml-[6px] flex w-full items-center justify-start overflow-hidden px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="innerStatusFilters" direction="vertical" size="small"> <a-checkbox-group v-model:model-value="innerStatusFilters" direction="vertical" size="small">
<a-checkbox <a-checkbox
v-for="(item, index) of props.list" v-for="(item, index) of props.list"
:key="item[props.valueKey || 'value']" :key="item[props.valueKey || 'value']"
:value="item[props.valueKey || 'value']" :value="item[props.valueKey || 'value']"
> >
<div class="one-line-text max-w-[300px]"> <a-tooltip :content="item[props.labelKey || 'text']" :mouse-enter-delay="300">
<slot name="item" :item="item" :index="index"></slot> <div class="one-line-text">
</div> <slot name="item" :item="item" :index="index"></slot>
</div>
</a-tooltip>
</a-checkbox> </a-checkbox>
</a-checkbox-group> </a-checkbox-group>
</div> </div>
@ -41,12 +43,17 @@
const { t } = useI18n(); const { t } = useI18n();
export interface FilterListItem {
[key: string]: any;
}
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
title: string; title: string;
statusFilters: string[]; statusFilters: string[];
list: any[]; list: FilterListItem[];
valueKey?: string; valueKey?: string;
labelKey?: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{

View File

@ -27,7 +27,7 @@ export default {
'system.config.baseInfo.pageUrlPlaceholder': '请输入当前站点 URL', 'system.config.baseInfo.pageUrlPlaceholder': '请输入当前站点 URL',
'system.config.baseInfo.prometheusSub': '例如:{prometheus}', 'system.config.baseInfo.prometheusSub': '例如:{prometheus}',
'system.config.baseInfo.prometheusRequired': 'prometheus 不能为空', 'system.config.baseInfo.prometheusRequired': 'prometheus 不能为空',
'system.config.baseInfo.prometheusPlaceholder': ' 请输入 prometheus', 'system.config.baseInfo.prometheusPlaceholder': '请输入 prometheus',
'system.config.email.updateTitle': '更新邮件设置', 'system.config.email.updateTitle': '更新邮件设置',
'system.config.email.update': '更新', 'system.config.email.update': '更新',
'system.config.email.hostRequired': 'SMTP 主机不能为空', 'system.config.email.hostRequired': 'SMTP 主机不能为空',