fix: 修改全局部分bug

This commit is contained in:
xinxin.wu 2024-03-29 19:10:16 +08:00 committed by Craftsman
parent fa1016566f
commit c64b97ba06
30 changed files with 349 additions and 369 deletions

View File

@ -38,6 +38,7 @@
v-model:model-value="record.expression" v-model:model-value="record.expression"
class="ms-params-input" class="ms-params-input"
:max-length="255" :max-length="255"
size="mini"
:disabled="props.disabled" :disabled="props.disabled"
:placeholder="t('apiTestDebug.commonPlaceholder')" :placeholder="t('apiTestDebug.commonPlaceholder')"
@input="() => handleExpressionChange(rowIndex)" @input="() => handleExpressionChange(rowIndex)"
@ -133,6 +134,7 @@
:disabled="props.disabled" :disabled="props.disabled"
class="ms-params-input" class="ms-params-input"
:max-length="255" :max-length="255"
size="mini"
:placeholder="t('apiTestDebug.commonPlaceholder')" :placeholder="t('apiTestDebug.commonPlaceholder')"
@input="() => handleExpressionChange(rowIndex)" @input="() => handleExpressionChange(rowIndex)"
@change="() => handleExpressionChange(rowIndex)" @change="() => handleExpressionChange(rowIndex)"
@ -281,6 +283,7 @@
:disabled="props.disabled" :disabled="props.disabled"
class="ms-params-input" class="ms-params-input"
:max-length="255" :max-length="255"
size="mini"
@input="() => handleExpressionChange(rowIndex)" @input="() => handleExpressionChange(rowIndex)"
@change="() => handleExpressionChange(rowIndex)" @change="() => handleExpressionChange(rowIndex)"
> >
@ -457,7 +460,6 @@
dataIndex: 'condition', dataIndex: 'condition',
slotName: 'condition', slotName: 'condition',
options: statusCodeOptions, options: statusCodeOptions,
width: 120,
}, },
{ {
title: 'ms.assertion.matchValue', title: 'ms.assertion.matchValue',

View File

@ -40,15 +40,13 @@
> >
<div class="ms-assertion-body-left-item-row"> <div class="ms-assertion-body-left-item-row">
<span class="ms-assertion-body-left-item-row-num">{{ index + 1 }}</span> <span class="ms-assertion-body-left-item-row-num">{{ index + 1 }}</span>
<div class="one-text-line">{{ item.name }}</div> <div class="one-line-text" :class="{ 'text-[rgb(var(--primary-5))]': activeKey === item.id }">{{
item.name
}}</div>
</div> </div>
<div class="ms-assertion-body-left-item-switch"> <div class="ms-assertion-body-left-item-switch">
<div v-show="!props.disabled" class="ms-assertion-body-left-item-switch-action"> <div v-show="!props.disabled" class="ms-assertion-body-left-item-switch-action">
<!-- <MsIcon <icon-drag-dot-vertical class="ms-list-drag-icon sort-handle" />
type="icon-icon_drag"
class="action-btn-move sort-handle cursor-move text-[12px] text-[var(--color-text-4)]"
/> -->
<icon-drag-dot-vertical class="ms-list-drag-icon" />
<MsTableMoreAction <MsTableMoreAction
:list="getItemMoreActions(item)" :list="getItemMoreActions(item)"
trigger="click" trigger="click"
@ -267,14 +265,21 @@
case ResponseAssertionType.RESPONSE_HEADER: case ResponseAssertionType.RESPONSE_HEADER:
assertions.value.push({ assertions.value.push({
...tmpObj, ...tmpObj,
assertions: [], assertions: [
{
header: '',
condition: EQUAL.value,
expectedValue: '',
enable: true,
},
],
}); });
break; break;
// //
case ResponseAssertionType.RESPONSE_CODE: case ResponseAssertionType.RESPONSE_CODE:
assertions.value.push({ assertions.value.push({
...tmpObj, ...tmpObj,
condition: 'EQUALS', condition: EQUAL.value,
expectedValue: '200', expectedValue: '200',
}); });
break; break;

View File

@ -82,16 +82,6 @@
innerKeyword.value = inputVal; innerKeyword.value = inputVal;
}, 300); }, 300);
watch(
() => props.modelValue,
(val) => {
if (val) {
selectValue.value = val as any;
}
},
{ immediate: true }
);
watch( watch(
() => props.keyword, () => props.keyword,
(val) => { (val) => {
@ -116,6 +106,9 @@
if (props.options) { if (props.options) {
optionsList.value = props.options; optionsList.value = props.options;
} }
if (props.modelValue) {
selectValue.value = props.modelValue;
}
}); });
onMounted(() => { onMounted(() => {

View File

@ -1,4 +1,4 @@
import { ExecuteConditionProcessor } from '../apiTest/common'; import { EnableKeyValueParam, ExecuteConditionProcessor } from '@/models/apiTest/common';
export interface EnvListItem { export interface EnvListItem {
mock?: boolean; mock?: boolean;
@ -140,7 +140,7 @@ export interface HttpForm {
description?: string; description?: string;
hostname: string; hostname: string;
type: string; type: string;
headers: Record<string, any>[]; headers: EnableKeyValueParam[];
// pathMatchRule: { // pathMatchRule: {
path: string; path: string;
condition: string; condition: string;

View File

@ -113,6 +113,12 @@
return data.value.filter((item: any) => item.processorType === RequestConditionProcessor.EXTRACT).length > 0; return data.value.filter((item: any) => item.processorType === RequestConditionProcessor.EXTRACT).length > 0;
}); });
const hasSql = computed(
() =>
data.value.filter((item: any) => item.processorType === RequestConditionProcessor.SQL).length > 0 &&
props.showPrePostRequest
);
const itemMoreActions: ActionsItem[] = [ const itemMoreActions: ActionsItem[] = [
{ {
label: 'common.copy', label: 'common.copy',
@ -131,7 +137,7 @@
watchEffect(() => { watchEffect(() => {
activeItem.value = data.value.find((item) => item.id === props.activeId) || data.value[0] || {}; activeItem.value = data.value.find((item) => item.id === props.activeId) || data.value[0] || {};
emit('activeChange', activeItem.value); emit('activeChange', activeItem.value);
if (hasPreAndPost.value || hasEXTRACT.value) { if (hasPreAndPost.value || hasEXTRACT.value || hasSql.value) {
moreActions = itemMoreActions.slice(-1); moreActions = itemMoreActions.slice(-1);
} else { } else {
moreActions = itemMoreActions; moreActions = itemMoreActions;

View File

@ -61,7 +61,16 @@
</div> </div>
</template> </template>
</a-popover> </a-popover>
<span v-if="props.showType && props.showType !== 'CASE'">{{ props.environmentName }}</span> <a-popover position="left" content-class="response-popover-content">
<div v-if="props.showType && props.showType !== 'CASE'" class="one-line-text max-w-[150px]">{{
props.environmentName
}}</div>
<template #content>
<div v-if="props.showType && props.showType !== 'CASE'" class="one-line-text">{{
props.environmentName
}}</div>
</template>
</a-popover>
</div> </div>
</div> </div>
<div v-if="activeType === 'SubRequest'" class="my-4 flex justify-start"> <div v-if="activeType === 'SubRequest'" class="my-4 flex justify-start">
@ -112,7 +121,7 @@
import { reportCaseStepDetail, reportStepDetail } from '@/api/modules/api-test/report'; import { reportCaseStepDetail, reportStepDetail } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { findNodeByKey } from '@/utils'; import { findNodeByKey, formatDuration } from '@/utils';
import type { ReportStepDetail, ReportStepDetailItem, ScenarioItemType } from '@/models/apiTest/report'; import type { ReportStepDetail, ReportStepDetailItem, ScenarioItemType } from '@/models/apiTest/report';
import { ResponseComposition, ScenarioStepType } from '@/enums/apiEnum'; import { ResponseComposition, ScenarioStepType } from '@/enums/apiEnum';

View File

@ -421,7 +421,7 @@
dataIndex: 'tags', dataIndex: 'tags',
isTag: true, isTag: true,
isStringTag: true, isStringTag: true,
width: 150, width: 400,
showDrag: true, showDrag: true,
}, },
{ {

View File

@ -482,6 +482,7 @@
isTag: true, isTag: true,
isStringTag: true, isStringTag: true,
showDrag: true, showDrag: true,
width: 400,
}, },
{ {
title: 'case.lastReportStatus', title: 'case.lastReportStatus',

View File

@ -1,74 +1,14 @@
<template> <template>
<div class="report-container h-full"> <div class="report-container h-full">
<!-- 报告参数开始 --> <!-- 报告参数开始 -->
<div class="report-header flex items-center justify-between"> <ReportDetailHeader :detail="detail" show-type="CASE" />
<span>
<span v-if="route.query.shareId" class="font-medium"
>报告名称 <span>{{ detail.name }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider
></span>
<a-popover position="left" content-class="response-popover-content">
<span> {{ detail.environmentName || t('report.detail.api.defaultEnv') }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.executeEnv') }}</div>
<span class="mx-1"> {{ detail.environmentName || t('report.detail.api.caseSaveEnv') }}</span>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<span> {{ detail.poolName || '-' }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('project.taskCenter.resourcePool') }}</div>
<span class="mx-1"> {{ detail.poolName || '-' }}</span>
</div>
</template>
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="detail.runMode">
{{ detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</span
>
<a-divider v-if="detail.runMode" direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.runMode') }}</div>
<div class="mx-1 mt-1">
{{ detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</div
>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<span> {{ detail.creatUserName || '-' }}</span>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.reportCreator') }}</div>
<div class="mt-1"> {{ detail.creatUserName || '-' }}</div>
</div>
</template>
</a-popover>
</span>
<span>
<span class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionTime') }}</span>
{{ detail.startTime ? dayjs(detail.startTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
<span class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionTimeTo') }}</span>
{{ detail.endTime ? dayjs(detail.endTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
</span>
</div>
<!-- 报告参数结束 --> <!-- 报告参数结束 -->
<!-- 报告分析开始 --> <!-- 报告分析开始 -->
<div class="analyze mb-1"> <div class="analyze mb-1">
<!-- 请求分析 --> <!-- 请求分析 -->
<div class="request-analyze min-h-[110px]"> <div class="request-analyze min-h-[110px]">
<div class="block-title mb-4">{{ t('report.detail.api.requestAnalysis') }}</div> <div class="block-title mb-4">{{ t('report.detail.api.requestAnalysis') }}</div>
<!-- 独立报告 -->
<SetReportChart :legend-data="legendData" :options="charOptions" :request-total="getIndicators(detail.total)" /> <SetReportChart :legend-data="legendData" :options="charOptions" :request-total="getIndicators(detail.total)" />
<!-- 集合报告 -->
<!-- </div> -->
</div> </div>
<!-- 耗时分析 --> <!-- 耗时分析 -->
<div class="time-analyze"> <div class="time-analyze">
@ -76,15 +16,17 @@
<div class="time-card-item flex h-full"> <div class="time-card-item flex h-full">
<MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" /> <MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
<span class="time-card-item-title">{{ t('report.detail.api.totalTime') }}</span> <span class="time-card-item-title">{{ t('report.detail.api.totalTime') }}</span>
<span class="count">{{ getTotalTime.split('-')[0] || '-' }}</span <span class="count">{{ getTotalTime.split('-')[0] || 0 }}</span
><span class="time-card-item-title">{{ getTotalTime.split('-')[1] || 'ms' }}</span> ><span class="time-card-item-title">{{ getTotalTime.split('-')[1] || 'ms' }}</span>
</div> </div>
<div class="time-card-item h-full"> <div class="time-card-item h-full">
<MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" /> <MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
<span class="time-card-item-title"> {{ t('report.detail.api.requestTotalTime') }}</span> <span class="time-card-item-title"> {{ t('report.detail.api.requestTotalTime') }}</span>
<span class="count">{{ formatDuration(detail.requestDuration).split('-')[0] || '-' }}</span <span class="count">{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '-'
}}</span
><span class="time-card-item-title">{{ ><span class="time-card-item-title">{{
formatDuration(detail.requestDuration).split('-')[1] || 'ms' detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms'
}}</span> }}</span>
</div> </div>
</div> </div>
@ -132,7 +74,7 @@
<!-- 报告步骤分析结束 --> <!-- 报告步骤分析结束 -->
<!-- 报告明细开始 --> <!-- 报告明细开始 -->
<div class="report-info"> <div class="report-info">
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" /> <reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" show-type="CASE" />
<TiledList <TiledList
:key-words="cascaderKeywords" :key-words="cascaderKeywords"
show-type="CASE" show-type="CASE"
@ -147,9 +89,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import dayjs from 'dayjs'; import { cloneDeep } from 'lodash-es';
import SetReportChart from './case/setReportChart.vue'; import SetReportChart from './case/setReportChart.vue';
import ReportDetailHeader from './reportDetailHeader.vue';
import reportInfoHeader from './step/reportInfoHeaders.vue'; import reportInfoHeader from './step/reportInfoHeaders.vue';
import TiledList from './tiledList.vue'; import TiledList from './tiledList.vue';
@ -329,7 +272,12 @@
rateKey: 'requestPendingRate', rateKey: 'requestPendingRate',
}, },
]; ];
const validArr = props?.detailInfo?.integrated ? tempArr : tempArr.slice(0, 1); let validArr;
if (props?.detailInfo?.integrated) {
validArr = cloneDeep(tempArr);
} else {
validArr = props?.detailInfo?.status === 'SUCCESS' ? [tempArr[0]] : [tempArr[2]];
}
charOptions.value.series.data = validArr.map((item: any) => { charOptions.value.series.data = validArr.map((item: any) => {
return { return {
value: detail.value[item.value] || 0, value: detail.value[item.value] || 0,

View File

@ -0,0 +1,102 @@
<template>
<div class="report-header flex items-center justify-between">
<div class="flex items-center">
<div v-if="route.query.shareId" class="font-medium"
>{{ t('report.name') }}
<div class="one-line-text max-w-[150px]">{{ props.detail.name }}</div>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider
></div>
<a-popover position="left" content-class="response-popover-content">
<div class="one-line-text max-w-[150px]"> {{ props.detail.environmentName || '-' }}</div>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.executeEnv') }}</div>
<div class="mx-1"> {{ props.detail.environmentName || '-' }}</div>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<div class="one-line-text max-w-[150px]"> {{ props.detail.poolName || '-' }}</div>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('project.taskCenter.resourcePool') }}</div>
<span class="mx-1"> {{ props.detail.poolName || '-' }}</span>
</div>
</template>
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="!props.detail.integrated && props.showType === 'API'">
{{ props.detail.waitingTime ? formatDuration(props.detail.waitingTime).split('-')[0] : '-' }}
<span>{{ props.detail.waitingTime ? formatDuration(props.detail.waitingTime).split('-')[1] : 'ms' }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
</span>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.globalWaitingTime') }}</div>
{{ props.detail.waitingTime ? formatDuration(props.detail.waitingTime).split('-')[0] : '-' }}
<span class="mx-1">{{
props.detail.waitingTime ? formatDuration(props.detail.waitingTime).split('-')[1] : 'ms'
}}</span>
</div>
</template>
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="showRunMode">
{{ props.detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</span
>
<a-divider v-if="showRunMode" direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.runMode') }}</div>
<div class="mt-1">
{{ props.detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</div
>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<div class="one-line-text max-w-[150px]"> {{ props.detail.creatUserName || '-' }}</div>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.reportCreator') }}</div>
<div class="mx-1 mt-1"> {{ props.detail.creatUserName || '-' }}</div>
</div>
</template>
</a-popover>
</div>
<div>
<span class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionTime') }}</span>
{{ props.detail.startTime ? dayjs(props.detail.startTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
<span class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionTimeTo') }}</span>
{{ props.detail.endTime ? dayjs(props.detail.endTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import dayjs from 'dayjs';
import { useI18n } from '@/hooks/useI18n';
import { formatDuration } from '@/utils';
import type { ReportDetail } from '@/models/apiTest/report';
const { t } = useI18n();
const route = useRoute();
const props = defineProps<{
detail: ReportDetail;
showType: 'API' | 'CASE';
}>();
const showRunMode = computed(() => {
return props.showType === 'API' ? props.detail.runMode : props.detail.runMode && props.detail.integrated;
});
</script>
<style scoped></style>

View File

@ -27,7 +27,7 @@
<template #name="{ record, rowIndex }"> <template #name="{ record, rowIndex }">
<div <div
type="text" type="text"
class="one-text-line flex w-full text-[rgb(var(--primary-5))]" class="one-line-text flex w-full text-[rgb(var(--primary-5))]"
@click="showReportDetail(record.id, rowIndex, record.integrated)" @click="showReportDetail(record.id, rowIndex, record.integrated)"
>{{ characterLimit(record.name) }}</div >{{ characterLimit(record.name) }}</div
> >
@ -87,8 +87,12 @@
</a-button> </a-button>
<template #content> <template #content>
<div class="arco-table-filters-content"> <div class="arco-table-filters-content">
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]"> <div class="flex items-center justify-center px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="statusListFilters" direction="vertical" size="small"> <a-checkbox-group
v-model:model-value="statusListFiltersMap[showType]"
direction="vertical"
size="small"
>
<a-checkbox v-for="key of statusFilters" :key="key" :value="key"> <a-checkbox v-for="key of statusFilters" :key="key" :value="key">
<ExecutionStatus :module-type="props.moduleType" :status="key" /> <ExecutionStatus :module-type="props.moduleType" :status="key" />
</a-checkbox> </a-checkbox>
@ -153,6 +157,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
@ -190,7 +195,6 @@
const statusFilterVisible = ref(false); const statusFilterVisible = ref(false);
const triggerModeFilterVisible = ref(false); const triggerModeFilterVisible = ref(false);
const statusListFilters = ref<string[]>([]);
const triggerModeListFilters = ref<string[]>([]); const triggerModeListFilters = ref<string[]>([]);
type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED'; type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED';
@ -251,8 +255,9 @@
slotName: 'createUserName', slotName: 'createUserName',
dataIndex: 'createUserName', dataIndex: 'createUserName',
showInTable: true, showInTable: true,
width: 200, width: 300,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'report.operating', title: 'report.operating',
@ -303,6 +308,15 @@
}), }),
rename rename
); );
//
const allListFilters = ref<string[]>([]);
const independentListFilters = ref<string[]>([]);
const integratedListFilters = ref<string[]>([]);
const statusListFiltersMap = ref<Record<string, string[]>>({
all: allListFilters.value,
INDEPENDENT: independentListFilters.value,
INTEGRATED: integratedListFilters.value,
});
function initData() { function initData() {
setLoadListParams({ setLoadListParams({
@ -310,7 +324,7 @@
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
moduleType: props.moduleType, moduleType: props.moduleType,
filter: { filter: {
status: statusListFilters.value, status: statusListFiltersMap.value[showType.value],
integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()), integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()),
triggerMode: triggerModeListFilters.value, triggerMode: triggerModeListFilters.value,
}, },
@ -342,7 +356,7 @@
selectIds: params?.selectedIds || [], selectIds: params?.selectedIds || [],
condition: { condition: {
filter: { filter: {
status: statusListFilters.value, status: statusListFiltersMap.value[showType.value],
integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()), integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()),
triggerMode: triggerModeListFilters.value, triggerMode: triggerModeListFilters.value,
}, },
@ -429,7 +443,7 @@
function resetStatusFilter() { function resetStatusFilter() {
statusFilterVisible.value = false; statusFilterVisible.value = false;
statusListFilters.value = []; statusListFiltersMap.value[showType.value] = [];
initData(); initData();
} }

View File

@ -1,78 +1,7 @@
<template> <template>
<div class="report-container h-full"> <div class="report-container h-full">
<!-- 报告参数开始 --> <!-- 报告参数开始 -->
<div class="report-header flex items-center justify-between"> <ReportDetailHeader :detail="detail" show-type="API" />
<!-- TODO 虚拟数据替换接口后边 -->
<span>
<a-popover position="left" content-class="response-popover-content">
<span> {{ detail.environmentName || t('report.detail.api.defaultEnv') }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.executeEnv') }}</div>
<span class="mx-1"> {{ detail.environmentName || t('report.detail.api.scenarioSavedEnv') }}</span>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<span> {{ detail.poolName || '-' }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('project.taskCenter.resourcePool') }}</div>
<span class="mx-1"> {{ detail.poolName || '-' }}</span>
</div>
</template>
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="!detail.integrated">
{{ detail.waitingTime ? formatDuration(detail.waitingTime).split('-')[0] : '-' }}
<span>{{ detail.waitingTime ? formatDuration(detail.waitingTime).split('-')[1] : 'ms' }}</span>
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
</span>
<template #content>
<div class="flex items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.globalWaitingTime') }}</div>
{{ detail.waitingTime ? formatDuration(detail.waitingTime).split('-')[0] : '-' }}
<span class="mx-1">{{
detail.waitingTime ? formatDuration(detail.waitingTime).split('-')[1] : 'ms'
}}</span>
</div>
</template>
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="detail.runMode">
{{ detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</span
>
<a-divider v-if="detail.runMode" direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.runMode') }}</div>
<div class="mt-1">
{{ detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</div
>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<span> {{ detail.creatUserName || '-' }}</span>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.reportCreator') }}</div>
<div class="mx-1 mt-1"> {{ detail.creatUserName || '-' }}</div>
</div>
</template>
</a-popover>
</span>
<span>
<span class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionTime') }}</span>
{{ detail.startTime ? dayjs(detail.startTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
<span class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionTimeTo') }}</span>
{{ detail.endTime ? dayjs(detail.endTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
</span>
</div>
<!-- 报告参数结束 --> <!-- 报告参数结束 -->
<!-- 报告步骤分析和请求分析开始 --> <!-- 报告步骤分析和请求分析开始 -->
<div class="analyze mb-1"> <div class="analyze mb-1">
@ -128,10 +57,10 @@
</div> </div>
<div> <div>
<span class="ml-4 text-[18px] font-medium">{{ <span class="ml-4 text-[18px] font-medium">{{
formatDuration(detail.requestDuration).split('-')[0] || '-' detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '-'
}}</span> }}</span>
<span class="ml-1 text-[var(--color-text-4)]">{{ <span class="ml-1 text-[var(--color-text-4)]">{{
formatDuration(detail.requestDuration).split('-')[1] || 'ms' detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms'
}}</span> }}</span>
</div> </div>
</div> </div>
@ -188,7 +117,7 @@
<!-- 报告步骤分析和请求分析结束 --> <!-- 报告步骤分析和请求分析结束 -->
<!-- 报告明细开始 --> <!-- 报告明细开始 -->
<div class="report-info"> <div class="report-info">
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" /> <reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" show-type="API" />
<TiledList :key-words="cascaderKeywords" show-type="API" :active-type="activeTab" :report-detail="detail || []" /> <TiledList :key-words="cascaderKeywords" show-type="API" :active-type="activeTab" :report-detail="detail || []" />
</div> </div>
<!-- 报告明细结束 --> <!-- 报告明细结束 -->
@ -197,9 +126,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { useRoute } from 'vue-router';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import MsChart from '@/components/pure/chart/index.vue'; import MsChart from '@/components/pure/chart/index.vue';
import ReportDetailHeader from './reportDetailHeader.vue';
import reportInfoHeader from './step/reportInfoHeaders.vue'; import reportInfoHeader from './step/reportInfoHeaders.vue';
import StepProgress from './stepProgress.vue'; import StepProgress from './stepProgress.vue';
import TiledList from './tiledList.vue'; import TiledList from './tiledList.vue';
@ -211,6 +142,8 @@
import { getIndicators } from '../utils'; import { getIndicators } from '../utils';
const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
detailInfo?: ReportDetail; detailInfo?: ReportDetail;

View File

@ -15,7 +15,7 @@
option-size="small" option-size="small"
class="w-full" class="w-full"
:multiple="false" :multiple="false"
:options="cascaderOptions || []" :options="filterOptions || []"
:virtual-list-props="{ height: 200 }" :virtual-list-props="{ height: 200 }"
:placeholder="t('report.detail.api.filterPlaceholder')" :placeholder="t('report.detail.api.filterPlaceholder')"
> >
@ -49,6 +49,7 @@
const props = defineProps<{ const props = defineProps<{
activeTab: 'tiled' | 'tab'; activeTab: 'tiled' | 'tab';
keyword: string; keyword: string;
showType: 'API' | 'CASE';
}>(); }>();
const emit = defineEmits(['update:activeTab', 'update:keyword']); const emit = defineEmits(['update:activeTab', 'update:keyword']);
@ -101,6 +102,9 @@
children: createChildOption(ScenarioStepType.CUSTOM_REQUEST), children: createChildOption(ScenarioStepType.CUSTOM_REQUEST),
}, },
]); ]);
const filterOptions = computed(() =>
props.showType === 'API' ? cascaderOptions.value : cascaderOptions.value.slice(-1)
);
</script> </script>
<style scoped lang="less"></style> <style scoped lang="less"></style>

View File

@ -39,6 +39,7 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import type { ScenarioItemType } from '@/models/apiTest/report'; import type { ScenarioItemType } from '@/models/apiTest/report';
import { ScenarioStepType } from '@/enums/apiEnum';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
@ -65,12 +66,12 @@
}); });
const showCondition = ref<string[]>([ const showCondition = ref<string[]>([
'API', ScenarioStepType.API,
'API_CASE', ScenarioStepType.API_CASE,
' CUSTOM_REQUEST', ScenarioStepType.CUSTOM_REQUEST,
' LOOP_CONTROLLER', ScenarioStepType.LOOP_CONTROLLER,
'IF_CONTROLLER', ScenarioStepType.IF_CONTROLLER,
'ONCE_ONLY_CONTROLLER', ScenarioStepType.ONCE_ONLY_CONTROLLER,
]); ]);
</script> </script>

View File

@ -71,7 +71,7 @@
</a-tooltip> </a-tooltip>
</div> </div>
<div class="flex"> <div class="flex">
<stepStatus v-if="step.status" :status="step.status" /> <stepStatus :status="step.status || 'PENDING'" />
<!-- 脚本报错 --> <!-- 脚本报错 -->
<a-popover position="left" content-class="response-popover-content"> <a-popover position="left" content-class="response-popover-content">
<MsTag <MsTag
@ -94,9 +94,13 @@
</a-popover> </a-popover>
<div v-show="showStatus(step)" class="flex"> <div v-show="showStatus(step)" class="flex">
<span class="statusCode mx-2"> <span class="statusCode mx-2">
<div class="mr-2"> {{ t('report.detail.api.statusCode') }}</div> <div v-if="step.code" class="mr-2"> {{ t('report.detail.api.statusCode') }}</div>
<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(step.code) }"> <div
v-if="step.code"
class="one-line-text max-w-[200px]"
:style="{ color: statusCodeColor(step.code) }"
>
{{ step.code || '-' }} {{ step.code || '-' }}
</div> </div>
<template #content> <template #content>
@ -109,25 +113,39 @@
</template> </template>
</a-popover> </a-popover>
</span> </span>
<span class="resTime">
<span v-if="step.requestTime !== null" class="resTime">
{{ t('report.detail.api.responseTime') }} {{ t('report.detail.api.responseTime') }}
<span class="resTimeCount ml-2" <a-popover position="left" content-class="response-popover-content">
>{{ step.requestTime ? formatDuration(step.requestTime).split('-')[0] : '-' <span class="resTimeCount ml-2"
}}{{ step.requestTime ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}</span >{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
></span }}{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}</span
>
<a-popover position="left" content-class="response-popover-content">
<span class="resSize">
{{ t('report.detail.api.responseSize') }}
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
>
<template #content>
<span class="resSize">
{{ t('report.detail.api.responseSize') }}
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
> >
</template> <template #content>
</a-popover> <span v-if="step.requestTime !== null" class="resTime">
{{ t('report.detail.api.responseTime') }}
<span class="resTimeCount ml-2"
>{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
}}{{
step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms'
}}</span
></span
>
</template>
</a-popover></span
>
<span v-if="step.responseSize !== null" class="resSize">
{{ t('report.detail.api.responseSize') }}
<a-popover position="left" content-class="response-popover-content">
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span>
<template #content>
<span class="resSize">
{{ t('report.detail.api.responseSize') }}
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
>
</template>
</a-popover></span
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -51,13 +51,13 @@
<div class="ml-[48px] mt-[8px] text-[var(--color-text-4)]"> <div class="ml-[48px] mt-[8px] text-[var(--color-text-4)]">
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }} {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300"> <a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
<span v-if="item.deleted" class="one-text-line ml-[16px] max-w-[300px] break-words break-all"> <span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
{{ characterLimit(item.reviewName) }} {{ characterLimit(item.reviewName) }}
</span> </span>
<span <span
v-else v-else
class="one-text-line ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]" class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
@click="review(item)" @click="review(item)"
> >
{{ characterLimit(item.reviewName) }} {{ characterLimit(item.reviewName) }}

View File

@ -3,7 +3,7 @@
<template #demandName="{ record }"> <template #demandName="{ record }">
<span :class="[props.highlightName ? 'text-[rgb(var(--primary-5))]' : '']" @click="emit('open', record)"> <span :class="[props.highlightName ? 'text-[rgb(var(--primary-5))]' : '']" @click="emit('open', record)">
{{ characterLimit(record.demandName) }} </span {{ characterLimit(record.demandName) }} </span
><span class="one-text-line text-[rgb(var(--primary-5))]">({{ (record.children || []).length || 0 }})</span> ><span class="one-line-text text-[rgb(var(--primary-5))]">({{ (record.children || []).length || 0 }})</span>
</template> </template>
<template #operation="{ record }"> <template #operation="{ record }">
<MsButton <MsButton

View File

@ -30,7 +30,6 @@
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore'; import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { EnvConfigItem } from '@/models/projectManagement/environmental'; import { EnvConfigItem } from '@/models/projectManagement/environmental';
import { TableKeyEnum } from '@/enums/tableEnum';
const projectEnvStore = useProjectEnvStore(); const projectEnvStore = useProjectEnvStore();
const appStore = useAppStore(); const appStore = useAppStore();

View File

@ -3,47 +3,30 @@
ref="popoverRef" ref="popoverRef"
:popup-visible="currentVisible" :popup-visible="currentVisible"
position="bottom" position="bottom"
trigger="click"
class="ms-pop-confirm--hidden-icon" class="ms-pop-confirm--hidden-icon"
:content-class="props.id ? 'move-left' : ''"
:ok-loading="loading" :ok-loading="loading"
:on-before-ok="handleBeforeOk"
:cancel-button-props="{ disabled: loading }" :cancel-button-props="{ disabled: loading }"
@popup-visible-change="reset" @popup-visible-change="reset"
> >
<template #content> <template #content>
<div class="mb-[1px] text-[14px] font-medium text-[var(--color-text-1)]">{{ <div class="mb-[8px] text-[14px] font-medium text-[var(--color-text-1)]">{{
props.id ? t('system.userGroup.rename') : t('system.userGroup.createUserGroup') props.id ? t('system.userGroup.rename') : t('system.userGroup.createUserGroup')
}}</div> }}</div>
<div v-outer="handleOutsideClick"> <div v-outer="handleOutsideClick">
<div class="form"> <a-form ref="formRef" :model="form" layout="vertical">
<a-form ref="formRef" :model="form" layout="vertical"> <a-form-item class="hidden-item" field="name" :rules="[{ validator: validateName }]">
<a-form-item field="name" :rules="[{ validator: validateName }]"> <a-input
<a-input v-model="form.name"
v-model="form.name" class="w-[245px]"
class="w-[245px]" :placeholder="t('system.userGroup.pleaseInputUserGroupName')"
:placeholder="t('system.userGroup.pleaseInputUserGroupName')" allow-clear
allow-clear :max-length="255"
:max-length="255" @press-enter="handleBeforeOk(undefined)"
@press-enter="handleBeforeOk" @keyup.esc="handleCancel"
@keyup.esc="handleCancel" />
/> </a-form-item>
</a-form-item> </a-form>
</a-form>
</div>
<!-- <div class="mb-1 mt-4 flex flex-row flex-nowrap justify-end gap-2">
<a-button type="secondary" size="mini" :disabled="loading" @click="handleCancel">
{{ t('common.cancel') }}
</a-button>
<a-button
type="primary"
size="mini"
:loading="loading"
:disabled="form.name.length === 0"
@click="handleBeforeOk"
>
{{ t('common.confirm') }}
</a-button>
</div> -->
</div> </div>
</template> </template>
<slot></slot> <slot></slot>
@ -118,43 +101,43 @@
emit('cancel', false); emit('cancel', false);
}; };
const handleBeforeOk = () => { const handleBeforeOk = (done?: (closed: boolean) => void) => {
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => { formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (errors) { if (!errors) {
return false; try {
} loading.value = true;
// let res: EnvListItem;
try {
loading.value = true;
if (props.type === EnvAuthScopeEnum.PROJECT) { if (props.type === EnvAuthScopeEnum.PROJECT) {
await updateOrAddEnv({ fileList: [], request: { ...store.currentEnvDetailInfo, name: form.name } }); await updateOrAddEnv({ fileList: [], request: { ...store.currentEnvDetailInfo, name: form.name } });
} else { } else {
const id = store.currentGroupId === NEW_ENV_GROUP ? undefined : store.currentGroupId; const id = store.currentGroupId === NEW_ENV_GROUP ? undefined : store.currentGroupId;
if (id) { if (id) {
const detail: Record<string, any> = await getGroupDetailEnv(id); const detail: Record<string, any> = await getGroupDetailEnv(id);
const envGroupProject = detail?.environmentGroupInfo.filter( const envGroupProject = detail?.environmentGroupInfo.filter(
(item: any) => item.projectId && item.environmentId (item: any) => item.projectId && item.environmentId
); );
const params = { const params = {
id, id,
name: form.name, name: form.name,
description: detail.description, description: detail.description,
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
envGroupProject, envGroupProject,
}; };
await groupUpdateEnv(params); await groupUpdateEnv(params);
}
} }
Message.success(t('project.fileManagement.renameSuccess'));
emit('success');
handleCancel();
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
} finally {
loading.value = false;
} }
Message.success(t('project.fileManagement.renameSuccess')); } else if (done) {
emit('success'); done(false);
handleCancel();
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
} finally {
loading.value = false;
} }
}); });
}; };

View File

@ -7,6 +7,7 @@
v-model="keyword" v-model="keyword"
class="w-[240px]" class="w-[240px]"
allow-clear allow-clear
:placeholder="t('project.menu.nameSearch')"
@press-enter="fetchData" @press-enter="fetchData"
@search="fetchData" @search="fetchData"
></a-input-search> ></a-input-search>

View File

@ -74,6 +74,17 @@
/> />
</div> </div>
</template> </template>
<template #empty>
<div class="flex w-full items-center justify-center text-[var(--color-text-4)]">
<span v-if="hasAnyPermission(['PROJECT_ENVIRONMENT:READ+UPDATE'])">{{
t('caseManagement.caseReview.tableNoData')
}}</span>
<span v-else>{{ t('caseManagement.featureCase.tableNoData') }}</span>
<MsButton v-permission="['PROJECT_ENVIRONMENT:READ+UPDATE']" class="ml-[8px]">
{{ t('project.environmental.addHttp') }}
</MsButton>
</div>
</template>
</MsBaseTable> </MsBaseTable>
<AddHttpDrawer <AddHttpDrawer
v-model:visible="addVisible" v-model:visible="addVisible"

View File

@ -94,8 +94,10 @@
<a-form-item <a-form-item
v-if="form.type === 'MODULE'" v-if="form.type === 'MODULE'"
class="mb-[16px]" class="mb-[16px]"
field="description" field="moduleId"
:label="t('project.environmental.http.selectApiModule')" :label="t('project.environmental.http.selectApiModule')"
:rules="[{ required: true, message: t('project.environmental.http.selectModule') }]"
asterisk-position="end"
> >
<!-- TODO 先做普通树 放在下一个版本 --> <!-- TODO 先做普通树 放在下一个版本 -->
<!-- <ApiTree <!-- <ApiTree
@ -161,7 +163,13 @@
</a-input-group> </a-input-group>
</a-form-item> </a-form-item>
</a-form> </a-form>
<RequestHeader v-model:params="form.headers" :no-param-type="true" /> <httpHeader
v-model:params="form.headers"
:layout="activeLayout"
:disabled-param-value="false"
:disabled-except-param="false"
:second-box-height="secondBoxHeight"
/>
</MsDrawer> </MsDrawer>
</template> </template>
@ -170,13 +178,10 @@
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue'; import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types'; import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import type { MsTreeNodeData } from '@/components/business/ms-tree/types'; import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
import RequestHeader from '../../requestHeader/index.vue';
import { getEnvModules } from '@/api/modules/api-test/management'; import { getEnvModules } from '@/api/modules/api-test/management';
// import ApiTree from './apiTree.vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore'; import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
@ -186,6 +191,8 @@
import type { ModuleTreeNode } from '@/models/common'; import type { ModuleTreeNode } from '@/models/common';
import { HttpForm } from '@/models/projectManagement/environmental'; import { HttpForm } from '@/models/projectManagement/environmental';
const httpHeader = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/header.vue'));
const props = defineProps<{ const props = defineProps<{
currentId: string; currentId: string;
isCopy: boolean; isCopy: boolean;
@ -236,6 +243,8 @@
const form = ref<HttpForm>({ ...initForm }); const form = ref<HttpForm>({ ...initForm });
const hostType = ref<string>('http://'); const hostType = ref<string>('http://');
const secondBoxHeight = ref(0);
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
const httpRef = ref(); const httpRef = ref();
@ -307,22 +316,11 @@
store.currentEnvDetailInfo.config.httpConfig.push(httpItem); store.currentEnvDetailInfo.config.httpConfig.push(httpItem);
} }
emit('close'); emit('close');
resetForm();
}; };
const envTree = ref<ModuleTreeNode[]>([]); const envTree = ref<ModuleTreeNode[]>([]);
const moreActions: ActionsItem[] = [
{
label: 'caseManagement.featureCase.copyStep',
eventTag: 'copyStep',
},
];
const selectedKeys = ref<string[]>([]);
const focusNodeKey = ref<string>('');
function handleMoreActionSelect(item: ActionsItem, node: MsTreeNodeData) {}
const title = ref<string>(''); const title = ref<string>('');
watchEffect(() => { watchEffect(() => {
title.value = props.currentId ? t('project.environmental.http.edit') : t('project.environmental.http.add'); title.value = props.currentId ? t('project.environmental.http.edit') : t('project.environmental.http.add');

View File

@ -145,7 +145,7 @@
type: Boolean, type: Boolean,
}); });
const form = ref<DataSourceItem>({ const initForm = {
id: '', id: '',
dataSource: '', dataSource: '',
driverId: '', driverId: '',
@ -154,6 +154,10 @@
password: '', password: '',
poolMax: 1, poolMax: 1,
timeout: 1000, timeout: 1000,
};
const form = ref<DataSourceItem>({
...initForm,
}); });
const getDriverOption = async () => { const getDriverOption = async () => {
@ -196,10 +200,6 @@
); );
}; };
const isXpack = computed(() => {
return licenseStore.hasLicense();
});
const formReset = () => { const formReset = () => {
form.value = { form.value = {
id: '', id: '',
@ -250,6 +250,7 @@
}; };
store.currentEnvDetailInfo.config.dataSources.push(dataSourceItem); store.currentEnvDetailInfo.config.dataSources.push(dataSourceItem);
} }
formReset();
currentVisible.value = false; currentVisible.value = false;
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console

View File

@ -5,7 +5,7 @@
</a-tabs> </a-tabs>
<a-divider margin="0"></a-divider> <a-divider margin="0"></a-divider>
<div v-if="activeTab === 'scenarioProcessorConfig'" class="h-[calc(100vh - 100px)] mt-4"> <div v-if="activeTab === 'scenarioProcessorConfig'" class="h-[calc(100vh - 100px)] mt-4">
<a-alert class="mb-4"> {{ t('project.environmental.sceneAlertDesc') }} </a-alert> <a-alert class="mb-4" closable> {{ t('project.environmental.sceneAlertDesc') }} </a-alert>
<a-scrollbar <a-scrollbar
:style="{ :style="{
overflow: 'auto', overflow: 'auto',
@ -27,7 +27,7 @@
</a-scrollbar> </a-scrollbar>
</div> </div>
<div v-if="activeTab === 'requestProcessorConfig'" class="mt-4 h-full"> <div v-if="activeTab === 'requestProcessorConfig'" class="mt-4 h-full">
<a-alert class="mb-4"> {{ t('project.environmental.requestAlertDesc') }} </a-alert> <a-alert class="mb-4" closable> {{ t('project.environmental.requestAlertDesc') }} </a-alert>
<PreTab <PreTab
v-if="props.activeType === 'pre'" v-if="props.activeType === 'pre'"
:show-associated-scene="showAssociatedScene" :show-associated-scene="showAssociatedScene"

View File

@ -1,29 +1,20 @@
<template> <template>
<div v-permission="['PROJECT_ENVIRONMENT:READ+UPDATE']" class="mb-[8px] flex items-center justify-between"> <httpHeader
<div class="font-medium">{{ t('apiTestDebug.header') }}</div>
<batchAddKeyVal :no-param-type="props.noParamType" :params="innerParams" @apply="handleBatchParamApply" />
</div>
<paramsTable
v-model:params="innerParams" v-model:params="innerParams"
:selectable="true" :layout="activeLayout"
:show-setting="false" :disabled-param-value="props.disabledParamValue"
:columns="columns" :disabled-except-param="props.disabledExceptParam"
:table-key="TableKeyEnum.PROJECT_MANAGEMENT_ENV_ALL_PARAM_HEADER" :second-box-height="secondBoxHeight"
:default-param-item="defaultParamItem" @change="emit('change')"
@change="handleParamTableChange"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import { responseHeaderOption } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { TableKeyEnum } from '@/enums/tableEnum'; const httpHeader = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/header.vue'));
defineOptions({ defineOptions({
name: 'EnvManangeGloblaRequestHeader', name: 'EnvManangeGloblaRequestHeader',
@ -32,67 +23,17 @@
const props = defineProps<{ const props = defineProps<{
params: any[]; params: any[];
noParamType?: boolean; noParamType?: boolean;
disabledParamValue?: boolean;
disabledExceptParam?: boolean; //
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:params', value: any[]): void; (e: 'update:params', value: any[]): void;
(e: 'change'): void; // (e: 'change'): void; //
}>(); }>();
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit); const innerParams = useVModel(props, 'params', emit);
const defaultParamItem = { const secondBoxHeight = ref(0);
key: '', const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
value: '',
description: '',
};
const columns: ParamTableColumn[] = [
{
title: 'apiTestDebug.paramName',
dataIndex: 'key',
slotName: 'key',
permission: ['PROJECT_ENVIRONMENT:READ+UPDATE'],
inputType: 'autoComplete',
autoCompleteParams: responseHeaderOption,
},
{
title: 'apiTestDebug.paramValue',
dataIndex: 'value',
slotName: 'value',
permission: ['PROJECT_ENVIRONMENT:READ+UPDATE'],
},
{
title: 'apiTestDebug.desc',
dataIndex: 'description',
slotName: 'description',
permission: ['PROJECT_ENVIRONMENT:READ+UPDATE'],
},
{
title: '',
slotName: 'operation',
width: 50,
},
];
/**
* 批量参数代码转换为参数表格数据
*/
function handleBatchParamApply(resultArr: any[]) {
if (resultArr.length < innerParams.value.length) {
innerParams.value.splice(0, innerParams.value.length - 1, ...resultArr);
} else {
innerParams.value = [...resultArr, innerParams.value[innerParams.value.length - 1]];
}
emit('change');
}
function handleParamTableChange(resultArr: any[], isInit?: boolean) {
innerParams.value = [...resultArr];
if (!isInit) {
emit('change');
}
}
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>

View File

@ -126,4 +126,5 @@ export default {
'Deleting it will cause scenes that reference this environment group to fail to execute properly!', 'Deleting it will cause scenes that reference this environment group to fail to execute properly!',
'project.environmental.database.nameIsExist': 'Database name already exists', 'project.environmental.database.nameIsExist': 'Database name already exists',
'project.environmental.http.noneDataExist': 'There is already a domain name with an enabled range of none!', 'project.environmental.http.noneDataExist': 'There is already a domain name with an enabled range of none!',
'project.environmental.http.selectModule': 'Please select module',
}; };

View File

@ -130,4 +130,5 @@ export default {
'project.environmental.env.deleteGroupTip': '删除后会导致引用此环境组的场景无法正常执行', 'project.environmental.env.deleteGroupTip': '删除后会导致引用此环境组的场景无法正常执行',
'project.environmental.database.nameIsExist': '数据源名称已存在', 'project.environmental.database.nameIsExist': '数据源名称已存在',
'project.environmental.http.noneDataExist': '已存在启用范围为无的域名!', 'project.environmental.http.noneDataExist': '已存在启用范围为无的域名!',
'project.environmental.http.selectModule': '请选择模块',
}; };

View File

@ -200,6 +200,7 @@
resetForm(); resetForm();
}; };
const handlePlatformChange = async (value: SelectValue) => { const handlePlatformChange = async (value: SelectValue) => {
platformRules.value = [];
try { try {
if (value) { if (value) {
const res = await getPlatformInfo(value as string, MenuEnum.bugManagement); const res = await getPlatformInfo(value as string, MenuEnum.bugManagement);

View File

@ -171,15 +171,17 @@
title: 'project.taskCenter.resourceID', title: 'project.taskCenter.resourceID',
dataIndex: 'resourceId', dataIndex: 'resourceId',
slotName: 'resourceId', slotName: 'resourceId',
width: 300, width: 200,
showInTable: true, showInTable: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.resourceName', title: 'project.taskCenter.resourceName',
slotName: 'resourceName', slotName: 'resourceName',
dataIndex: 'resourceName', dataIndex: 'resourceName',
width: 200, width: 300,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.executionResult', title: 'project.taskCenter.executionResult',
@ -203,16 +205,16 @@
slotName: 'poolName', slotName: 'poolName',
dataIndex: 'poolName', dataIndex: 'poolName',
showInTable: true, showInTable: true,
width: 200,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.operator', title: 'project.taskCenter.operator',
slotName: 'operationName', slotName: 'operationName',
dataIndex: 'operationName', dataIndex: 'operationName',
showInTable: true, showInTable: true,
width: 300,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.operating', title: 'project.taskCenter.operating',
@ -367,7 +369,7 @@
showDetailDrawer.value = true; showDetailDrawer.value = true;
} }
activeDetailId.value = id; activeDetailId.value = id;
activeReportIndex.value = rowIndex; activeReportIndex.value = rowIndex - 1;
} }
watch( watch(

View File

@ -78,6 +78,7 @@
slotName: 'resourceNum', slotName: 'resourceNum',
width: 300, width: 300,
showInTable: true, showInTable: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.resourceName', title: 'project.taskCenter.resourceName',
@ -85,6 +86,7 @@
dataIndex: 'resourceName', dataIndex: 'resourceName',
width: 200, width: 200,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.resourceClassification', title: 'project.taskCenter.resourceClassification',
@ -93,6 +95,7 @@
showInTable: true, showInTable: true,
width: 150, width: 150,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.operationRule', title: 'project.taskCenter.operationRule',
@ -101,6 +104,7 @@
showInTable: true, showInTable: true,
width: 150, width: 150,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.operator', title: 'project.taskCenter.operator',
@ -109,6 +113,7 @@
showInTable: true, showInTable: true,
width: 200, width: 200,
showDrag: true, showDrag: true,
showTooltip: true,
}, },
{ {
title: 'project.taskCenter.operating', title: 'project.taskCenter.operating',