fix(all): 修复bugs

This commit is contained in:
baiqi 2024-04-11 18:41:32 +08:00 committed by 刘瑞斌
parent 617b44780e
commit 526e563270
20 changed files with 258 additions and 195 deletions

View File

@ -6,6 +6,8 @@ import {
GetPluginScriptUrl,
GetProtocolListUrl,
LocalExecuteApiDebugUrl,
StopExecuteUrl,
StopLocalExecuteUrl,
} from '@/api/requrls/api-test/common';
import {
@ -16,6 +18,7 @@ import {
ProtocolItem,
} from '@/models/apiTest/common';
import { EnvConfig, EnvironmentItem } from '@/models/projectManagement/environmental';
import { ScenarioStepType } from '@/enums/apiEnum';
// 获取协议列表
export function getProtocolList(organizationId: string) {
@ -46,3 +49,15 @@ export function getEnvList(projectId: string) {
export function getEnvironment(envId: string) {
return MSR.get<EnvConfig>({ url: GetEnvironmentUrl, params: envId });
}
// 停止本地执行
export function stopLocalExecute(host: string, id: string | number, type?: ScenarioStepType) {
return MSR.post({
url: type ? `${host}${StopLocalExecuteUrl}/${type}/${id}` : `${host}${StopLocalExecuteUrl}/${id}`,
});
}
// 停止执行
export function stopExecute(id: string | number, type?: ScenarioStepType) {
return MSR.get({ url: type ? `${StopExecuteUrl}/${type}/${id}` : `${StopExecuteUrl}/${id}` });
}

View File

@ -4,3 +4,5 @@ export const GetPluginScriptUrl = '/api/test/plugin/script'; // 获取插件配
export const LocalExecuteApiDebugUrl = '/api/debug'; // 本地执行调试
export const GetEnvListUrl = '/api/test/env-list'; // 获取接口测试环境列表
export const GetEnvironmentUrl = '/api/test/environment'; // 获取接口测试环境详情
export const StopExecuteUrl = '/task/center/api/project/stop'; // 停止执行
export const StopLocalExecuteUrl = '/api/stop'; // 停止本地执行

View File

@ -4,7 +4,11 @@
<a-space>
<div class="one-line-text flex max-w-[145px] items-center">
<img :src="props.logo" class="mr-[4px] h-[34px] w-[32px]" />
<div class="font-['Helvetica_Neue'] text-[16px] font-bold text-[rgb(var(--primary-5))]">{{ props.name }}</div>
<a-tooltip :content="props.name">
<div class="one-line-text font-['Helvetica_Neue'] text-[16px] font-bold text-[rgb(var(--primary-5))]">
{{ props.name }}
</div>
</a-tooltip>
</div>
</a-space>
</div>

View File

@ -408,6 +408,7 @@ export interface Scenario {
stepResponses: Record<string | number, Array<RequestResult>>; // 步骤响应集合key 为步骤 idvalue 为步骤响应内容
isExecute?: boolean; // 是否从列表执行进去场景详情
isDebug?: boolean; // 是否调试,区分执行场景和批量调试步骤
executeType?: 'localExec' | 'serverExec'; // 执行类型
}
// 场景详情
export interface ScenarioDetail extends Scenario {

View File

@ -87,8 +87,8 @@
:max-length="255"
:disabled="props.disabled"
size="small"
@press-enter="isShowEditScriptNameInput = false"
@blur="isShowEditScriptNameInput = false"
@press-enter="handleScriptNameInputBlur"
@blur="handleScriptNameInputBlur"
@change="() => emit('change')"
/>
</div>
@ -689,6 +689,13 @@ if (!result){
},
];
function handleScriptNameInputBlur() {
if (condition.value.name === '') {
condition.value.name = t('apiTestDebug.preconditionScriptName');
}
isShowEditScriptNameInput.value = false;
}
const showQuoteDrawer = ref(false);
function saveQuoteScriptHandler(item: any) {
// TODO:any

View File

@ -222,6 +222,7 @@
try {
deleteListItem(item);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
@ -238,4 +239,4 @@
background: white !important;
box-shadow: 0 0 0 1px var(--color-text-n8);
}
</style>
</style>

View File

@ -841,7 +841,7 @@
if (
!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault &&
!props.isTreeTable &&
!filterKeyValParams(arr, arr[arr.length - 2]).lastDataIsDefault //
!filterKeyValParams([arr[arr.length - 2], arr[arr.length - 1]], arr[arr.length - 1]).lastDataIsDefault //
) {
addTableLine(arr.length - 1, false, true);
}

View File

@ -1,19 +1,18 @@
<template>
<a-scrollbar class="overflow-y-auto" :style="{ height: '300px' }">
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<MsFormTable :data="props.requestResult?.responseResult.assertions" :columns="columns" :selectable="false">
<template #status="{ record }">
<MsTag :type="record.pass === true ? 'success' : 'danger'" theme="light">
{{ record.pass === true ? t('common.success') : t('common.fail') }}
</MsTag>
</template>
</MsBaseTable>
</MsFormTable>
</a-scrollbar>
</template>
<script setup lang="ts">
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import MsFormTable from '@/components/pure/ms-form-table/index.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import { useI18n } from '@/hooks/useI18n';
@ -43,22 +42,6 @@
showTooltip: true,
},
];
const { propsRes, propsEvent } = useTable(undefined, {
scroll: { x: '100%' },
columns,
});
watch(
() => props.requestResult?.responseResult.assertions,
(val) => {
if (val) {
propsRes.value.data = props.requestResult?.responseResult.assertions || [];
}
},
{
immediate: true,
}
);
</script>
<style scoped></style>

View File

@ -104,7 +104,7 @@
import TabCaseDependency from '@/views/api-test/management/components/management/case/tabContent/tabCaseDependency.vue';
import TabCaseExecuteHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseExecuteHistory.vue';
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
import { localExecuteApiDebug, stopExecute, stopLocalExecute } from '@/api/modules/api-test/common';
import { debugCase, deleteCase, runCase, toggleFollowCase } from '@/api/modules/api-test/management';
import { getSocket } from '@/api/modules/project-management/commonScript';
import useModal from '@/hooks/useModal';
@ -112,7 +112,7 @@
import { ProtocolItem } from '@/models/apiTest/common';
import { EnvConfig } from '@/models/projectManagement/environmental';
import { RequestMethods } from '@/enums/apiEnum';
import { RequestMethods, ScenarioStepType } from '@/enums/apiEnum';
import { defaultResponse } from '@/views/api-test/components/config';
@ -299,7 +299,17 @@
executeCase.value = false;
}
}
function stopDebug() {
async function stopDebug() {
try {
if (caseDetail.value.frontendDebug) {
await stopLocalExecute(executeRef.value?.localExecuteUrl || '', reportId.value, ScenarioStepType.API_CASE);
} else {
await stopExecute(reportId.value, ScenarioStepType.API_CASE);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
websocket.value?.close();
caseDetail.value.executeLoading = false;
executeCase.value = false;

View File

@ -82,7 +82,7 @@
:scenario-id="scenarioId"
@replace="handleReplace"
/>
<MsButton type="icon" status="secondary" @click="emit('deleteStep')">
<MsButton type="icon" status="secondary" class="mr-4" @click="emit('deleteStep')">
<MsIcon type="icon-icon_delete-trash_outlined" />
{{ t('common.delete') }}
</MsButton>

View File

@ -142,6 +142,7 @@
import { getScenarioDetail } from '@/api/modules/api-test/scenario';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { deleteNodes, filterTree, getGenerateId, mapTree, traverseTree } from '@/utils';
import { countNodes } from '@/utils/tree';
@ -156,6 +157,7 @@
}>();
const { t } = useI18n();
const { openModal } = useModal();
const scenario = defineModel<Scenario>('scenario', {
required: true,
@ -268,12 +270,26 @@
}
function batchDelete() {
deleteNodes(scenario.value.steps, checkedKeys.value, (node) => !node.isQuoteScenarioStep, 'uniqueId');
Message.success(t('common.deleteSuccess'));
if (scenario.value.steps.length === 0) {
checkedAll.value = false;
indeterminate.value = false;
}
openModal({
type: 'error',
title: t('common.tip'),
content: t('apiScenario.deleteStepConfirmWithChildren'),
okText: t('common.confirmDelete'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'danger',
},
maskClosable: false,
onBeforeOk: async () => {
deleteNodes(scenario.value.steps, checkedKeys.value, (node) => !node.isQuoteScenarioStep, 'uniqueId');
Message.success(t('common.deleteSuccess'));
if (scenario.value.steps.length === 0) {
checkedAll.value = false;
indeterminate.value = false;
}
},
hideCancel: false,
});
}
const showScenarioReportVisible = ref(false);

View File

@ -328,7 +328,7 @@
</div>
</a-form-item>
<a-form-item v-show="scenarioConfigForm.useOriginScenarioParam" class="hidden-item">
<a-radio-group v-model:model-value="scenarioConfigForm.useOriginScenarioParamPreferential" type="button">
<a-radio-group v-model:model-value="scenarioConfigForm.useOriginScenarioParamPreferential">
<a-radio :value="true">
<div class="flex items-center gap-[4px]">
{{ t('apiScenario.sourceScenario') }}
@ -530,6 +530,7 @@
import { debugScenario, getScenarioStep } from '@/api/modules/api-test/scenario';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useAppStore from '@/store/modules/app';
import {
deleteNode,
@ -594,6 +595,7 @@
const appStore = useAppStore();
const { t } = useI18n();
const { openModal } = useModal();
const steps = defineModel<ScenarioStepItem[]>('steps', {
required: true,
@ -779,69 +781,45 @@
});
const showScenarioConfig = ref(false);
const scenarioConfigParamTip = computed(() => {
if (
scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv
) {
// 使-
return t('apiScenario.currentScenarioAndNull');
if (!scenarioConfigForm.value.useOriginScenarioParam && !scenarioConfigForm.value.enableScenarioEnv) {
// 使-
return t('apiScenario.notSource');
}
if (
scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv
) {
// 使--
return t('apiScenario.currentScenarioAndNullAndSourceEnv');
if (!scenarioConfigForm.value.useOriginScenarioParam && scenarioConfigForm.value.enableScenarioEnv) {
// 使-
return t('apiScenario.notSourceParamAndSourceEnv');
}
if (
scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv
) {
// 使-
return t('apiScenario.currentScenarioAndSourceScenario');
// 使-使
return t('apiScenario.sourceParamAndSource');
}
if (
scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv
) {
// 使--
return t('apiScenario.currentScenarioAndSourceScenarioAndSourceEnv');
// 使-使-
return t('apiScenario.sourceParamAndSourceEnv');
}
if (
!scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv
) {
// 使-
return t('apiScenario.sourceScenarioAndNull');
// 使-使
return t('apiScenario.currentParamAndSource');
}
if (
!scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useOriginScenarioParam &&
!scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv
) {
// 使--
return t('apiScenario.sourceScenarioAndNullAndSourceEnv');
}
if (
!scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useOriginScenarioParamPreferential &&
!scenarioConfigForm.value.enableScenarioEnv
) {
// 使-
return t('apiScenario.sourceScenarioAndCurrentScenario');
}
if (
!scenarioConfigForm.value.useOriginScenarioParam &&
scenarioConfigForm.value.useOriginScenarioParamPreferential &&
scenarioConfigForm.value.enableScenarioEnv
) {
// 使--
return t('apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv');
// 使-使-
return t('apiScenario.currentParamAndSourceEnv');
}
});
@ -1153,7 +1131,7 @@
})[0]
),
name: `copy_${node.name}`,
copyFromStepId: node.id,
copyFromStepId: stepDetail ? node.id : node.copyFromStepId,
sort: node.sort + 1,
isNew: true,
id,
@ -1174,8 +1152,25 @@
showScenarioConfig.value = true;
break;
case 'delete':
deleteNode(steps.value, node.uniqueId, 'uniqueId');
scenario.value.unSaved = true;
openModal({
type: 'error',
title: t('common.tip'),
content:
node.children && node.children.length > 0
? t('apiScenario.deleteStepConfirmWithChildren')
: t('apiScenario.deleteStepConfirm', { name: node.name }),
okText: t('common.confirmDelete'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'danger',
},
maskClosable: false,
onBeforeOk: async () => {
deleteNode(steps.value, node.uniqueId, 'uniqueId');
scenario.value.unSaved = true;
},
hideCancel: false,
});
break;
case 'saveAsApi':
activeStep.value = node as ScenarioStepItem;
@ -1321,7 +1316,7 @@
// api api api
activeStep.value = step;
if (
(stepDetails.value[step.id] === undefined && step.copyFromStepId && !step.isNew) ||
(stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
(stepDetails.value[step.id] === undefined && !step.isNew)
) {
//
@ -1332,7 +1327,7 @@
activeStep.value = step;
if (
_stepType.isCopyCase &&
((stepDetails.value[step.id] === undefined && step.copyFromStepId && !step.isNew) ||
((stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
(stepDetails.value[step.id] === undefined && !step.isNew))
) {
// case
@ -1358,16 +1353,11 @@
/**
* 开启websocket监听接收执行结果
*/
function debugSocket(
step: ScenarioStepItem,
_scenario: Scenario,
reportId: string | number,
executeType?: 'localExec' | 'serverExec'
) {
function debugSocket(step: ScenarioStepItem, _scenario: Scenario, reportId: string | number) {
websocketMap[reportId] = getSocket(
reportId || '',
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl?.value : ''
scenario.value.executeType === 'localExec' ? '/ws/debug' : '',
scenario.value.executeType === 'localExec' ? localExecuteUrl?.value : ''
);
websocketMap[reportId].addEventListener('message', (event) => {
const data = JSON.parse(event.data);
@ -1396,21 +1386,20 @@
}
async function realExecute(
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId' | 'stepFileParam'>,
executeType?: 'localExec' | 'serverExec'
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId' | 'stepFileParam'>
) {
const [currentStep] = executeParams.steps;
try {
currentStep.isExecuting = true;
currentStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
debugSocket(currentStep, scenario.value, executeParams.reportId, executeType); // websocket
debugSocket(currentStep, scenario.value, executeParams.reportId); // websocket
const res = await debugScenario({
id: scenario.value.id || '',
grouped: false,
environmentId: currentEnvConfig?.value?.id || '',
projectId: appStore.currentProjectId,
scenarioConfig: scenario.value.scenarioConfig,
frontendDebug: executeType === 'localExec',
frontendDebug: scenario.value.executeType === 'localExec',
...executeParams,
steps: mapTree(executeParams.steps, (node) => {
return {
@ -1420,7 +1409,7 @@
};
}),
});
if (executeType === 'localExec' && localExecuteUrl?.value) {
if (scenario.value.executeType === 'localExec' && localExecuteUrl?.value) {
await localExecuteApiDebug(localExecuteUrl.value, res);
}
} catch (error) {
@ -1461,17 +1450,15 @@
return step.enable || step.uniqueId === realStep.uniqueId;
}
);
realExecute(
{
steps: [realStep as ScenarioStepItem],
stepDetails: _stepDetails,
reportId: realStep.reportId,
stepFileParam: {
[realStep.id]: stepFileParam,
},
scenario.value.executeType = isPriorityLocalExec?.value ? 'localExec' : 'serverExec';
realExecute({
steps: [realStep as ScenarioStepItem],
stepDetails: _stepDetails,
reportId: realStep.reportId,
stepFileParam: {
[realStep.id]: stepFileParam,
},
isPriorityLocalExec?.value ? 'localExec' : 'serverExec'
);
});
}
}
@ -1488,19 +1475,17 @@
realStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
const stepFileParam = scenario.value.stepFileParam[realStep.id];
request.executeLoading = true;
realExecute(
{
steps: [realStep as ScenarioStepItem],
stepDetails: {
[realStep.id]: request,
},
reportId: realStep.reportId,
stepFileParam: {
[realStep.uniqueId]: stepFileParam,
},
scenario.value.executeType = executeType;
realExecute({
steps: [realStep as ScenarioStepItem],
stepDetails: {
[realStep.id]: request,
},
executeType
);
reportId: realStep.reportId,
stepFileParam: {
[realStep.uniqueId]: stepFileParam,
},
});
} else {
//
const reportId = getGenerateId();
@ -1519,26 +1504,23 @@
reportId,
uniqueId: request.stepId,
};
realExecute(
{
steps: [activeStep.value],
stepDetails: {
[request.stepId]: request,
},
reportId,
stepFileParam: {
[request.stepId]: {
uploadFileIds: request.uploadFileIds || [],
linkFileIds: request.linkFileIds || [],
},
realExecute({
steps: [activeStep.value],
stepDetails: {
[request.stepId]: request,
},
reportId,
stepFileParam: {
[request.stepId]: {
uploadFileIds: request.uploadFileIds || [],
linkFileIds: request.linkFileIds || [],
},
},
executeType
);
});
}
}
function handleStopExecute(step?: ScenarioStepItem) {
async function handleStopExecute(step?: ScenarioStepItem) {
if (step?.reportId) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
websocketMap[step.reportId].close();
@ -1759,12 +1741,26 @@
*/
function deleteStep(step?: ScenarioStepItem) {
if (step) {
customCaseDrawerVisible.value = false;
customApiDrawerVisible.value = false;
deleteNode(steps.value, step.uniqueId, 'uniqueId');
activeStep.value = undefined;
scenario.value.unSaved = true;
Message.success(t('common.deleteSuccess'));
openModal({
type: 'error',
title: t('common.tip'),
content: t('apiScenario.deleteStepConfirm', { name: step.name }),
okText: t('common.confirmDelete'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'danger',
},
maskClosable: false,
onBeforeOk: async () => {
customCaseDrawerVisible.value = false;
customApiDrawerVisible.value = false;
deleteNode(steps.value, step.uniqueId, 'uniqueId');
activeStep.value = undefined;
scenario.value.unSaved = true;
Message.success(t('common.deleteSuccess'));
},
hideCancel: false,
});
}
}

View File

@ -74,7 +74,11 @@ export default function updateStepStatus(
// 非逻辑控制器直接更改本身状态
if (stepResponses[node.uniqueId] && stepResponses[node.uniqueId].length > 0) {
// 存在多个请求结果说明是循环控制器下的步骤,需要判断其子步骤的执行结果
if (stepResponses[node.uniqueId].some((report) => !report.isSuccessful)) {
if (
stepResponses[node.uniqueId].some(
(report) => !report.isSuccessful && report.status !== ScenarioExecuteStatus.FAKE_ERROR
)
) {
node.executeStatus = ScenarioExecuteStatus.FAILED;
} else if (
stepResponses[node.uniqueId].some((report) => report.status === ScenarioExecuteStatus.FAKE_ERROR)

View File

@ -118,7 +118,7 @@
import executeButton from '@/views/api-test/components/executeButton.vue';
import ScenarioTable from '@/views/api-test/scenario/components/scenarioTable.vue';
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
import { localExecuteApiDebug, stopExecute, stopLocalExecute } from '@/api/modules/api-test/common';
import {
addScenario,
debugScenario,
@ -246,14 +246,14 @@
activeScenarioTab.value.stepResponses = {};
activeScenarioTab.value.reportId = executeParams.reportId; // ID
debugSocket(activeScenarioTab.value, executeType, localExecuteUrl); // websocket
activeScenarioTab.value.isDebug = !isExecute;
let res;
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
//
activeScenarioTab.value.isDebug = false;
res = await executeScenario({
id: activeScenarioTab.value.id,
grouped: false,
environmentId: currentEnvConfig.value?.id || '',
environmentId: activeScenarioTab.value.environmentId || '',
projectId: appStore.currentProjectId,
scenarioConfig: activeScenarioTab.value.scenarioConfig,
...executeParams,
@ -266,11 +266,10 @@
}),
});
} else {
activeScenarioTab.value.isDebug = true;
res = await debugScenario({
id: activeScenarioTab.value.id,
grouped: false,
environmentId: currentEnvConfig.value?.id || '',
environmentId: activeScenarioTab.value.environmentId || '',
projectId: appStore.currentProjectId,
scenarioConfig: activeScenarioTab.value.scenarioConfig,
stepFileParam: activeScenarioTab.value.stepFileParam,
@ -329,7 +328,24 @@
);
}
function handleStopExecute() {
async function handleStopExecute() {
if (!activeScenarioTab.value.isDebug) {
//
try {
if (activeScenarioTab.value.executeType === 'localExec') {
await stopLocalExecute(
executeButtonRef.value?.localExecuteUrl || '',
activeScenarioTab.value.reportId,
ScenarioStepType.API_SCENARIO
);
} else {
await stopExecute(activeScenarioTab.value.reportId, ScenarioStepType.API_SCENARIO);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
websocketMap[activeScenarioTab.value.reportId]?.close();
activeScenarioTab.value.executeLoading = false;
setStepExecuteStatus(activeScenarioTab.value);
@ -479,7 +495,7 @@
};
}),
projectId: appStore.currentProjectId,
environmentId: currentEnvConfig.value?.id || '',
environmentId: activeScenarioTab.value.environmentId || '',
});
const scenarioDetail = await getScenarioDetail(res.id);
scenarioDetail.stepDetails = {};
@ -519,7 +535,7 @@
} else {
await updateScenario({
...activeScenarioTab.value,
environmentId: currentEnvConfig.value?.id || '',
environmentId: activeScenarioTab.value.environmentId || '',
steps: mapTree(activeScenarioTab.value.steps, (node) => {
return {
...node,
@ -554,7 +570,7 @@
if (isLoadedTabIndex > -1 && action !== 'copy') {
// tabtab
activeScenarioTab.value = scenarioTabs.value[isLoadedTabIndex];
// requestCompositionRefid,id
// tabid,id
if (action === 'execute') {
handleExecute(executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec');
}

View File

@ -170,21 +170,17 @@ export default {
'apiScenario.sourceScenarioParams': 'Source Scenario Parameters',
'apiScenario.sourceScenarioEnv': 'Use source Scenario Environment',
'apiScenario.valuePriority': 'Value Priority:',
'apiScenario.currentScenarioAndNull':
'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Empty Value',
'apiScenario.currentScenarioAndNullAndSourceEnv':
'Current Step Parameters > Current Scenario Parameters > Source Environment Parameters > Empty Value',
'apiScenario.currentScenarioAndSourceScenario':
'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Source Scenario Parameters',
'apiScenario.currentScenarioAndSourceScenarioAndSourceEnv':
'Current Step Parameters > Current Scenario Parameters > Source Scenario Parameters > Source Environment Parameters',
'apiScenario.sourceScenarioAndNull': 'Source Scenario Parameters > Empty Value',
'apiScenario.sourceScenarioAndNullAndSourceEnv':
'Source Scenario Parameters > Source Environment Parameters > Empty Value',
'apiScenario.sourceScenarioAndCurrentScenario':
'Source Scenario Parameters > Current Step Parameters > Current Scenario Parameters > Current Environment Parameters',
'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv':
'Source Scenario Parameters > Source Environment Parameters > Current Step Parameters > Current Scenario Parameters',
'apiScenario.notSource': 'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters',
'apiScenario.notSourceParamAndSourceEnv':
'Current Step Parameters > Current Scenario Parameters > Original Environment Parameters',
'apiScenario.currentParamAndSource':
'Current Step Parameters > Current Scenario Parameters > Current Environment Parameters > Original Scenario Parameters',
'apiScenario.currentParamAndSourceEnv':
'Current Step Parameters > Current Scenario Parameters > Original Scenario Parameters > Original Environment Parameters',
'apiScenario.sourceParamAndSource':
'Original Scenario Parameters > Current Step Parameters > Current Scenario Parameters > Current Environment Parameters',
'apiScenario.sourceParamAndSourceEnv':
'Original Scenario Parameters > Original Environment Parameters > Current Step Parameters > Current Scenario Parameters',
'apiScenario.fullQuoteTip':
'Full Quote: Follows the original step content and step status changes. Step status cannot be adjusted.',
'apiScenario.stepQuoteTip':
@ -193,6 +189,9 @@ export default {
'apiScenario.setSuccess': 'Set Successful',
'apiScenario.pleaseInputUrl': 'Please enter URL',
'apiScenario.syncSaveAsCase': 'Synchronously add test interface case',
'apiScenario.deleteStepConfirm': 'Are you sure you want to delete {name}?',
'apiScenario.deleteStepConfirmWithChildren':
'Are you sure you want to delete the selected step and all substeps within the step?',
// Execution History
'apiScenario.executeHistory.searchPlaceholder': 'Search by ID or name',
'apiScenario.executeHistory.num': 'No.',

View File

@ -169,20 +169,20 @@ export default {
'apiScenario.sourceScenarioParams': '使用原场景参数',
'apiScenario.sourceScenarioEnv': '使用原场景环境',
'apiScenario.valuePriority': '取值优先级:',
'apiScenario.currentScenarioAndNull': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 空值',
'apiScenario.currentScenarioAndNullAndSourceEnv': '当前步骤参数 > 当前场景参数 > 原环境参数 > 空值',
'apiScenario.currentScenarioAndSourceScenario': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 原场景参数',
'apiScenario.currentScenarioAndSourceScenarioAndSourceEnv': '当前步骤参数 > 当前场景参数 > 原场景参数> 原环境参数',
'apiScenario.sourceScenarioAndNull': '原场景参数 > 空值',
'apiScenario.sourceScenarioAndNullAndSourceEnv': '原场景参数 > 原环境参数 > 空值',
'apiScenario.sourceScenarioAndCurrentScenario': '原场景参数 > 当前步骤参数 > 当前场景参数 > 当前环境参数',
'apiScenario.sourceScenarioAndCurrentScenarioAndSourceEnv': '原场景参数 > 原环境参数 > 当前步骤参数 > 当前场景参数',
'apiScenario.notSource': '当前步骤参数 > 当前场景参数 > 当前环境参数',
'apiScenario.notSourceParamAndSourceEnv': '当前步骤参数 > 当前场景参数 > 原环境参数',
'apiScenario.currentParamAndSource': '当前步骤参数 > 当前场景参数 > 当前环境参数 > 原场景参数',
'apiScenario.currentParamAndSourceEnv': '当前步骤参数 > 当前场景参数 > 原场景参数 > 原环境参数',
'apiScenario.sourceParamAndSource': '原场景参数 > 当前步骤参数 > 当前场景参数 > 当前环境参数',
'apiScenario.sourceParamAndSourceEnv': '原场景参数 > 原环境参数 > 当前步骤参数 > 当前场景参数',
'apiScenario.fullQuoteTip': '完全引用:跟随原步骤内容及步骤状态变化,步骤状态不可调整',
'apiScenario.stepQuoteTip': '步骤引用:仅跟随原步骤内容变化,步骤状态可调整',
'apiScenario.sourceScenarioEnvTip': '使用原场景运行环境,包含环境参数',
'apiScenario.setSuccess': '设置成功',
'apiScenario.pleaseInputUrl': '请输入 url',
'apiScenario.syncSaveAsCase': '同步添加测试接口用例',
'apiScenario.deleteStepConfirm': '确认删除 {name} 吗?',
'apiScenario.deleteStepConfirmWithChildren': '确认删除所选步骤以及步骤内所有子步骤?',
// 执行历史
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
'apiScenario.executeHistory.num': '序号',

View File

@ -5,14 +5,16 @@
<img :src="innerLogo" class="h-[60px] w-[290px]" />
</div>
<div class="title-0 mt-[16px] flex justify-center">
<span class="title-welcome">{{ t(innerSlogan || '') || t('login.form.title') }}</span>
<span class="title-welcome one-line-text max-w-[300px]">
{{ t(innerSlogan || '') || t('login.form.title') }}
</span>
</div>
</div>
<div class="form mt-[32px] min-w-[416px]">
<div v-if="userInfo.authenticate === 'LOCAL'" class="mb-7 text-[18px] font-medium text-[rgb(var(--primary-5))]">{{
t('login.form.accountLogin')
}}</div>
<div v-if="userInfo.authenticate === 'LOCAL'" class="mb-7 text-[18px] font-medium text-[rgb(var(--primary-5))]">
{{ t('login.form.accountLogin') }}
</div>
<div
v-if="isShowLDAP && userInfo.authenticate !== 'LOCAL'"
class="mb-7 text-[18px] font-medium text-[rgb(var(--primary-5))]"

View File

@ -1,7 +1,7 @@
<template>
<div class="relative">
<!-- 风格主题色配置 -->
<MsCard class="mb-[16px]" :loading="pageloading" simple auto-height>
<MsCard class="mb-[16px]" :loading="pageLoading" simple auto-height>
<div class="config-title">
{{ t('system.config.page.theme') }}
<a-tooltip :content="t('system.config.page.themeTip')" position="tl" class="themeTip">
@ -32,7 +32,7 @@
</div>
</MsCard>
<!-- 登录页配置 -->
<MsCard class="mb-[16px]" :loading="pageloading" simple auto-height>
<MsCard class="mb-[16px]" :loading="pageLoading" simple auto-height>
<div class="config-title">
{{ t('system.config.page.loginPageConfig') }}
</div>
@ -194,6 +194,7 @@
v-model:model-value="pageConfig.slogan"
:placeholder="t('system.config.page.sloganPlaceholder')"
:max-length="255"
:disabled="!hasAnyPermission(['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE'])"
></a-input>
<MsFormItemSub :text="t('system.config.page.sloganTip')" :show-fill-icon="false" />
</a-form-item>
@ -202,6 +203,7 @@
v-model:model-value="pageConfig.title"
:placeholder="t('system.config.page.titlePlaceholder')"
:max-length="255"
:disabled="!hasAnyPermission(['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE'])"
></a-input>
<MsFormItemSub :text="t('system.config.page.titleTip')" :show-fill-icon="false" />
</a-form-item>
@ -212,7 +214,7 @@
</div>
</MsCard>
<!-- 平台主页面配置 -->
<MsCard class="mb-[96px]" :loading="pageloading" simple auto-height>
<MsCard class="mb-[96px]" :loading="pageLoading" simple auto-height>
<div class="config-title">
{{ t('system.config.page.platformConfig') }}
</div>
@ -273,8 +275,8 @@
<MsUpload
v-model:file-list="pageConfig.logoPlatform"
accept="image"
:max-size="1"
size-unit="MB"
:max-size="200"
size-unit="KB"
:auto-upload="false"
>
<a-button
@ -308,6 +310,7 @@
v-model:model-value="pageConfig.platformName"
:placeholder="t('system.config.page.platformNamePlaceholder')"
:max-length="255"
:disabled="!hasAnyPermission(['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE'])"
></a-input>
<MsFormItemSub :text="t('system.config.page.platformNameTip')" :show-fill-icon="false" />
</a-form-item>
@ -316,6 +319,7 @@
v-model:model-value="pageConfig.helpDoc"
:placeholder="t('system.config.page.helpDocPlaceholder')"
:max-length="255"
:disabled="!hasAnyPermission(['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE'])"
></a-input>
<MsFormItemSub :text="t('system.config.page.helpDocTip')" :show-fill-icon="false" />
</a-form-item>
@ -329,9 +333,9 @@
class="fixed bottom-0 right-[16px] z-[999] flex justify-between bg-white p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
:style="{ width: `calc(100% - ${menuWidth + 16}px)` }"
>
<a-button v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']" type="secondary" @click="resetAll">{{
t('system.config.page.resetAll')
}}</a-button>
<a-button v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']" type="secondary" @click="resetAll">
{{ t('system.config.page.resetAll') }}
</a-button>
<a-button v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']" type="primary" @click="beforeSave">
{{ t('system.config.page.save') }}
</a-button>
@ -359,6 +363,7 @@
import useAppStore from '@/store/modules/app';
import { sleep } from '@/utils';
import { scrollIntoView } from '@/utils/dom';
import { hasAnyPermission } from '@/utils/permission';
import { setCustomTheme, setPlatformColor, watchStyle, watchTheme } from '@/utils/theme';
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
@ -373,7 +378,7 @@
const menuWidth = computed(() => {
return appStore.menuCollapse ? collapsedWidth : appStore.menuWidth;
});
const pageloading = ref(false);
const pageLoading = ref(false);
const pageConfig = ref({ ...appStore.pageConfig });
const loginPageFullRef = ref<HTMLElement | null>(null);
const platformPageFullRef = ref<HTMLElement | null>(null);
@ -554,39 +559,41 @@
*/
async function save() {
try {
pageloading.value = true;
pageLoading.value = true;
await savePageConfig(makeParams());
Message.success(t('system.config.page.saveSuccess'));
await sleep(300);
window.location.reload();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
pageloading.value = false;
pageLoading.value = false;
}
}
/**
* 保存前校验
*/
function beforeSave() {
async function beforeSave() {
try {
loginConfigFormRef.value?.validate((errors: Record<string, ValidatedError> | undefined) => {
await loginConfigFormRef.value?.validate((errors: Record<string, ValidatedError> | undefined) => {
if (errors) {
throw new Error('登录页表单校验不通过');
}
});
platformConfigFormRef.value?.validate((errors: Record<string, ValidatedError> | undefined) => {
await platformConfigFormRef.value?.validate((errors: Record<string, ValidatedError> | undefined) => {
if (errors) {
throw new Error('平台页表单校验不通过');
}
});
save();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
const errDom = document.querySelector('.arco-form-item-message');
scrollIntoView(errDom, { block: 'center' });
}
const errDom = document.querySelector('.arco-form-item-message');
scrollIntoView(errDom, { block: 'center' });
}
onBeforeUnmount(() => {

View File

@ -85,7 +85,7 @@ export default {
'system.config.page.platformNamePlaceholder': 'Please enter a platform name',
'system.config.page.platformNameRequired': 'Platform name cannot be empty',
'system.config.page.platformNameTip':
'The general product name of the whole site, the recommended number of words is 8',
'The general product name of the whole site, the recommended number of words is 7',
'system.config.page.helpDoc': 'Help document',
'system.config.page.helpDocPlaceholder': 'Please enter the address of the help document',
'system.config.page.helpDocTip': 'Help document jump link can be set, the default is official help document',

View File

@ -60,7 +60,7 @@ export default {
'system.config.page.replace': '替换图片',
'system.config.page.iconTip':
'顶部网站显示的 icon建议使用 SVG 或 PNG 格式透明背景图片,宽高 18px图片大小仅支持 200 KB 以内',
'system.config.page.loginLogo': '登录 logo',
'system.config.page.loginLogo': '登录 Logo',
'system.config.page.loginLogoTip':
'登录页面右侧 logo建议使用 SVG 或 PNG 格式透明背景图片,高度不小于 48px图片大小仅支持 200 KB 以内',
'system.config.page.loginBg': '登录背景图',
@ -69,7 +69,7 @@ export default {
'system.config.page.slogan': 'Slogan',
'system.config.page.sloganPlaceholder': '请输入 Slogan',
'system.config.page.sloganRequired': 'Slogan不能为空',
'system.config.page.sloganTip': '产品logo下的 slogan',
'system.config.page.sloganTip': '产品Logo下的 slogan',
'system.config.page.title': '网站名称',
'system.config.page.titlePlaceholder': '请输入网站名称',
'system.config.page.titleTip': '显示在网页tab的平台名称',
@ -77,11 +77,11 @@ export default {
'system.config.page.platformConfig': '平台设置',
'system.config.page.platformLogo': '平台 Logo',
'system.config.page.platformLogoTip':
'平台页面顶部显示的 logo建议使用 SVG 或 PNG 格式透明背景图片,高度不小于 32px图片大小仅支持 200 KB 以内',
'平台页面顶部显示的 Logo建议使用 SVG 或 PNG 格式透明背景图片,高度不小于 32px图片大小仅支持 200 KB 以内',
'system.config.page.platformName': '平台名称',
'system.config.page.platformNamePlaceholder': '请输入平台名称',
'system.config.page.platformNameRequired': '平台名称不能为空',
'system.config.page.platformNameTip': '全站通用产品名称,建议字数 8',
'system.config.page.platformNameTip': '全站通用产品名称,建议字数 7',
'system.config.page.helpDoc': '帮助文档',
'system.config.page.helpDocPlaceholder': '请输入帮助文档地址',
'system.config.page.helpDocTip': '可设置帮助文档跳转链接,默认为官方帮助文档',