fix: 修复缺陷管理&用例管理&报告相关问题
This commit is contained in:
parent
890429bb6e
commit
1b4b6f0076
|
@ -51,7 +51,8 @@
|
||||||
<div>{{ t('apiTestDebug.expressionTip2') }}</div>
|
<div>{{ t('apiTestDebug.expressionTip2') }}</div>
|
||||||
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- <MsIcon
|
<MsIcon
|
||||||
|
v-if="props.showExtraction"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
type="icon-icon_flashlamp"
|
type="icon-icon_flashlamp"
|
||||||
:size="15"
|
:size="15"
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
: 'ms-params-input-suffix-icon'
|
: 'ms-params-input-suffix-icon'
|
||||||
"
|
"
|
||||||
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.JSON_PATH)"
|
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.JSON_PATH)"
|
||||||
/> -->
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
|
@ -146,7 +147,8 @@
|
||||||
<div>{{ t('apiTestDebug.expressionTip2') }}</div>
|
<div>{{ t('apiTestDebug.expressionTip2') }}</div>
|
||||||
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
||||||
</template>
|
</template>
|
||||||
<!-- <MsIcon
|
<MsIcon
|
||||||
|
v-if="props.showExtraction"
|
||||||
type="icon-icon_flashlamp"
|
type="icon-icon_flashlamp"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:size="15"
|
:size="15"
|
||||||
|
@ -156,7 +158,7 @@
|
||||||
: 'ms-params-input-suffix-icon'
|
: 'ms-params-input-suffix-icon'
|
||||||
"
|
"
|
||||||
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.X_PATH)"
|
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.X_PATH)"
|
||||||
/> -->
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
|
@ -295,6 +297,7 @@
|
||||||
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
||||||
</template>
|
</template>
|
||||||
<MsIcon
|
<MsIcon
|
||||||
|
v-if="props.showExtraction"
|
||||||
type="icon-icon_flashlamp"
|
type="icon-icon_flashlamp"
|
||||||
:size="15"
|
:size="15"
|
||||||
:class="
|
:class="
|
||||||
|
@ -349,7 +352,7 @@
|
||||||
import { TableColumnData, TableData } from '@arco-design/web-vue';
|
import { TableColumnData, TableData } from '@arco-design/web-vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import { statusCodeOptions } from '@/components/pure/ms-advance-filter';
|
import { EQUAL, statusCodeOptions } from '@/components/pure/ms-advance-filter';
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
import { TableOperationColumn } from '../../ms-user-group-comp/authTable.vue';
|
import { TableOperationColumn } from '../../ms-user-group-comp/authTable.vue';
|
||||||
import fastExtraction from '@/views/api-test/components/fastExtraction/index.vue';
|
import fastExtraction from '@/views/api-test/components/fastExtraction/index.vue';
|
||||||
|
@ -389,11 +392,17 @@
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(
|
||||||
data: Param;
|
defineProps<{
|
||||||
response?: string;
|
data: Param;
|
||||||
disabled?: boolean;
|
response?: string;
|
||||||
}>();
|
disabled?: boolean;
|
||||||
|
showExtraction?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
showExtraction: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:data', data: ExecuteConditionProcessor): void;
|
(e: 'update:data', data: ExecuteConditionProcessor): void;
|
||||||
|
@ -486,11 +495,9 @@
|
||||||
// json默认值
|
// json默认值
|
||||||
const jsonPathDefaultParamItem = {
|
const jsonPathDefaultParamItem = {
|
||||||
expression: '',
|
expression: '',
|
||||||
condition: '',
|
condition: EQUAL.value,
|
||||||
expectedValue: '',
|
expectedValue: '',
|
||||||
enable: true,
|
enable: true,
|
||||||
moreSettingPopoverVisible: false,
|
|
||||||
disable: true,
|
|
||||||
};
|
};
|
||||||
// xpath默认值
|
// xpath默认值
|
||||||
const xPathDefaultParamItem = {
|
const xPathDefaultParamItem = {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:step="100"
|
:step="100"
|
||||||
:min="0"
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
mode="button"
|
mode="button"
|
||||||
@blur="
|
@blur="
|
||||||
emit('change', {
|
emit('change', {
|
||||||
|
|
|
@ -105,6 +105,7 @@
|
||||||
v-model:data="getCurrentItemState"
|
v-model:data="getCurrentItemState"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:response="props.response"
|
:response="props.response"
|
||||||
|
:show-extraction="props.showExtraction"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
/>
|
/>
|
||||||
<!-- 响应时间 -->
|
<!-- 响应时间 -->
|
||||||
|
@ -176,6 +177,7 @@
|
||||||
assertionConfig?: ExecuteAssertionConfig; // 是否开启全局
|
assertionConfig?: ExecuteAssertionConfig; // 是否开启全局
|
||||||
response?: string; // 响应内容
|
response?: string; // 响应内容
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
showExtraction?: boolean; // 是否显示提取
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* @description 用于自己扩展功能的form-create
|
* @description 用于自己扩展功能的form-create
|
||||||
*/
|
*/
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
import { useVModel } from '@vueuse/core';
|
||||||
|
|
||||||
import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create';
|
import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create';
|
||||||
import JiraKey from './comp/jiraKey.vue';
|
import JiraKey from './comp/jiraKey.vue';
|
||||||
|
@ -36,7 +37,7 @@
|
||||||
api: any; // 表单对象
|
api: any; // 表单对象
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:api', 'update', 'update:form-item']);
|
const emit = defineEmits(['update:api', 'update', 'update:formItem', 'change']);
|
||||||
|
|
||||||
const fApi = computed({
|
const fApi = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -47,6 +48,8 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const innerFormItem = useVModel(props, 'formItem', emit);
|
||||||
|
|
||||||
// 规定好的字段格式
|
// 规定好的字段格式
|
||||||
const formItems = ref<FormItem[]>([...props.formRule]);
|
const formItems = ref<FormItem[]>([...props.formRule]);
|
||||||
// 控制器的选项
|
// 控制器的选项
|
||||||
|
@ -281,15 +284,16 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function changeHandler(value: any) {
|
function changeHandler(value: any, defaultValue: any, formRuleItem: FormRuleItem, api: any) {
|
||||||
fApi.value.validateField(value);
|
fApi.value.validateField(value);
|
||||||
|
emit('change', defaultValue, formRuleItem, api);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => formRuleList.value,
|
() => formRuleList.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
emit('update:form-item', formRuleList.value);
|
innerFormItem.value = val;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -93,12 +93,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const isInit = ref<boolean>(true);
|
||||||
watch(
|
watch(
|
||||||
() => selectValue.value,
|
() => selectValue.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
selectValue.value = val;
|
selectValue.value = val;
|
||||||
emit('update:model-value', val);
|
if (!isInit.value) {
|
||||||
|
emit('update:model-value', val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -115,6 +117,7 @@
|
||||||
if (props.inputSearch && props.optionMethod) {
|
if (props.inputSearch && props.optionMethod) {
|
||||||
getLinksItem();
|
getLinksItem();
|
||||||
}
|
}
|
||||||
|
isInit.value = false;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -198,8 +198,8 @@ export interface ResponseDocumentAssertion {
|
||||||
}
|
}
|
||||||
// 断言-断言列表的断言子项
|
// 断言-断言列表的断言子项
|
||||||
export interface ResponseAssertionItem {
|
export interface ResponseAssertionItem {
|
||||||
condition: RequestAssertionConditionType;
|
condition?: RequestAssertionConditionType;
|
||||||
expectedValue: string;
|
expectedValue?: string;
|
||||||
expression: string;
|
expression: string;
|
||||||
enable?: boolean;
|
enable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@ import {
|
||||||
ExecuteRequestFormBodyFormValue,
|
ExecuteRequestFormBodyFormValue,
|
||||||
KeyValueParam,
|
KeyValueParam,
|
||||||
RequestTaskResult,
|
RequestTaskResult,
|
||||||
|
ResponseAssertionItem,
|
||||||
ResponseDefinition,
|
ResponseDefinition,
|
||||||
} from '@/models/apiTest/common';
|
} from '@/models/apiTest/common';
|
||||||
import {
|
import {
|
||||||
|
RequestAssertionCondition,
|
||||||
RequestBodyFormat,
|
RequestBodyFormat,
|
||||||
RequestCaseStatus,
|
RequestCaseStatus,
|
||||||
RequestContentTypeEnum,
|
RequestContentTypeEnum,
|
||||||
|
@ -157,3 +159,17 @@ export const caseStatusOptions = [
|
||||||
{ label: 'apiTestManagement.deprecate', value: RequestCaseStatus.DEPRECATED },
|
{ label: 'apiTestManagement.deprecate', value: RequestCaseStatus.DEPRECATED },
|
||||||
{ label: 'apiTestManagement.done', value: RequestCaseStatus.DONE },
|
{ label: 'apiTestManagement.done', value: RequestCaseStatus.DONE },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 断言 参数表格默认行的值
|
||||||
|
export const defaultAssertParamsItem: ResponseAssertionItem = {
|
||||||
|
expression: '',
|
||||||
|
condition: RequestAssertionCondition.EQUALS,
|
||||||
|
expectedValue: '',
|
||||||
|
enable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 断言xpath & reg
|
||||||
|
export const defaultAssertXpathParamsItem: ResponseAssertionItem = {
|
||||||
|
expression: '',
|
||||||
|
enable: true,
|
||||||
|
};
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
v-model:params="requestVModel.children[0].assertionConfig.assertions"
|
v-model:params="requestVModel.children[0].assertionConfig.assertions"
|
||||||
:disabled="props.disabledExceptParam"
|
:disabled="props.disabledExceptParam"
|
||||||
:is-definition="false"
|
:is-definition="false"
|
||||||
|
:show-extraction="true"
|
||||||
:assertion-config="requestVModel.children[0].assertionConfig"
|
:assertion-config="requestVModel.children[0].assertionConfig"
|
||||||
/>
|
/>
|
||||||
<auth
|
<auth
|
||||||
|
|
|
@ -264,6 +264,7 @@
|
||||||
:is-definition="props.isDefinition"
|
:is-definition="props.isDefinition"
|
||||||
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
|
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
|
||||||
:assertion-config="requestVModel.children[0].assertionConfig"
|
:assertion-config="requestVModel.children[0].assertionConfig"
|
||||||
|
:show-extraction="true"
|
||||||
/>
|
/>
|
||||||
<auth
|
<auth
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
||||||
|
@ -580,6 +581,8 @@
|
||||||
import {
|
import {
|
||||||
casePriorityOptions,
|
casePriorityOptions,
|
||||||
caseStatusOptions,
|
caseStatusOptions,
|
||||||
|
defaultAssertParamsItem,
|
||||||
|
defaultAssertXpathParamsItem,
|
||||||
defaultBodyParamsItem,
|
defaultBodyParamsItem,
|
||||||
defaultHeaderParamsItem,
|
defaultHeaderParamsItem,
|
||||||
defaultKeyValueParamItem,
|
defaultKeyValueParamItem,
|
||||||
|
@ -1210,6 +1213,39 @@
|
||||||
requestName = requestVModel.value.isNew ? saveModalForm.value.name : requestVModel.value.name;
|
requestName = requestVModel.value.isNew ? saveModalForm.value.name : requestVModel.value.name;
|
||||||
requestModuleId = requestVModel.value.isNew ? saveModalForm.value.moduleId : requestVModel.value.moduleId;
|
requestModuleId = requestVModel.value.isNew ? saveModalForm.value.moduleId : requestVModel.value.moduleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理断言参数
|
||||||
|
const { assertionConfig } = requestVModel.value.children[0];
|
||||||
|
const assertionList = assertionConfig.assertions.map((assertItem: any) => {
|
||||||
|
const bodyAssertionDataByTypeList = filterKeyValParams(
|
||||||
|
assertItem.bodyAssertionDataByType.assertions,
|
||||||
|
defaultAssertParamsItem,
|
||||||
|
isExecute
|
||||||
|
).validParams;
|
||||||
|
return {
|
||||||
|
...assertItem,
|
||||||
|
bodyAssertionDataByType: {
|
||||||
|
...assertItem.bodyAssertionDataByType,
|
||||||
|
assertions: bodyAssertionDataByTypeList,
|
||||||
|
},
|
||||||
|
regexAssertion: {
|
||||||
|
...assertItem.regexAssertion,
|
||||||
|
assertions: filterKeyValParams(assertItem.regexAssertion.assertions, defaultAssertXpathParamsItem, isExecute)
|
||||||
|
.validParams,
|
||||||
|
},
|
||||||
|
xpathAssertion: {
|
||||||
|
...assertItem.xpathAssertion,
|
||||||
|
assertions: filterKeyValParams(assertItem.xpathAssertion.assertions, defaultAssertXpathParamsItem, isExecute)
|
||||||
|
.validParams,
|
||||||
|
},
|
||||||
|
jsonPathAssertion: {
|
||||||
|
...assertItem.jsonPathAssertion,
|
||||||
|
assertions: filterKeyValParams(assertItem.jsonPathAssertion.assertions, defaultAssertParamsItem, isExecute)
|
||||||
|
.validParams,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: requestVModel.value.id.toString(),
|
id: requestVModel.value.id.toString(),
|
||||||
reportId: reportId.value,
|
reportId: reportId.value,
|
||||||
|
@ -1226,7 +1262,10 @@
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||||
assertionConfig: requestVModel.value.children[0].assertionConfig,
|
assertionConfig: {
|
||||||
|
...assertionConfig,
|
||||||
|
assertions: assertionList,
|
||||||
|
},
|
||||||
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
|
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
|
||||||
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
|
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
|
||||||
},
|
},
|
||||||
|
@ -1658,15 +1697,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.url-input-tip {
|
.url-input-tip {
|
||||||
display: flex;
|
margin-top: 2px 0 250px;
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
margin-left: 250px;
|
|
||||||
color: rgb(var(--danger-6));
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
color: rgb(var(--danger-6));
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
margin-top: 2px;
|
@apply flex flex-col flex-nowrap items-center justify-start;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,114 +1,115 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="isShowLoopControl" class="my-4 flex items-center justify-start" @click.stop="() => {}">
|
<div class="flex h-[calc(100%-8px)] flex-col" @click.stop="() => {}">
|
||||||
<a-pagination
|
<div v-if="isShowLoopControl" class="my-4 flex items-center justify-start" @click.stop="() => {}">
|
||||||
v-model:page-size="controlPageSize"
|
<a-pagination
|
||||||
v-model:current="controlCurrent"
|
v-model:page-size="controlPageSize"
|
||||||
:total="controlTotal"
|
v-model:current="controlCurrent"
|
||||||
size="mini"
|
:total="controlTotal"
|
||||||
show-total
|
size="mini"
|
||||||
:show-jumper="controlTotal > 5"
|
show-total
|
||||||
@change="loadControlLoop"
|
:show-jumper="controlTotal > 5"
|
||||||
/>
|
@change="loadControlLoop"
|
||||||
</div>
|
/>
|
||||||
<div class="mt-4 flex w-full items-center justify-between rounded bg-[var(--color-text-n9)] p-4">
|
<!-- <loopPagination v-model:current-loop="controlCurrent" :loop-total="controlTotal" /> -->
|
||||||
<div class="font-medium">
|
|
||||||
<span
|
|
||||||
:class="{ 'text-[rgb(var(--primary-5))]': activeType === 'ResContent' }"
|
|
||||||
@click.stop="setActiveType('ResContent')"
|
|
||||||
>{{ t('report.detail.api.resContent') }}</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
v-if="total > 0"
|
|
||||||
:class="{ 'text-[rgb(var(--primary-5))]': activeType === 'SubRequest' }"
|
|
||||||
@click.stop="setActiveType('SubRequest')"
|
|
||||||
>
|
|
||||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
|
||||||
{{ t('report.detail.api.subRequest') }}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row gap-6 text-center">
|
<div class="mt-4 flex w-full items-center justify-between rounded bg-[var(--color-text-n9)] p-4">
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
<div class="font-medium">
|
||||||
<div class="one-line-text max-w-[200px]" :style="{ color: statusCodeColor }">
|
<span
|
||||||
{{ activeStepDetail?.content?.responseResult.responseCode || '-' }}
|
:class="{ 'text-[rgb(var(--primary-5))]': activeType === 'ResContent' }"
|
||||||
</div>
|
@click.stop="setActiveType('ResContent')"
|
||||||
<template #content>
|
>{{ t('report.detail.api.resContent') }}</span
|
||||||
<div class="flex items-center gap-[8px] text-[14px]">
|
>
|
||||||
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.statusCode') }}</div>
|
<span
|
||||||
<div :style="{ color: statusCodeColor }">
|
v-if="total > 0"
|
||||||
{{ activeStepDetail?.content?.responseResult.responseCode || '-' }}
|
:class="{ 'text-[rgb(var(--primary-5))]': activeType === 'SubRequest' }"
|
||||||
|
@click.stop="setActiveType('SubRequest')"
|
||||||
|
>
|
||||||
|
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||||
|
{{ t('report.detail.api.subRequest') }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-6 text-center">
|
||||||
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
|
<div class="one-line-text max-w-[200px]" :style="{ color: statusCodeColor }">
|
||||||
|
{{ activeStepDetail?.content?.responseResult.responseCode || '-' }}
|
||||||
|
</div>
|
||||||
|
<template #content>
|
||||||
|
<div class="flex items-center gap-[8px] text-[14px]">
|
||||||
|
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.statusCode') }}</div>
|
||||||
|
<div :style="{ color: statusCodeColor }">
|
||||||
|
{{ activeStepDetail?.content?.responseResult.responseCode || '-' }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</a-popover>
|
||||||
</a-popover>
|
<a-popover position="left" content-class="w-[400px]">
|
||||||
<a-popover position="left" content-class="w-[400px]">
|
<div class="one-line-text text-[rgb(var(--success-7))]"> {{ timingInfo?.responseTime || 0 }} ms </div>
|
||||||
<div class="one-line-text text-[rgb(var(--success-7))]"> {{ timingInfo?.responseTime || 0 }} ms </div>
|
<template #content>
|
||||||
<template #content>
|
<div class="mb-[8px] flex items-center gap-[8px] text-[14px]">
|
||||||
<div class="mb-[8px] flex items-center gap-[8px] text-[14px]">
|
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.responseTime') }}</div>
|
||||||
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.responseTime') }}</div>
|
<div class="text-[rgb(var(--success-7))]"> {{ timingInfo?.responseTime }} ms </div>
|
||||||
<div class="text-[rgb(var(--success-7))]"> {{ timingInfo?.responseTime }} ms </div>
|
|
||||||
</div>
|
|
||||||
<responseTimeLine v-if="timingInfo" :response-timing="timingInfo" />
|
|
||||||
</template>
|
|
||||||
</a-popover>
|
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
|
||||||
<div class="one-line-text text-[rgb(var(--success-7))]">
|
|
||||||
{{ activeStepDetail?.content?.responseResult.responseSize || '-' }} bytes
|
|
||||||
</div>
|
|
||||||
<template #content>
|
|
||||||
<div class="flex items-center gap-[8px] text-[14px]">
|
|
||||||
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.responseSize') }}</div>
|
|
||||||
<div class="one-line-text text-[rgb(var(--success-7))]">
|
|
||||||
{{ activeStepDetail?.content?.responseResult.responseSize }} bytes
|
|
||||||
</div>
|
</div>
|
||||||
|
<responseTimeLine v-if="timingInfo" :response-timing="timingInfo" />
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
|
<div class="one-line-text text-[rgb(var(--success-7))]">
|
||||||
|
{{ activeStepDetail?.content?.responseResult.responseSize || '-' }} bytes
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<template #content>
|
||||||
</a-popover>
|
<div class="flex items-center gap-[8px] text-[14px]">
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.responseSize') }}</div>
|
||||||
<div v-if="props.showType && props.showType !== 'CASE'" class="one-line-text max-w-[150px]">{{
|
<div class="one-line-text text-[rgb(var(--success-7))]">
|
||||||
props.environmentName
|
{{ activeStepDetail?.content?.responseResult.responseSize }} bytes
|
||||||
}}</div>
|
</div>
|
||||||
<template #content>
|
</div>
|
||||||
<div v-if="props.showType && props.showType !== 'CASE'" class="one-line-text">{{
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
<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
|
props.environmentName
|
||||||
}}</div>
|
}}</div>
|
||||||
</template>
|
<template #content>
|
||||||
</a-popover>
|
<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">
|
<MsPagination
|
||||||
<MsPagination
|
v-model:page-size="pageSize"
|
||||||
v-model:page-size="pageSize"
|
v-model:current="current"
|
||||||
v-model:current="current"
|
:total="total"
|
||||||
:total="total"
|
size="mini"
|
||||||
size="mini"
|
@change="loadLoop"
|
||||||
@change="loadLoop"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<!-- 平铺 -->
|
||||||
<!-- 平铺 -->
|
<TiledDisplay
|
||||||
<TiledDisplay
|
v-if="props.mode === 'tiled'"
|
||||||
v-if="props.mode === 'tiled'"
|
:menu-list="responseCompositionTabList"
|
||||||
:menu-list="responseCompositionTabList"
|
|
||||||
:request-result="activeStepDetailCopy?.content"
|
|
||||||
:console="props.console"
|
|
||||||
:is-definition="props.isDefinition"
|
|
||||||
:report-id="props.reportId"
|
|
||||||
/>
|
|
||||||
<!-- 响应内容tab -->
|
|
||||||
<a-spin
|
|
||||||
v-else
|
|
||||||
:loading="loading"
|
|
||||||
:class="[props.isResponseModel ? 'h-full w-full' : 'h-[calc(100%-35px)] w-full px-[18px] pb-[18px]']"
|
|
||||||
>
|
|
||||||
<result
|
|
||||||
v-model:active-tab="activeTab"
|
|
||||||
:request-result="activeStepDetailCopy?.content"
|
:request-result="activeStepDetailCopy?.content"
|
||||||
:console="props.console"
|
:console="props.console"
|
||||||
:is-http-protocol="false"
|
:is-definition="props.isDefinition"
|
||||||
:request-url="activeStepDetail?.content.url"
|
:report-id="props.reportId"
|
||||||
is-definition
|
|
||||||
:is-priority-local-exec="false"
|
|
||||||
/>
|
/>
|
||||||
</a-spin>
|
<!-- 响应内容tab -->
|
||||||
|
<div v-else class="h-[calc(100%-8px)]">
|
||||||
|
<a-spin :loading="loading" class="h-[calc(100%-8px)] w-full pb-1">
|
||||||
|
<result
|
||||||
|
v-model:active-tab="activeTab"
|
||||||
|
:request-result="activeStepDetailCopy?.content"
|
||||||
|
:console="props.console"
|
||||||
|
:is-http-protocol="false"
|
||||||
|
:request-url="activeStepDetail?.content.url"
|
||||||
|
is-definition
|
||||||
|
:is-priority-local-exec="false"
|
||||||
|
/>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -116,18 +117,19 @@
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsPagination from '@/components/pure/ms-pagination/index';
|
|
||||||
import TiledDisplay from './tiledDisplay.vue';
|
|
||||||
import result from '@/views/api-test/components/requestComposition/response/result.vue';
|
import result from '@/views/api-test/components/requestComposition/response/result.vue';
|
||||||
import responseTimeLine from '@/views/api-test/components/responseTimeLine.vue';
|
import loopPagination from '@/views/api-test/scenario/components/common/customApiDrawer.vue';
|
||||||
|
|
||||||
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, 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';
|
||||||
|
|
||||||
|
const TiledDisplay = defineAsyncComponent(() => import('./tiledDisplay.vue'));
|
||||||
|
const responseTimeLine = defineAsyncComponent(() => import('@/views/api-test/components/responseTimeLine.vue'));
|
||||||
|
const MsPagination = defineAsyncComponent(() => import('@/components/pure/ms-pagination/index'));
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mode: 'tiled' | 'tab'; // 平铺 | tab形式
|
mode: 'tiled' | 'tab'; // 平铺 | tab形式
|
||||||
stepItem?: ScenarioItemType; // 步骤详情
|
stepItem?: ScenarioItemType; // 步骤详情
|
||||||
|
@ -312,10 +314,10 @@
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const controlCurrent = ref(1);
|
const controlCurrent = ref<number>(1);
|
||||||
const controlTotal = computed(() => {
|
const controlTotal = computed(() => {
|
||||||
if (props.stepItem?.children) {
|
if (props.stepItem?.children) {
|
||||||
return props.stepItem.children.length;
|
return props.stepItem.children.length || 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex min-h-[110px] items-center">
|
<div class="flex min-h-[110px] items-center">
|
||||||
<div class="relative mr-4">
|
<div class="relative mr-4">
|
||||||
<div class="absolute bottom-0 left-[30%] top-[35%] text-center">
|
<div class="charts absolute text-center">
|
||||||
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
||||||
<div class="text-[18px] font-medium">{{ props.requestTotal }}</div>
|
<div class="text-[18px] font-medium">{{ props.requestTotal }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,4 +53,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.charts {
|
||||||
|
top: 30%;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -249,6 +249,7 @@
|
||||||
color: '#00C261',
|
color: '#00C261',
|
||||||
class: 'bg-[rgb(var(--success-6))]',
|
class: 'bg-[rgb(var(--success-6))]',
|
||||||
rateKey: 'requestPassRate',
|
rateKey: 'requestPassRate',
|
||||||
|
key: 'SUCCESS',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'report.detail.api.misstatement',
|
label: 'report.detail.api.misstatement',
|
||||||
|
@ -256,6 +257,7 @@
|
||||||
color: '#FFC14E',
|
color: '#FFC14E',
|
||||||
class: 'bg-[rgb(var(--warning-6))]',
|
class: 'bg-[rgb(var(--warning-6))]',
|
||||||
rateKey: 'requestFakeErrorRate',
|
rateKey: 'requestFakeErrorRate',
|
||||||
|
key: 'FAKE_ERROR',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'report.detail.api.error',
|
label: 'report.detail.api.error',
|
||||||
|
@ -263,6 +265,7 @@
|
||||||
color: '#ED0303',
|
color: '#ED0303',
|
||||||
class: 'bg-[rgb(var(--danger-6))]',
|
class: 'bg-[rgb(var(--danger-6))]',
|
||||||
rateKey: 'requestErrorRate',
|
rateKey: 'requestErrorRate',
|
||||||
|
key: 'ERROR',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'report.detail.api.pending',
|
label: 'report.detail.api.pending',
|
||||||
|
@ -270,14 +273,16 @@
|
||||||
color: '#D4D4D8',
|
color: '#D4D4D8',
|
||||||
class: 'bg-[var(--color-text-input-border)]',
|
class: 'bg-[var(--color-text-input-border)]',
|
||||||
rateKey: 'requestPendingRate',
|
rateKey: 'requestPendingRate',
|
||||||
|
key: 'PENDING',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let validArr;
|
let validArr;
|
||||||
if (props?.detailInfo?.integrated) {
|
if (props?.detailInfo?.integrated) {
|
||||||
validArr = cloneDeep(tempArr);
|
validArr = cloneDeep(tempArr);
|
||||||
} else {
|
} else {
|
||||||
validArr = props?.detailInfo?.status === 'SUCCESS' ? [tempArr[0]] : [tempArr[2]];
|
validArr = tempArr.filter((e) => e.key === props?.detailInfo?.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
@ -287,6 +292,7 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
legendData.value = validArr.map((item: any) => {
|
legendData.value = validArr.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
|
|
|
@ -50,8 +50,10 @@
|
||||||
</MsButton> -->
|
</MsButton> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ detail }">
|
<template #default="{ loading }">
|
||||||
<CaseReportCom :detail-info="detail" />
|
<a-spin class="h-full w-full" :loading="loading">
|
||||||
|
<CaseReportCom :detail-info="reportStepDetail" />
|
||||||
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
</MsDetailDrawer>
|
</MsDetailDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@ -103,7 +105,7 @@
|
||||||
const innerReportId = ref(props.reportId);
|
const innerReportId = ref(props.reportId);
|
||||||
const detailDrawerRef = ref();
|
const detailDrawerRef = ref();
|
||||||
|
|
||||||
const reportStepDetail = ref<ReportDetail>({
|
const initReportDetail = {
|
||||||
id: '',
|
id: '',
|
||||||
name: '', // 报告名称
|
name: '', // 报告名称
|
||||||
testPlanId: '',
|
testPlanId: '',
|
||||||
|
@ -141,6 +143,10 @@
|
||||||
children: [], // 步骤列表
|
children: [], // 步骤列表
|
||||||
stepTotal: 0, // 步骤总数
|
stepTotal: 0, // 步骤总数
|
||||||
console: '',
|
console: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportStepDetail = ref<ReportDetail>({
|
||||||
|
...initReportDetail,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,8 +191,22 @@
|
||||||
// 详情
|
// 详情
|
||||||
function loadedReport(detail: ReportDetail) {
|
function loadedReport(detail: ReportDetail) {
|
||||||
innerReportId.value = detail.id;
|
innerReportId.value = detail.id;
|
||||||
|
reportStepDetail.value = { ...initReportDetail };
|
||||||
reportStepDetail.value = cloneDeep(detail);
|
reportStepDetail.value = cloneDeep(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
detailDrawerRef.value?.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showDrawer.value,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
reportStepDetail.value = { ...initReportDetail };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
:table-data="props.tableData"
|
:table-data="props.tableData"
|
||||||
:page-change="props.pageChange"
|
:page-change="props.pageChange"
|
||||||
show-full-screen
|
show-full-screen
|
||||||
:unmount-on-close="true"
|
unmount-on-close
|
||||||
@loaded="loadedReport"
|
@loaded="loadedReport"
|
||||||
>
|
>
|
||||||
<template #titleRight="{ loading }">
|
<template #titleRight="{ loading }">
|
||||||
|
@ -51,8 +51,10 @@
|
||||||
</MsButton> -->
|
</MsButton> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ detail }">
|
<template #default="{ loading }">
|
||||||
<ScenarioCom :detail-info="detail" />
|
<a-spin class="h-full w-full" :loading="loading">
|
||||||
|
<ScenarioCom :detail-info="reportStepDetail" />
|
||||||
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
</MsDetailDrawer>
|
</MsDetailDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
@ -67,15 +69,13 @@
|
||||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||||
import ScenarioCom from './scenarioCom.vue';
|
import ScenarioCom from './scenarioCom.vue';
|
||||||
|
|
||||||
import { getShareInfo, getShareTime, reportScenarioDetail } from '@/api/modules/api-test/report';
|
import { getShareInfo, reportScenarioDetail } from '@/api/modules/api-test/report';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
import type { ReportDetail } from '@/models/apiTest/report';
|
import type { ReportDetail } from '@/models/apiTest/report';
|
||||||
import { RouteEnum } from '@/enums/routeEnum';
|
import { RouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import * as constants from 'constants';
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
|
|
||||||
const innerReportId = ref(props.reportId);
|
const innerReportId = ref(props.reportId);
|
||||||
|
|
||||||
const reportStepDetail = ref<ReportDetail>({
|
const initReportStepDetail = {
|
||||||
id: '',
|
id: '',
|
||||||
name: '', // 报告名称
|
name: '', // 报告名称
|
||||||
testPlanId: '',
|
testPlanId: '',
|
||||||
|
@ -143,6 +143,10 @@
|
||||||
children: [], // 步骤列表
|
children: [], // 步骤列表
|
||||||
stepTotal: 0, // 步骤总数
|
stepTotal: 0, // 步骤总数
|
||||||
console: '',
|
console: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const reportStepDetail = ref<ReportDetail>({
|
||||||
|
...initReportStepDetail,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,6 +154,7 @@
|
||||||
*/
|
*/
|
||||||
function loadedReport(detail: ReportDetail) {
|
function loadedReport(detail: ReportDetail) {
|
||||||
innerReportId.value = detail.id;
|
innerReportId.value = detail.id;
|
||||||
|
reportStepDetail.value = { ...initReportStepDetail };
|
||||||
reportStepDetail.value = cloneDeep(detail);
|
reportStepDetail.value = cloneDeep(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +200,21 @@
|
||||||
*/
|
*/
|
||||||
const exportLoading = ref<boolean>(false);
|
const exportLoading = ref<boolean>(false);
|
||||||
function exportHandler() {}
|
function exportHandler() {}
|
||||||
|
|
||||||
|
const detailDrawerRef = ref();
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
detailDrawerRef.value?.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showDrawer.value,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
reportStepDetail.value = { ...initReportStepDetail };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
@ -11,13 +11,19 @@
|
||||||
></div>
|
></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-[150px]"> {{ props.detail.environmentName || '-' }}</div>
|
<div class="one-line-text max-w-[150px]">
|
||||||
|
{{ props.detail.environmentName || t('report.detail.api.defaultEnv') }}</div
|
||||||
|
>
|
||||||
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
|
<a-divider direction="vertical" :margin="4" class="!mx-2"></a-divider>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="max-w-[400px] items-center gap-[8px] text-[14px]">
|
<div class="max-w-[400px] items-center gap-[8px] text-[14px]">
|
||||||
<div class="flex-shrink-0 text-[var(--color-text-4)]">{{ t('report.detail.api.executeEnv') }}</div>
|
<div class="flex-shrink-0 text-[var(--color-text-4)]">{{ t('report.detail.api.executeEnv') }}</div>
|
||||||
<div>
|
<div>
|
||||||
{{ props.detail.environmentName || '-' }}
|
{{
|
||||||
|
props.detail.environmentName || props.showType === 'CASE'
|
||||||
|
? t('report.detail.api.caseSaveEnv')
|
||||||
|
: t('report.detail.api.scenarioSavedEnv')
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -50,10 +56,10 @@
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
<span v-if="showRunMode">
|
<span v-if="props.detail.integrated">
|
||||||
{{ props.detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</span
|
{{ 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>
|
<a-divider v-if="props.detail.integrated" direction="vertical" :margin="4" class="!mx-2"></a-divider>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="items-center gap-[8px] text-[14px]">
|
<div class="items-center gap-[8px] text-[14px]">
|
||||||
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.runMode') }}</div>
|
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.runMode') }}</div>
|
||||||
|
@ -99,10 +105,6 @@
|
||||||
detail: ReportDetail;
|
detail: ReportDetail;
|
||||||
showType: 'API' | 'CASE';
|
showType: 'API' | 'CASE';
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const showRunMode = computed(() => {
|
|
||||||
return props.showType === 'API' ? props.detail.runMode : props.detail.runMode && props.detail.integrated;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -38,6 +38,21 @@
|
||||||
{{ record.integrated ? t('report.collection') : t('report.independent') }}
|
{{ record.integrated ? t('report.collection') : t('report.independent') }}
|
||||||
</MsTag>
|
</MsTag>
|
||||||
</template>
|
</template>
|
||||||
|
<template #integratedFilter="{ columnConfig }">
|
||||||
|
<TableFilter
|
||||||
|
v-model:visible="reportTypeVisible"
|
||||||
|
v-model:status-filters="integratedFiltersMap[showType]"
|
||||||
|
:title="(columnConfig.title as string)"
|
||||||
|
:list="reportTypeList"
|
||||||
|
@search="initData()"
|
||||||
|
>
|
||||||
|
<template #item="{ item }">
|
||||||
|
<MsTag theme="light" :type="item.value === 'INTEGRATED' ? 'primary' : undefined">
|
||||||
|
{{ item.value === 'INTEGRATED' ? t('report.collection') : t('report.independent') }}
|
||||||
|
</MsTag>
|
||||||
|
</template>
|
||||||
|
</TableFilter>
|
||||||
|
</template>
|
||||||
<!-- 报告触发方式筛选 -->
|
<!-- 报告触发方式筛选 -->
|
||||||
<template #triggerModeFilter="{ columnConfig }">
|
<template #triggerModeFilter="{ columnConfig }">
|
||||||
<a-trigger
|
<a-trigger
|
||||||
|
@ -45,7 +60,11 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
@popup-visible-change="handleFilterHidden"
|
@popup-visible-change="handleFilterHidden"
|
||||||
>
|
>
|
||||||
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="triggerModeFilterVisible = true">
|
<a-button
|
||||||
|
type="text"
|
||||||
|
class="arco-btn-text--secondary p-[8px_4px]"
|
||||||
|
@click.stop="triggerModeFilterVisible = true"
|
||||||
|
>
|
||||||
<div class="font-medium">
|
<div class="font-medium">
|
||||||
{{ t(columnConfig.title as string) }}
|
{{ t(columnConfig.title as string) }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,7 +98,7 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
@popup-visible-change="handleFilterHidden"
|
@popup-visible-change="handleFilterHidden"
|
||||||
>
|
>
|
||||||
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click="statusFilterVisible = true">
|
<a-button type="text" class="arco-btn-text--secondary p-[8px_4px]" @click.stop="statusFilterVisible = true">
|
||||||
<div class="font-medium">
|
<div class="font-medium">
|
||||||
{{ t(columnConfig.title as string) }}
|
{{ t(columnConfig.title as string) }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -170,6 +189,7 @@
|
||||||
import CaseReportDrawer from './caseReportDrawer.vue';
|
import CaseReportDrawer from './caseReportDrawer.vue';
|
||||||
import ReportDetailDrawer from './reportDetailDrawer.vue';
|
import ReportDetailDrawer from './reportDetailDrawer.vue';
|
||||||
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
||||||
|
import TableFilter from '@/views/case-management/caseManagementFeature/components/tableFilter.vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getShareTime,
|
getShareTime,
|
||||||
|
@ -230,6 +250,7 @@
|
||||||
title: 'report.type',
|
title: 'report.type',
|
||||||
slotName: 'integrated',
|
slotName: 'integrated',
|
||||||
dataIndex: 'integrated',
|
dataIndex: 'integrated',
|
||||||
|
titleSlotName: 'integratedFilter',
|
||||||
width: 150,
|
width: 150,
|
||||||
showDrag: true,
|
showDrag: true,
|
||||||
},
|
},
|
||||||
|
@ -321,11 +342,48 @@
|
||||||
const allListFilters = ref<string[]>([]);
|
const allListFilters = ref<string[]>([]);
|
||||||
const independentListFilters = ref<string[]>([]);
|
const independentListFilters = ref<string[]>([]);
|
||||||
const integratedListFilters = ref<string[]>([]);
|
const integratedListFilters = ref<string[]>([]);
|
||||||
|
|
||||||
const statusListFiltersMap = ref<Record<string, string[]>>({
|
const statusListFiltersMap = ref<Record<string, string[]>>({
|
||||||
all: allListFilters.value,
|
All: allListFilters.value,
|
||||||
INDEPENDENT: independentListFilters.value,
|
INDEPENDENT: independentListFilters.value,
|
||||||
INTEGRATED: integratedListFilters.value,
|
INTEGRATED: integratedListFilters.value,
|
||||||
});
|
});
|
||||||
|
// 全部过滤条件
|
||||||
|
const allIntegratedFilters = ref<string[]>([]);
|
||||||
|
const independentIntegratedFilters = ref<string[]>([]);
|
||||||
|
const integratedIntegratedFilters = ref<string[]>([]);
|
||||||
|
|
||||||
|
const reportTypeVisible = ref<boolean>(false);
|
||||||
|
|
||||||
|
const integratedFiltersMap = ref<Record<string, string[]>>({
|
||||||
|
All: allIntegratedFilters.value,
|
||||||
|
INDEPENDENT: independentIntegratedFilters.value,
|
||||||
|
INTEGRATED: integratedIntegratedFilters.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const reportTypeList = ref([
|
||||||
|
{
|
||||||
|
value: 'INDEPENDENT',
|
||||||
|
label: t('report.independent'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'INTEGRATED',
|
||||||
|
label: t('report.collection'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const integratedFilters = computed(() => {
|
||||||
|
if (showType.value === 'All') {
|
||||||
|
if (integratedFiltersMap.value[showType.value].length === 1) {
|
||||||
|
return integratedFiltersMap.value[showType.value].includes('INDEPENDENT') ? [false] : [true];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (showType.value === 'INTEGRATED') {
|
||||||
|
return [true];
|
||||||
|
}
|
||||||
|
return [false];
|
||||||
|
});
|
||||||
|
|
||||||
function initData() {
|
function initData() {
|
||||||
setLoadListParams({
|
setLoadListParams({
|
||||||
|
@ -334,7 +392,7 @@
|
||||||
moduleType: props.moduleType,
|
moduleType: props.moduleType,
|
||||||
filter: {
|
filter: {
|
||||||
status: statusListFiltersMap.value[showType.value],
|
status: statusListFiltersMap.value[showType.value],
|
||||||
integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()),
|
integrated: integratedFilters.value,
|
||||||
triggerMode: triggerModeListFilters.value,
|
triggerMode: triggerModeListFilters.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -366,7 +424,7 @@
|
||||||
condition: {
|
condition: {
|
||||||
filter: {
|
filter: {
|
||||||
status: statusListFiltersMap.value[showType.value],
|
status: statusListFiltersMap.value[showType.value],
|
||||||
integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()),
|
integrated: integratedFilters.value,
|
||||||
triggerMode: triggerModeListFilters.value,
|
triggerMode: triggerModeListFilters.value,
|
||||||
},
|
},
|
||||||
keyword: keyword.value,
|
keyword: keyword.value,
|
||||||
|
@ -433,7 +491,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusFilters = computed(() => {
|
const statusFilters = computed(() => {
|
||||||
return Object.keys(ReportStatus[props.moduleType]);
|
return Object.keys(ReportStatus[props.moduleType]) || [];
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleFilterHidden(val: boolean) {
|
function handleFilterHidden(val: boolean) {
|
||||||
|
@ -470,13 +528,13 @@
|
||||||
const showCaseDetailDrawer = ref<boolean>(false);
|
const showCaseDetailDrawer = ref<boolean>(false);
|
||||||
|
|
||||||
function showReportDetail(id: string, rowIndex: number) {
|
function showReportDetail(id: string, rowIndex: number) {
|
||||||
activeDetailId.value = id;
|
|
||||||
activeReportIndex.value = rowIndex - 1;
|
|
||||||
if (props.moduleType === ReportEnum.API_SCENARIO_REPORT) {
|
if (props.moduleType === ReportEnum.API_SCENARIO_REPORT) {
|
||||||
showDetailDrawer.value = true;
|
showDetailDrawer.value = true;
|
||||||
} else {
|
} else {
|
||||||
showCaseDetailDrawer.value = true;
|
showCaseDetailDrawer.value = true;
|
||||||
}
|
}
|
||||||
|
activeDetailId.value = id;
|
||||||
|
activeReportIndex.value = rowIndex - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shareTime = ref<string>('');
|
const shareTime = ref<string>('');
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
<div class="block-title">{{ t('report.detail.api.requestAnalysis') }}</div>
|
<div class="block-title">{{ t('report.detail.api.requestAnalysis') }}</div>
|
||||||
<div class="flex min-h-[110px] items-center">
|
<div class="flex min-h-[110px] items-center">
|
||||||
<div class="relative mr-4">
|
<div class="relative mr-4">
|
||||||
<div class="absolute bottom-0 left-[30%] top-[35%] text-center">
|
<div class="charts absolute text-center">
|
||||||
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
||||||
<div class="text-[18px] font-medium">{{ getIndicators(detail.requestTotal) }}</div>
|
<div class="text-[18px] font-medium">{{ getIndicators(detail.requestTotal) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -388,4 +388,11 @@
|
||||||
.block-title {
|
.block-title {
|
||||||
@apply mb-4 font-medium;
|
@apply mb-4 font-medium;
|
||||||
}
|
}
|
||||||
|
.charts {
|
||||||
|
top: 30%;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-full flex-col gap-[16px]">
|
<div class="flex h-full flex-col gap-[16px]">
|
||||||
<a-spin class="w-full" :loading="loading">
|
<a-spin class="w-full" :loading="loading">
|
||||||
<!-- 不做虚拟滚动 :virtual-list-props="{
|
|
||||||
height: `calc(100vh - 454px)`,
|
|
||||||
threshold: 20,
|
|
||||||
fixedSize: true,
|
|
||||||
buffer: 15,
|
|
||||||
}" -->
|
|
||||||
<MsTree
|
<MsTree
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
v-model:selected-keys="selectedKeys"
|
v-model:selected-keys="selectedKeys"
|
||||||
|
@ -16,6 +10,14 @@
|
||||||
:field-names="{ title: 'name', key: 'stepId', children: 'children' }"
|
:field-names="{ title: 'name', key: 'stepId', children: 'children' }"
|
||||||
title-class="step-tree-node-title"
|
title-class="step-tree-node-title"
|
||||||
node-highlight-class="step-tree-node-focus"
|
node-highlight-class="step-tree-node-focus"
|
||||||
|
:virtual-list-props="{
|
||||||
|
height: 'calc(100vh - 200px)',
|
||||||
|
threshold: 200,
|
||||||
|
fixedSize: true,
|
||||||
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
|
isStaticItemHeight: true,
|
||||||
|
estimatedSize: 48,
|
||||||
|
}"
|
||||||
action-on-node-click="expand"
|
action-on-node-click="expand"
|
||||||
disabled-title-tooltip
|
disabled-title-tooltip
|
||||||
block-node
|
block-node
|
||||||
|
@ -24,142 +26,138 @@
|
||||||
@more-actions-close="() => setFocusNodeKey('')"
|
@more-actions-close="() => setFocusNodeKey('')"
|
||||||
>
|
>
|
||||||
<template #title="step">
|
<template #title="step">
|
||||||
<div class="flex w-full items-center gap-[8px]">
|
<div class="flex flex-col">
|
||||||
<div
|
<div class="flex w-full items-center gap-[8px]">
|
||||||
class="flex h-[16px] min-w-[16px] items-center justify-center rounded-full bg-[var(--color-text-brand)] px-[2px] !text-white"
|
<div
|
||||||
>
|
class="flex h-[16px] min-w-[16px] items-center justify-center rounded-full bg-[var(--color-text-brand)] px-[2px] !text-white"
|
||||||
{{ step.sort }}
|
>
|
||||||
</div>
|
{{ step.sort }}
|
||||||
<div class="step-node-content flex justify-between">
|
</div>
|
||||||
<div class="flex flex-1 items-center">
|
<div class="step-node-content flex justify-between">
|
||||||
<!-- 步骤展开折叠按钮 -->
|
<div class="flex flex-1 items-center">
|
||||||
<a-tooltip
|
<!-- 步骤展开折叠按钮 -->
|
||||||
v-if="step.children?.length > 0"
|
<a-tooltip
|
||||||
:content="
|
v-if="step.children?.length > 0"
|
||||||
t(step.expanded ? 'apiScenario.collapseStepTip' : 'apiScenario.expandStepTip', {
|
:content="
|
||||||
count: step.children.length,
|
t(step.expanded ? 'apiScenario.collapseStepTip' : 'apiScenario.expandStepTip', {
|
||||||
})
|
count: step.children.length,
|
||||||
"
|
})
|
||||||
>
|
"
|
||||||
<div class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-4)]">
|
>
|
||||||
<MsIcon
|
<div class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-4)]">
|
||||||
:type="step.expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'"
|
<MsIcon
|
||||||
:size="14"
|
:type="step.expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'"
|
||||||
/>
|
:size="14"
|
||||||
<span class="mx-1"> {{ step.children?.length || 0 }}</span>
|
/>
|
||||||
</div>
|
<span class="mx-1"> {{ step.children?.length || 0 }}</span>
|
||||||
</a-tooltip>
|
|
||||||
<!-- 展开折叠控制器 -->
|
|
||||||
<div v-if="getShowExpand(step)" class="mx-1" @click.stop="expandHandler(step)">
|
|
||||||
<span v-if="step.fold" class="collapsebtn flex items-center justify-center">
|
|
||||||
<icon-right class="text-[var(--color-text-4)]" :style="{ 'font-size': '12px' }" />
|
|
||||||
</span>
|
|
||||||
<span v-else class="expand flex items-center justify-center">
|
|
||||||
<icon-down class="text-[rgb(var(--primary-6))]" :style="{ 'font-size': '12px' }" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="props.showType === 'API' && showCondition.includes(step.stepType)" class="flex-shrink-0">
|
|
||||||
<ConditionStatus class="mx-1" :status="step.stepType || ''" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-tooltip :content="step.name" position="tl">
|
|
||||||
<div class="step-name-container w-full flex-grow" @click.stop="showDetail(step)">
|
|
||||||
<div class="one-line-text mx-[4px] max-w-[150px] text-[var(--color-text-1)]">
|
|
||||||
{{ step.name }}
|
|
||||||
</div>
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<!-- 展开折叠控制器 -->
|
||||||
|
<div v-show="getShowExpand(step)" class="mx-1" @click.stop="expandHandler(step)">
|
||||||
|
<span v-if="step.fold" class="collapsebtn flex items-center justify-center">
|
||||||
|
<icon-right class="text-[var(--color-text-4)]" :style="{ 'font-size': '12px' }" />
|
||||||
|
</span>
|
||||||
|
<span v-else class="expand flex items-center justify-center">
|
||||||
|
<icon-down class="text-[rgb(var(--primary-6))]" :style="{ 'font-size': '12px' }" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="props.showType === 'API' && showCondition.includes(step.stepType)" class="flex-shrink-0">
|
||||||
|
<ConditionStatus class="mx-1" :status="step.stepType || ''" />
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<div class="flex">
|
|
||||||
<stepStatus :status="step.status || 'PENDING'" />
|
|
||||||
<!-- 脚本报错 -->
|
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
|
||||||
<MsTag
|
|
||||||
v-if="step.scriptIdentifier"
|
|
||||||
type="primary"
|
|
||||||
theme="light"
|
|
||||||
:self-style="{
|
|
||||||
color: 'rgb(var(--primary-3))',
|
|
||||||
background: 'rgb(var(--primary-1))',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<MsIcon type="icon-icon_info_outlined" class="mx-1 !text-[rgb(var(--primary-3))]" size="16" />
|
|
||||||
<span class="!text-[rgb(var(--primary-3))]">{{ t('report.detail.api.scriptErrorTip') }}</span>
|
|
||||||
</template>
|
|
||||||
</MsTag>
|
|
||||||
<template #content>
|
|
||||||
<div class="max-w-[400px]">{{ step.scriptIdentifier }}</div>
|
|
||||||
</template>
|
|
||||||
</a-popover>
|
|
||||||
<div v-show="showStatus(step)" class="flex">
|
|
||||||
<span class="statusCode mx-2">
|
|
||||||
<div v-if="step.code" class="mr-2"> {{ t('report.detail.api.statusCode') }}</div>
|
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
|
||||||
<div
|
|
||||||
v-if="step.code"
|
|
||||||
class="one-line-text max-w-[200px]"
|
|
||||||
:style="{ color: statusCodeColor(step.code) }"
|
|
||||||
>
|
|
||||||
{{ step.code || '-' }}
|
|
||||||
</div>
|
|
||||||
<template #content>
|
|
||||||
<div class="flex items-center gap-[8px] text-[14px]">
|
|
||||||
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.statusCode') }}</div>
|
|
||||||
<div :style="{ color: statusCodeColor(step.code) }">
|
|
||||||
{{ step.code || '-' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-popover>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-if="step.requestTime !== null" class="resTime">
|
<a-tooltip :content="step.name" position="tl">
|
||||||
{{ t('report.detail.api.responseTime') }}
|
<div class="step-name-container w-full flex-grow" @click.stop="showDetail(step)">
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
<div class="one-line-text mx-[4px] max-w-[150px] text-[var(--color-text-1)]">
|
||||||
<span class="resTimeCount ml-2"
|
{{ step.name }}
|
||||||
>{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
|
</div>
|
||||||
}}{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}</span
|
</div>
|
||||||
>
|
</a-tooltip>
|
||||||
<template #content>
|
</div>
|
||||||
<span v-if="step.requestTime !== null" class="resTime">
|
<div class="flex">
|
||||||
{{ t('report.detail.api.responseTime') }}
|
<stepStatus :status="step.status || 'PENDING'" />
|
||||||
<span class="resTimeCount ml-2"
|
<!-- 脚本报错 -->
|
||||||
>{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
}}{{
|
<MsTag
|
||||||
step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms'
|
v-if="step.scriptIdentifier"
|
||||||
}}</span
|
type="primary"
|
||||||
></span
|
theme="light"
|
||||||
>
|
:self-style="{
|
||||||
|
color: 'rgb(var(--primary-3))',
|
||||||
|
background: 'rgb(var(--primary-1))',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<MsIcon type="icon-icon_info_outlined" class="mx-1 !text-[rgb(var(--primary-3))]" size="16" />
|
||||||
|
<span class="!text-[rgb(var(--primary-3))]">{{ t('report.detail.api.scriptErrorTip') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</a-popover></span
|
</MsTag>
|
||||||
>
|
<template #content>
|
||||||
<span v-if="step.responseSize !== null" class="resSize">
|
<div class="max-w-[400px] break-words">{{ step.scriptIdentifier }}</div>
|
||||||
{{ t('report.detail.api.responseSize') }}
|
</template>
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
</a-popover>
|
||||||
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span>
|
<div v-show="showStatus(step)" class="flex">
|
||||||
<template #content>
|
<span class="statusCode mx-2">
|
||||||
<span class="resSize">
|
<div v-show="step.code" class="mr-2"> {{ t('report.detail.api.statusCode') }}</div>
|
||||||
{{ t('report.detail.api.responseSize') }}
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
|
<div
|
||||||
|
v-show="step.code"
|
||||||
|
class="one-line-text max-w-[200px]"
|
||||||
|
:style="{ color: statusCodeColor(step.code) }"
|
||||||
>
|
>
|
||||||
</template>
|
{{ step.code || '-' }}
|
||||||
</a-popover></span
|
</div>
|
||||||
>
|
<template #content>
|
||||||
|
<div class="flex items-center gap-[8px] text-[14px]">
|
||||||
|
<div class="text-[var(--color-text-4)]">{{ t('apiTestDebug.statusCode') }}</div>
|
||||||
|
<div :style="{ color: statusCodeColor(step.code) }">
|
||||||
|
{{ step.code || '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-show="step.requestTime !== null" class="resTime">
|
||||||
|
{{ t('report.detail.api.responseTime') }}
|
||||||
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
|
<span class="resTimeCount ml-2"
|
||||||
|
>{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
|
||||||
|
}}{{
|
||||||
|
step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms'
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<span v-show="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-show="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 v-if="!step.fold" class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!step.fold" class="line"></div>
|
<!-- 折叠展开内容 -->
|
||||||
</div>
|
<div v-show="showResContent(step)" class="foldContent mt-4 pl-2">
|
||||||
<!-- 折叠展开内容 v-if="showResContent(step)" -->
|
|
||||||
<div v-if="showResContent(step)" class="foldContent mt-4 pl-2">
|
|
||||||
<a-scrollbar
|
|
||||||
:style="{
|
|
||||||
overflow: 'auto',
|
|
||||||
height: 'calc(100vh - 540px)',
|
|
||||||
width: '100%',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<StepDetailContent
|
<StepDetailContent
|
||||||
:mode="props.activeType"
|
:mode="props.activeType"
|
||||||
:step-item="step"
|
:step-item="step"
|
||||||
|
@ -171,7 +169,14 @@
|
||||||
:report-id="props?.reportId"
|
:report-id="props?.reportId"
|
||||||
:steps="steps"
|
:steps="steps"
|
||||||
/>
|
/>
|
||||||
</a-scrollbar>
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="steps.length === 0" #empty>
|
||||||
|
<div
|
||||||
|
class="rounded-[var(--border-radius-small)] bg-[var(--color-fill-1)] p-[8px] text-center text-[12px] leading-[16px] text-[var(--color-text-4)]"
|
||||||
|
>
|
||||||
|
{{ t('common.noData') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MsTree>
|
</MsTree>
|
||||||
|
@ -182,12 +187,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
|
||||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
|
||||||
import { MsTreeExpandedData } from '@/components/business/ms-tree/types';
|
import { MsTreeExpandedData } from '@/components/business/ms-tree/types';
|
||||||
import stepStatus from './stepStatus.vue';
|
|
||||||
import StepDetailContent from '@/views/api-test/components/requestComposition/response/result/index.vue';
|
|
||||||
import ConditionStatus from '@/views/api-test/report/component/conditionStatus.vue';
|
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { findNodeByKey, formatDuration, mapTree } from '@/utils';
|
import { findNodeByKey, formatDuration, mapTree } from '@/utils';
|
||||||
|
@ -195,6 +195,14 @@
|
||||||
import type { ScenarioItemType } from '@/models/apiTest/report';
|
import type { ScenarioItemType } from '@/models/apiTest/report';
|
||||||
import { ScenarioStepType } from '@/enums/apiEnum';
|
import { ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
const StepDetailContent = defineAsyncComponent(
|
||||||
|
() => import('@/views/api-test/components/requestComposition/response/result/index.vue')
|
||||||
|
);
|
||||||
|
const stepStatus = defineAsyncComponent(() => import('./stepStatus.vue'));
|
||||||
|
const ConditionStatus = defineAsyncComponent(() => import('@/views/api-test/report/component/conditionStatus.vue'));
|
||||||
|
const MsTag = defineAsyncComponent(() => import('@/components/pure/ms-tag/ms-tag.vue'));
|
||||||
|
const MsTree = defineAsyncComponent(() => import('@/components/business/ms-tree/index.vue'));
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
stepKeyword?: string;
|
stepKeyword?: string;
|
||||||
|
@ -430,7 +438,6 @@
|
||||||
background: var(--color-text-n8) !important;
|
background: var(--color-text-n8) !important;
|
||||||
}
|
}
|
||||||
.resContentWrapper {
|
.resContentWrapper {
|
||||||
border-top: 1px solid red;
|
|
||||||
border-radius: 0 0 6px 6px;
|
border-radius: 0 0 6px 6px;
|
||||||
@apply mb-4 bg-white p-4;
|
@apply mb-4 bg-white p-4;
|
||||||
.resContent {
|
.resContent {
|
||||||
|
@ -451,4 +458,8 @@
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: var(--color-text-n8);
|
background: var(--color-text-n8);
|
||||||
}
|
}
|
||||||
|
.foldContent {
|
||||||
|
height: 100%;
|
||||||
|
height: calc(100vh);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
v-model:params="assertionConfig.assertions"
|
v-model:params="assertionConfig.assertions"
|
||||||
:is-definition="false"
|
:is-definition="false"
|
||||||
:assertion-config="assertionConfig"
|
:assertion-config="assertionConfig"
|
||||||
|
:show-extraction="true"
|
||||||
@change="emit('change')"
|
@change="emit('change')"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -246,6 +246,7 @@
|
||||||
is-definition
|
is-definition
|
||||||
:disabled="!isEditableApi"
|
:disabled="!isEditableApi"
|
||||||
:assertion-config="requestVModel.children[0].assertionConfig"
|
:assertion-config="requestVModel.children[0].assertionConfig"
|
||||||
|
:show-extraction="true"
|
||||||
/>
|
/>
|
||||||
<auth
|
<auth
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
||||||
|
|
|
@ -192,6 +192,7 @@
|
||||||
is-definition
|
is-definition
|
||||||
:disabled="!isEditableApi"
|
:disabled="!isEditableApi"
|
||||||
:assertion-config="requestVModel.children[0].assertionConfig"
|
:assertion-config="requestVModel.children[0].assertionConfig"
|
||||||
|
:show-extraction="true"
|
||||||
/>
|
/>
|
||||||
<auth
|
<auth
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
||||||
|
|
|
@ -199,7 +199,7 @@ export function initFormCreate(customFields: CustomAttributes[], permission: str
|
||||||
currentDefaultValue = item.type === 'MEMBER' ? item.defaultValue : JSON.parse(item.defaultValue);
|
currentDefaultValue = item.type === 'MEMBER' ? item.defaultValue : JSON.parse(item.defaultValue);
|
||||||
}
|
}
|
||||||
} else if (multipleInputType.includes(item.type)) {
|
} else if (multipleInputType.includes(item.type)) {
|
||||||
currentDefaultValue = JSON.parse(item.defaultValue);
|
currentDefaultValue = Array.isArray(item.defaultValue) ? item.defaultValue : JSON.parse(item.defaultValue);
|
||||||
} else if (singleType.includes(item.type)) {
|
} else if (singleType.includes(item.type)) {
|
||||||
const optionsIds = optionsValue.map((e: any) => e.value);
|
const optionsIds = optionsValue.map((e: any) => e.value);
|
||||||
currentDefaultValue = (optionsIds || []).find((e: any) => item.defaultValue === e) || '';
|
currentDefaultValue = (optionsIds || []).find((e: any) => item.defaultValue === e) || '';
|
||||||
|
|
|
@ -200,6 +200,7 @@
|
||||||
showInTable: true,
|
showInTable: true,
|
||||||
width: 200,
|
width: 200,
|
||||||
showDrag: true,
|
showDrag: true,
|
||||||
|
showTooltip: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'project.commonScript.createTime',
|
title: 'project.commonScript.createTime',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MsAssertion v-model:params="params" />
|
<MsAssertion v-model:params="params" :show-extraction="false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
v-model:model-value="form.requestTimeout"
|
v-model:model-value="form.requestTimeout"
|
||||||
:min="0"
|
:min="0"
|
||||||
:step="100"
|
:step="100"
|
||||||
|
:precision="0"
|
||||||
class="w-[180px]"
|
class="w-[180px]"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
>
|
>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
v-model:model-value="form.responseTimeout"
|
v-model:model-value="form.responseTimeout"
|
||||||
:min="0"
|
:min="0"
|
||||||
:step="100"
|
:step="100"
|
||||||
|
:precision="0"
|
||||||
class="w-[180px]"
|
class="w-[180px]"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in New Issue