fix: 修复接口测试报告bug&公共脚本bug&三方插件bug
This commit is contained in:
parent
1a2b88f7a2
commit
d6cc864783
|
@ -3,6 +3,7 @@ import {
|
|||
AddCommonScriptUrl,
|
||||
ConnectionWebsocketUrl,
|
||||
DeleteCommonScriptUrl,
|
||||
getChangeHistoryUrl,
|
||||
GetCommonScriptDetailUrl,
|
||||
GetCommonScriptPageUrl,
|
||||
GetCommonScriptStatusUrl,
|
||||
|
@ -19,6 +20,7 @@ import type { ModulesTreeType } from '@/models/caseManagement/featureCase';
|
|||
import { CommonList, TableQueryParams } from '@/models/common';
|
||||
import type {
|
||||
AddOrUpdateCommonScript,
|
||||
changeHistory,
|
||||
CommonScriptItem,
|
||||
TestScriptType,
|
||||
} from '@/models/projectManagement/commonScript';
|
||||
|
@ -57,6 +59,10 @@ export function getCommonScriptStatus(data: AddOrUpdateCommonScript) {
|
|||
export function getInsertCommonScriptPage(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<CommonScriptItem[]>>({ url: GetInsertCommonScriptPageUrl, data });
|
||||
}
|
||||
// 获取公共脚本变更历史详情
|
||||
export function getChangeHistory(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<changeHistory[]>>({ url: getChangeHistoryUrl, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 表格筛选字段的数据查询
|
||||
|
|
|
@ -25,3 +25,5 @@ export const GetFormApiImportModuleCountUrl = '/api/definition/module/count';
|
|||
export const TestScriptUrl = '/api/test/custom/func/run';
|
||||
// websoket连接
|
||||
export const ConnectionWebsocketUrl = '/ws/api';
|
||||
// 公共脚本变更历史详情
|
||||
export const getChangeHistoryUrl = '/project/custom/func/history/page';
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
<template>
|
||||
<div class="h-full w-full">
|
||||
<a-scrollbar
|
||||
:style="{
|
||||
overflow: 'auto',
|
||||
height: 'calc(100vh - 490px)',
|
||||
}"
|
||||
>
|
||||
<conditionContent v-model:data="condition" :disabled="props.disabled" />
|
||||
</a-scrollbar>
|
||||
</div>
|
||||
<conditionContent v-model:data="condition" :disabled="props.disabled" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@ -38,12 +29,6 @@
|
|||
|
||||
const condition = useVModel(props, 'data', emit);
|
||||
|
||||
// function handleChange() {
|
||||
// // eslint-disable-next-line no-use-before-define
|
||||
// emit('update:data');
|
||||
// emit('change', { ...condition.value });
|
||||
// }
|
||||
|
||||
const currentEnvConfig = ref({});
|
||||
|
||||
async function initEnvironment() {
|
||||
|
|
|
@ -81,7 +81,6 @@
|
|||
:style="{
|
||||
overflow: 'auto',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}"
|
||||
>
|
||||
<!-- 响应头 -->
|
||||
|
@ -123,13 +122,13 @@
|
|||
@change="handleChange"
|
||||
/>
|
||||
<!-- 脚本 -->
|
||||
<ScriptTab
|
||||
v-if="getCurrentItemState.assertionType === ResponseAssertionType.SCRIPT"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</a-scrollbar>
|
||||
<ScriptTab
|
||||
v-if="getCurrentItemState.assertionType === ResponseAssertionType.SCRIPT"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -383,7 +383,7 @@
|
|||
treeRef.value?.checkAll(val);
|
||||
}
|
||||
|
||||
function expandNode(key: string | number, expanded: boolean) {
|
||||
function expandNode(key: (string | number)[] | (string | number), expanded: boolean) {
|
||||
treeRef.value?.expandNode(key, expanded);
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ export const INT = {
|
|||
};
|
||||
|
||||
export const MULTIPLE_INPUT = {
|
||||
type: 'a-input-tag',
|
||||
type: 'MsTagsInput',
|
||||
field: 'fieldName',
|
||||
title: '',
|
||||
value: [],
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
import { ref, watch, watchEffect } from 'vue';
|
||||
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import JiraKey from './comp/jiraKey.vue';
|
||||
import PassWord from './formcreate-password.vue';
|
||||
import SearchSelect from './searchSelect.vue';
|
||||
|
@ -24,6 +25,7 @@
|
|||
formCreate.component('PassWord', PassWord);
|
||||
formCreate.component('SearchSelect', SearchSelect);
|
||||
formCreate.component('JiraKey', JiraKey);
|
||||
formCreate.component('MsTagsInput', MsTagsInput);
|
||||
const FormCreate = formCreate.$form();
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import JiraKey from './comp/jiraKey.vue';
|
||||
import PassWord from './formcreate-password.vue';
|
||||
import SearchSelect from './searchSelect.vue';
|
||||
|
@ -27,6 +28,7 @@
|
|||
formCreate.component('PassWord', PassWord);
|
||||
formCreate.component('SearchSelect', SearchSelect);
|
||||
formCreate.component('JiraKey', JiraKey);
|
||||
formCreate.component('MsTagsInput', MsTagsInput);
|
||||
|
||||
const FormCreate = formCreate.$form();
|
||||
// 处理配置项
|
||||
|
|
|
@ -1,37 +1,39 @@
|
|||
<template>
|
||||
<div :class="`flex w-full items-center ${props.class}`">
|
||||
<a-input-tag
|
||||
v-model:model-value="innerModelValue"
|
||||
v-model:input-value="innerInputValue"
|
||||
:error="isError"
|
||||
:placeholder="t(props.placeholder || 'ms.tagsInput.tagsInputPlaceholder')"
|
||||
:allow-clear="props.allowClear"
|
||||
:retain-input-value="props.retainInputValue"
|
||||
:unique-value="props.uniqueValue"
|
||||
:max-tag-count="props.maxTagCount"
|
||||
:readonly="props.readonly"
|
||||
:class="props.inputClass"
|
||||
:size="props.size"
|
||||
:disabled="props.disabled"
|
||||
@press-enter="tagInputEnter"
|
||||
@blur="tagInputBlur"
|
||||
@clear="emit('clear')"
|
||||
@click="emit('click')"
|
||||
>
|
||||
<template v-if="$slots.prefix" #prefix>
|
||||
<slot name="prefix"></slot>
|
||||
</template>
|
||||
<template v-if="$slots.tag" #tag="{ data }">
|
||||
<slot name="tag" :data="data"></slot>
|
||||
</template>
|
||||
<template v-if="$slots.suffix" #suffix>
|
||||
<slot name="suffix"></slot>
|
||||
</template>
|
||||
</a-input-tag>
|
||||
<div class="w-full">
|
||||
<div :class="`flex w-full items-center ${props.class}`">
|
||||
<a-input-tag
|
||||
v-model:model-value="innerModelValue"
|
||||
v-model:input-value="innerInputValue"
|
||||
:error="isError"
|
||||
:placeholder="t(props.placeholder || 'ms.tagsInput.tagsInputPlaceholder')"
|
||||
:allow-clear="props.allowClear"
|
||||
:retain-input-value="props.retainInputValue"
|
||||
:unique-value="props.uniqueValue"
|
||||
:max-tag-count="props.maxTagCount"
|
||||
:readonly="props.readonly"
|
||||
:class="props.inputClass"
|
||||
:size="props.size"
|
||||
:disabled="props.disabled"
|
||||
@press-enter="tagInputEnter"
|
||||
@blur="tagInputBlur"
|
||||
@clear="emit('clear')"
|
||||
@click="emit('click')"
|
||||
>
|
||||
<template v-if="$slots.prefix" #prefix>
|
||||
<slot name="prefix"></slot>
|
||||
</template>
|
||||
<template v-if="$slots.tag" #tag="{ data }">
|
||||
<slot name="tag" :data="data"></slot>
|
||||
</template>
|
||||
<template v-if="$slots.suffix" #suffix>
|
||||
<slot name="suffix"></slot>
|
||||
</template>
|
||||
</a-input-tag>
|
||||
</div>
|
||||
<div v-if="isError" class="ml-[1px] flex justify-start text-[12px] text-[rgb(var(--danger-6))]">
|
||||
{{ t('common.tagInputMaxLength', { number: props.maxLength }) }}
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="isError" class="ml-[1px] text-[12px] text-[rgb(var(--danger-6))]">
|
||||
{{ t('common.tagInputMaxLength', { number: props.maxLength }) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -105,6 +105,7 @@ export interface ScenarioItemType {
|
|||
children: ScenarioItemType[];
|
||||
level?: number;
|
||||
stepDetail: ReportStepDetailItem;
|
||||
stepChildren?: ScenarioItemType[]; // 步骤子步骤
|
||||
}
|
||||
|
||||
export type ScenarioDetailItem = Partial<ScenarioItemType>;
|
||||
|
|
|
@ -66,3 +66,16 @@ export interface TestScriptType {
|
|||
script: string;
|
||||
projectId: string;
|
||||
}
|
||||
// 变更历史详情
|
||||
export interface changeHistory {
|
||||
id: string;
|
||||
projectId: string;
|
||||
createTime: string;
|
||||
createUser: string;
|
||||
sourceId: string;
|
||||
type: string;
|
||||
module: string;
|
||||
refId: string;
|
||||
createUserName: string;
|
||||
versionName: string;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<slot name="titleRight"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="data.length > 0 && activeItem" class="flex h-[calc(100%-40px)] gap-[8px]">
|
||||
<div v-if="data.length > 0 && activeItem" class="flex h-[calc(100%-40px)] w-full gap-[8px]">
|
||||
<div class="h-full w-[20%] min-w-[220px]">
|
||||
<conditionList
|
||||
v-model:list="data"
|
||||
|
|
|
@ -310,14 +310,16 @@
|
|||
|
||||
const isShowLoopControl = computed(() => {
|
||||
return (
|
||||
props.stepItem?.children && props.stepItem.children.length && showApiType.value.includes(props.stepItem.stepType)
|
||||
props.stepItem?.stepChildren &&
|
||||
props.stepItem?.stepChildren.length &&
|
||||
showApiType.value.includes(props.stepItem.stepType)
|
||||
);
|
||||
});
|
||||
|
||||
const controlCurrent = ref<number>(1);
|
||||
const controlTotal = computed(() => {
|
||||
if (props.stepItem?.children) {
|
||||
return props.stepItem.children.length || 0;
|
||||
if (props.stepItem?.stepChildren) {
|
||||
return props.stepItem?.stepChildren.length || 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
@ -326,8 +328,8 @@
|
|||
* 循环次数控制器
|
||||
*/
|
||||
function loadControlLoop() {
|
||||
if (isShowLoopControl.value) {
|
||||
const loopStepId = props.stepItem?.children[controlCurrent.value - 1].stepId;
|
||||
if (isShowLoopControl.value && props.stepItem?.stepChildren) {
|
||||
const loopStepId = props.stepItem?.stepChildren[controlCurrent.value - 1].stepId;
|
||||
if (loopStepId) {
|
||||
getStepDetail(loopStepId);
|
||||
}
|
||||
|
@ -340,8 +342,8 @@
|
|||
() => props.stepItem?.stepId,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (isShowLoopControl.value) {
|
||||
getStepDetail(props.stepItem?.children[controlCurrent.value - 1].stepId as string);
|
||||
if (isShowLoopControl.value && props.stepItem?.stepChildren) {
|
||||
getStepDetail(props.stepItem?.stepChildren[controlCurrent.value - 1].stepId as string);
|
||||
} else {
|
||||
getStepDetail(val);
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
required: true,
|
||||
});
|
||||
|
||||
const reportStepDetail = ref<ReportDetail>({
|
||||
const initReportStepDetail = {
|
||||
id: '',
|
||||
name: '', // 报告名称
|
||||
testPlanId: '',
|
||||
|
@ -96,6 +96,9 @@
|
|||
children: [], // 步骤列表
|
||||
stepTotal: 0, // 步骤总数
|
||||
console: '',
|
||||
};
|
||||
const reportStepDetail = ref<ReportDetail>({
|
||||
...initReportStepDetail,
|
||||
});
|
||||
async function getReportCaseDetail() {
|
||||
try {
|
||||
|
@ -110,6 +113,7 @@
|
|||
() => innerVisible.value,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
reportStepDetail.value = { ...initReportStepDetail };
|
||||
await getReportCaseDetail();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,39 @@
|
|||
<div class="relative mr-4">
|
||||
<div class="charts absolute text-center">
|
||||
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
||||
<div class="text-[18px] font-medium">{{ props.requestTotal }}</div>
|
||||
<a-popover position="bottom" content-class="response-popover-content">
|
||||
<div class="flex justify-center text-[18px] font-medium">
|
||||
<div class="one-line-text max-w-[60px]">{{ getIndicators(requestTotal) }}</div>
|
||||
</div>
|
||||
<template #content>
|
||||
<div class="min-w-[176px] max-w-[400px] p-4 text-[14px]">
|
||||
<div class="text-[12px] font-medium text-[var(--color-text-4)]">{{ t('report.detail.api.total') }}</div>
|
||||
<div class="mt-2 text-[18px] font-medium text-[var(--color-text-1)]">{{
|
||||
getIndicators(addCommasToNumber(requestTotal))
|
||||
}}</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
<MsChart width="110px" height="110px" :options="props.options" />
|
||||
<a-popover position="bottom" content-class="response-popover-content">
|
||||
<div> <MsChart width="110px" height="110px" :options="props.options" /></div>
|
||||
<template #content>
|
||||
<div class="min-w-[176px] max-w-[400px] p-4">
|
||||
<div v-for="item of legendData" :key="item.value" class="mb-2 flex justify-between">
|
||||
<div class="chart-flag flex items-center">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full" :class="item.class"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ item.label }}</div>
|
||||
</div>
|
||||
<div class="count font-medium">{{ item.count || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
|
||||
<div class="chart-legend grid flex-1 gap-y-3">
|
||||
<!-- 图例开始 -->
|
||||
<div v-for="item of props.legendData" :key="item.value" class="chart-legend-item">
|
||||
<div v-for="item of legendData" :key="item.value" class="chart-legend-item">
|
||||
<div class="chart-flag">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full" :class="item.class"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ item.label }}</div>
|
||||
|
@ -30,9 +56,12 @@
|
|||
import MsChart from '@/components/pure/chart/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { addCommasToNumber, formatDuration } from '@/utils';
|
||||
|
||||
import type { LegendData } from '@/models/apiTest/report';
|
||||
|
||||
import { getIndicators } from '../../utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
options: Record<string, any>;
|
||||
|
@ -58,6 +87,7 @@
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -228,6 +228,7 @@
|
|||
const charOptions = ref({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
show: false,
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
|
|
|
@ -195,10 +195,6 @@
|
|||
reportStepDetail.value = cloneDeep(detail);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
detailDrawerRef.value?.destroy();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => showDrawer.value,
|
||||
(val) => {
|
||||
|
|
|
@ -203,10 +203,6 @@
|
|||
|
||||
const detailDrawerRef = ref();
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
detailDrawerRef.value?.destroy();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => showDrawer.value,
|
||||
(val) => {
|
||||
|
|
|
@ -47,8 +47,12 @@
|
|||
</div>
|
||||
<div>
|
||||
<a-popover position="bottom" content-class="response-popover-content">
|
||||
<span class="ml-4 text-[18px] font-medium">{{ getTotalTime.split('-')[0] || '-' }}</span>
|
||||
<span class="ml-1 text-[var(--color-text-4)]">{{ getTotalTime.split('-')[1] || 'ms' }}</span>
|
||||
<div class="flex items-center">
|
||||
<div class="one-line-text ml-4 max-w-[80px] text-[18px] font-medium">{{
|
||||
getTotalTime.split('-')[0] || '-'
|
||||
}}</div>
|
||||
<div class="ml-1 text-[var(--color-text-4)]">{{ getTotalTime.split('-')[1] || 'ms' }}</div>
|
||||
</div>
|
||||
<template #content>
|
||||
<div class="min-w-[140px] max-w-[400px] p-4 text-[14px]">
|
||||
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.totalTime') }}</div>
|
||||
|
@ -68,7 +72,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<a-popover position="bottom" content-class="response-popover-content">
|
||||
<span class="ml-4 text-[18px] font-medium">{{
|
||||
<span class="one-line-text ml-4 inline-block max-w-[80px] align-middle text-[18px] font-medium">{{
|
||||
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '-'
|
||||
}}</span>
|
||||
|
||||
|
@ -147,26 +151,11 @@
|
|||
|
||||
<div class="request-analyze">
|
||||
<div class="block-title">{{ t('report.detail.api.requestAnalysis') }}</div>
|
||||
<div class="flex min-h-[110px] items-center">
|
||||
<div class="relative mr-4">
|
||||
<div class="charts absolute text-center">
|
||||
<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>
|
||||
<MsChart width="110px" height="110px" :options="charOptions" />
|
||||
</div>
|
||||
<div class="chart-legend grid flex-1 gap-y-3">
|
||||
<!-- 图例开始 -->
|
||||
<div v-for="item of legendData" :key="item.value" class="chart-legend-item">
|
||||
<div class="chart-flag">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full" :class="item.class"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ item.label }}</div>
|
||||
</div>
|
||||
<div class="count">{{ item.count || 0 }}</div>
|
||||
<div class="count">{{ item.rote || 0 }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SetReportChart
|
||||
:legend-data="legendData"
|
||||
:options="charOptions"
|
||||
:request-total="getIndicators(detail.requestTotal)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 报告步骤分析和请求分析结束 -->
|
||||
|
@ -182,10 +171,9 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import MsChart from '@/components/pure/chart/index.vue';
|
||||
import MsColorLine from '@/components/pure/ms-color-line/index.vue';
|
||||
import SetReportChart from './case/setReportChart.vue';
|
||||
import ReportDetailHeader from './reportDetailHeader.vue';
|
||||
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
||||
import StepProgress from './stepProgress.vue';
|
||||
|
@ -265,6 +253,7 @@
|
|||
const legendData = ref<LegendData[]>([]);
|
||||
const charOptions = ref({
|
||||
tooltip: {
|
||||
show: false,
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
|
@ -449,6 +438,7 @@
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
isStaticItemHeight: true,
|
||||
estimatedSize: 48,
|
||||
}"
|
||||
:animation="false"
|
||||
action-on-node-click="expand"
|
||||
disabled-title-tooltip
|
||||
block-node
|
||||
|
@ -26,7 +27,8 @@
|
|||
@more-actions-close="() => setFocusNodeKey('')"
|
||||
>
|
||||
<template #title="step">
|
||||
<div class="flex flex-col">
|
||||
<!-- <div class="absolute h-full w-full top-0" style="border: 1px solid #0cc"></div> -->
|
||||
<div class="flex flex-col" @click="handleStop($event, step)">
|
||||
<div class="flex w-full items-center gap-[8px]">
|
||||
<div
|
||||
class="flex h-[16px] min-w-[16px] items-center justify-center rounded-full bg-[var(--color-text-brand)] px-[2px] !text-white"
|
||||
|
@ -44,7 +46,10 @@
|
|||
})
|
||||
"
|
||||
>
|
||||
<div class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-4)]">
|
||||
<div
|
||||
v-if="!(step.children && step.children.length && showApiType.includes(step.stepType))"
|
||||
class="flex cursor-pointer items-center gap-[2px] text-[var(--color-text-4)]"
|
||||
>
|
||||
<MsIcon
|
||||
:type="step.expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'"
|
||||
:size="14"
|
||||
|
@ -66,8 +71,14 @@
|
|||
</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)]">
|
||||
<div
|
||||
class="step-name-container"
|
||||
:class="{
|
||||
'w-full flex-grow': showApiType.includes(step.stepType),
|
||||
}"
|
||||
@click="showDetail($event, step)"
|
||||
>
|
||||
<div class="one-line-text mx-[4px] max-w-[300px] text-[var(--color-text-1)]">
|
||||
{{ step.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -186,6 +197,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { MsTreeExpandedData } from '@/components/business/ms-tree/types';
|
||||
|
||||
|
@ -226,14 +238,19 @@
|
|||
const innerExpandedKeys = defineModel<(string | number)[]>('expandedKeys', {
|
||||
required: false,
|
||||
});
|
||||
const showApiType = ref<string[]>([ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST]);
|
||||
|
||||
const innerNumber = ref<number>(0);
|
||||
|
||||
/**
|
||||
* 处理步骤展开折叠
|
||||
*/
|
||||
function handleStepExpand(data: MsTreeExpandedData) {
|
||||
const realStep = findNodeByKey<ScenarioItemType>(steps.value, data.node?.stepId, 'stepId');
|
||||
if (realStep) {
|
||||
realStep.expanded = !realStep.expanded;
|
||||
const isNotAllowExpand =
|
||||
data.node?.children && data.node?.children.length && showApiType.value.includes(data.node?.stepType);
|
||||
if (isNotAllowExpand && data.node && data.node.children) {
|
||||
data.node.stepChildren = cloneDeep(data.node.children);
|
||||
data.node.children = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,12 +272,17 @@
|
|||
|
||||
function expandHandler(item: ScenarioItemType) {
|
||||
const realStep = findNodeByKey<ScenarioItemType>(steps.value, item.stepId, 'stepId');
|
||||
// TODO
|
||||
if (realStep) {
|
||||
const isNotAllowExpand =
|
||||
realStep.children && realStep?.children.length && showApiType.value.includes(realStep?.stepType);
|
||||
if (isNotAllowExpand) {
|
||||
realStep.stepChildren = cloneDeep(realStep.children);
|
||||
realStep.children = [];
|
||||
}
|
||||
realStep.fold = !realStep.fold;
|
||||
}
|
||||
}
|
||||
const showApiType = ref<string[]>([ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST]);
|
||||
|
||||
const showCondition = ref<string[]>([
|
||||
ScenarioStepType.API,
|
||||
ScenarioStepType.API_CASE,
|
||||
|
@ -276,7 +298,7 @@
|
|||
return props.activeType === 'tab';
|
||||
}
|
||||
const activeItem = ref();
|
||||
function showDetail(item: ScenarioItemType) {
|
||||
function showDetail(event: Event, item: ScenarioItemType) {
|
||||
if (props.activeType === 'tab') {
|
||||
return;
|
||||
}
|
||||
|
@ -285,6 +307,7 @@
|
|||
}
|
||||
activeItem.value = item;
|
||||
emit('detail', activeItem.value);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// 响应状态码对应颜色
|
||||
|
@ -315,6 +338,12 @@
|
|||
}
|
||||
return item.children && item.children.length > 0;
|
||||
}
|
||||
|
||||
function handleStop(event: Event, step: ScenarioItemType) {
|
||||
if (step.children && step.children.length && showApiType.value.includes(step.stepType)) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
<template>
|
||||
<MsDrawer
|
||||
v-model:visible="scriptDetailDrawer"
|
||||
:title="t('project.commonScript.publicScriptName')"
|
||||
:width="768"
|
||||
:footer="false"
|
||||
unmount-on-close
|
||||
>
|
||||
<MsDrawer v-model:visible="scriptDetailDrawer" :title="form.name" :width="800" :footer="false" unmount-on-close>
|
||||
<template #headerLeft>
|
||||
<MsTag class="ml-1" type="success" theme="light">{{ t('project.commonScript.testSuccess') }}</MsTag>
|
||||
</template>
|
||||
<template #tbutton>
|
||||
<div class="flex">
|
||||
<MsButton
|
||||
v-permission="['PROJECT_API_REPORT:READ+SHARE']"
|
||||
type="icon"
|
||||
status="secondary"
|
||||
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||
@click="editHandler"
|
||||
>
|
||||
<MsIcon type="icon-icon_edit_outlined" class="mr-2 font-[16px]" />
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-permission="['PROJECT_API_REPORT:READ+SHARE']"
|
||||
type="icon"
|
||||
status="secondary"
|
||||
:loading="loading"
|
||||
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||
@click="testHandler"
|
||||
>
|
||||
{{ t('apiTestDebug.test') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<a-radio-group v-model="showType" type="button" size="small">
|
||||
<a-radio value="detail">{{ t('project.commonScript.detail') }}</a-radio>
|
||||
<a-radio value="changeHistory">{{ t('project.commonScript.changeHistory') }}</a-radio>
|
||||
|
@ -27,11 +45,11 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>{{ t('project.commonScript.inputParams') }}</span>
|
||||
<div class="mb-4">{{ t('project.commonScript.inputParams') }}</div>
|
||||
|
||||
<ms-base-table v-bind="propsRes" ref="tableRef" class="mb-4" no-disable v-on="propsEvent">
|
||||
<template #mustContain="{ record }">
|
||||
<a-checkbox v-model:model-value="record.mustContain" :disabled="true"></a-checkbox>
|
||||
<a-checkbox v-model:model-value="record.required" :disabled="true"></a-checkbox>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
|
||||
|
@ -46,7 +64,7 @@
|
|||
width="100%"
|
||||
height="calc(100vh - 155px)"
|
||||
theme="MS-text"
|
||||
:read-only="false"
|
||||
:read-only="true"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
/>
|
||||
|
@ -55,12 +73,15 @@
|
|||
v-else
|
||||
v-bind="changeHistoryPropsRes"
|
||||
ref="tableRef"
|
||||
class="mb-4"
|
||||
class="mt-4"
|
||||
no-disable
|
||||
v-on="changeHistoryPropsEvent"
|
||||
>
|
||||
<template #changeSerialNumber="{ record }"
|
||||
><div class="flex items-center"> {{ record.changeSerialNumber }} <MsTag>当前</MsTag> </div>
|
||||
<template #id="{ record }">
|
||||
<div class="flex items-center">
|
||||
<div class="one-line-text mr-2 max-w-[200px]"> {{ record.id }} </div>
|
||||
<MsTag>{{ t('project.processor.current') }}</MsTag>
|
||||
</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsButton status="primary" @click="recoverHandler(record)">
|
||||
|
@ -84,12 +105,21 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
|
||||
import { getCommonScriptDetail } from '@/api/modules/project-management/commonScript';
|
||||
import {
|
||||
getChangeHistory,
|
||||
getCommonScriptDetail,
|
||||
getSocket,
|
||||
testCommonScript,
|
||||
} from '@/api/modules/project-management/commonScript';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { getGenerateId } from '@/utils';
|
||||
|
||||
import type { AddOrUpdateCommonScript } from '@/models/projectManagement/commonScript';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -97,7 +127,7 @@
|
|||
scriptId: string; // 脚本id
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'change']);
|
||||
const emit = defineEmits(['update:visible', 'change', 'update']);
|
||||
|
||||
const showType = ref('detail');
|
||||
|
||||
|
@ -113,8 +143,8 @@
|
|||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.commonScript.ParameterNames',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
slotName: 'key',
|
||||
dataIndex: 'key',
|
||||
showTooltip: true,
|
||||
showInTable: true,
|
||||
},
|
||||
|
@ -127,8 +157,8 @@
|
|||
},
|
||||
{
|
||||
title: 'project.commonScript.description',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'description',
|
||||
dataIndex: 'description',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
|
@ -139,7 +169,7 @@
|
|||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, setProps } = useTable(undefined, {
|
||||
const { propsRes, propsEvent } = useTable(undefined, {
|
||||
columns,
|
||||
selectable: false,
|
||||
showSetting: false,
|
||||
|
@ -149,70 +179,61 @@
|
|||
|
||||
const scriptType = ref<'commonScript' | 'executionResult'>('commonScript');
|
||||
|
||||
const detailValue = ref('');
|
||||
|
||||
const changeHistoryColumns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.commonScript.changeSerialNumber',
|
||||
slotName: 'changeSerialNumber',
|
||||
dataIndex: 'changeSerialNumber',
|
||||
slotName: 'id',
|
||||
dataIndex: 'id',
|
||||
showTooltip: true,
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
title: 'project.commonScript.actionType',
|
||||
slotName: 'actionType',
|
||||
dataIndex: 'actionType',
|
||||
slotName: 'type',
|
||||
dataIndex: 'type',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'project.commonScript.actionUser',
|
||||
dataIndex: 'actionUser',
|
||||
slotName: 'actionUser',
|
||||
dataIndex: 'createUserName',
|
||||
slotName: 'createUserName',
|
||||
showTooltip: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.commonScript.updateTime',
|
||||
dataIndex: 'updateTime',
|
||||
slotName: 'updateTime',
|
||||
dataIndex: 'createTime',
|
||||
slotName: 'createTime',
|
||||
showTooltip: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.commonScript.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
// TODO:这个版本不上恢复
|
||||
// {
|
||||
// title: 'project.commonScript.tableColumnActions',
|
||||
// slotName: 'operation',
|
||||
// dataIndex: 'operation',
|
||||
// fixed: 'right',
|
||||
// width: 140,
|
||||
// showInTable: true,
|
||||
// showDrag: false,
|
||||
// },
|
||||
];
|
||||
|
||||
const {
|
||||
propsRes: changeHistoryPropsRes,
|
||||
propsEvent: changeHistoryPropsEvent,
|
||||
loadList: changeHistoryloadList,
|
||||
setLoadListParams: changeHistorySetLoadListParams,
|
||||
resetSelector: changeHistoryResetSelector,
|
||||
} = useTable(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
list: [],
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 2,
|
||||
}),
|
||||
{
|
||||
columns: changeHistoryColumns,
|
||||
tableKey: TableKeyEnum.PROJECT_MANAGEMENT_COMMON_SCRIPT_CHANGE_HISTORY,
|
||||
selectable: false,
|
||||
showSetting: false,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 300,
|
||||
}
|
||||
);
|
||||
setLoadListParams: setHistoryLoadListParams,
|
||||
loadList: loadHistoryList,
|
||||
} = useTable(getChangeHistory, {
|
||||
columns: changeHistoryColumns,
|
||||
tableKey: TableKeyEnum.PROJECT_MANAGEMENT_COMMON_SCRIPT_CHANGE_HISTORY,
|
||||
selectable: false,
|
||||
showSetting: false,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 300,
|
||||
});
|
||||
|
||||
function recoverHandler(record: any) {}
|
||||
|
||||
|
@ -230,27 +251,37 @@
|
|||
|
||||
const form = ref<AddOrUpdateCommonScript>({ ...initForm });
|
||||
|
||||
async function getDetail(scriptId: string) {
|
||||
const detailValue = computed({
|
||||
get: () => {
|
||||
return scriptType.value === 'commonScript' ? form.value.script : form.value.result;
|
||||
},
|
||||
set: (val) => val,
|
||||
});
|
||||
|
||||
async function getDetail() {
|
||||
try {
|
||||
const result = await getCommonScriptDetail(scriptId);
|
||||
const result = await getCommonScriptDetail(props.scriptId);
|
||||
form.value = cloneDeep(result);
|
||||
detailValue.value = form.value.script;
|
||||
const innerTableData = JSON.parse(form.value.params);
|
||||
setProps({ data: innerTableData.slice(0, innerTableData.length - 1) });
|
||||
propsRes.value.data = innerTableData;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
const originScript = ref<string>('');
|
||||
|
||||
watch(
|
||||
() => props.scriptId,
|
||||
(val) => {
|
||||
if (val && originScript.value !== val) {
|
||||
getDetail(val);
|
||||
}
|
||||
function loadChangeHistoryPage() {
|
||||
setHistoryLoadListParams({
|
||||
projectId: appStore.getCurrentProjectId,
|
||||
sourceId: props.scriptId,
|
||||
});
|
||||
loadHistoryList();
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.scriptId) {
|
||||
getDetail();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => scriptType.value,
|
||||
|
@ -260,8 +291,86 @@
|
|||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
originScript.value = props.scriptId;
|
||||
function editHandler() {
|
||||
emit('update', props.scriptId);
|
||||
}
|
||||
|
||||
const websocket = ref<any>();
|
||||
const reportId = ref('');
|
||||
|
||||
function testHandler() {
|
||||
reportId.value = getGenerateId();
|
||||
}
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const { type, script } = form.value;
|
||||
const parameters = JSON.parse(form.value.params);
|
||||
parameters
|
||||
.filter((item: any) => item.key && item.value)
|
||||
.map((item) => {
|
||||
return {
|
||||
key: item.key,
|
||||
value: item.value,
|
||||
valid: item.mustContain,
|
||||
};
|
||||
});
|
||||
const params = {
|
||||
type,
|
||||
script,
|
||||
params: parameters,
|
||||
projectId: appStore.currentProjectId,
|
||||
reportId: reportId.value,
|
||||
};
|
||||
await testCommonScript(params);
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onOpen() {
|
||||
run();
|
||||
}
|
||||
|
||||
function debugSocket() {
|
||||
loading.value = true;
|
||||
websocket.value = getSocket(reportId.value);
|
||||
websocket.value.onopen = onOpen;
|
||||
websocket.value.addEventListener('message', (event: any) => {
|
||||
const result = JSON.parse(event.data);
|
||||
if (result.msgType === 'EXEC_RESULT') {
|
||||
form.value.result = result.taskResult.console;
|
||||
scriptType.value = 'executionResult';
|
||||
websocket.value.close();
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => reportId.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
debugSocket();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => showType.value,
|
||||
(val) => {
|
||||
if (val === 'changeHistory') {
|
||||
loadChangeHistoryPage();
|
||||
} else {
|
||||
getDetail();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
getDetail,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -99,7 +99,12 @@
|
|||
:enable-radio-selected="radioSelected"
|
||||
@save="saveHandler"
|
||||
/>
|
||||
<ScriptDetailDrawer v-model:visible="showDetailDrawer" :script-id="scriptId" />
|
||||
<ScriptDetailDrawer
|
||||
ref="scriptDetailDrawer"
|
||||
v-model:visible="showDetailDrawer"
|
||||
:script-id="scriptId"
|
||||
@update="updateHandler"
|
||||
/>
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
|
@ -329,6 +334,7 @@
|
|||
|
||||
const paramsList = ref<ParamsRequestType[]>([]);
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
const scriptDetailDrawer = ref();
|
||||
|
||||
// 保存自定义代码片段应用
|
||||
async function saveHandler(form: AddOrUpdateCommonScript) {
|
||||
|
@ -351,6 +357,9 @@
|
|||
? t('project.commonScript.saveDraftSuccessfully')
|
||||
: t('project.commonScript.appliedSuccessfully')
|
||||
);
|
||||
if (showDetailDrawer.value) {
|
||||
scriptDetailDrawer.value.getDetail();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
|
@ -363,6 +372,11 @@
|
|||
showScriptDrawer.value = true;
|
||||
}
|
||||
|
||||
function updateHandler(id: string) {
|
||||
isEditId.value = id;
|
||||
showScriptDrawer.value = true;
|
||||
}
|
||||
|
||||
const radioSelected = ref<boolean>(false);
|
||||
|
||||
function editHandler(record: AddOrUpdateCommonScript) {
|
||||
|
|
|
@ -65,4 +65,5 @@ export default {
|
|||
'project.processor.terminationTest': 'Termination test',
|
||||
'project.processor.code_hide_report_length': 'Hide report length',
|
||||
'project.processor.code_add_report_length': 'Add report length to head',
|
||||
'project.processor.current': 'current',
|
||||
};
|
||||
|
|
|
@ -66,4 +66,5 @@ export default {
|
|||
'project.processor.terminationTest': '终止测试',
|
||||
'project.processor.code_hide_report_length': '隐藏报文长度',
|
||||
'project.processor.code_add_report_length': '报文头添加长度',
|
||||
'project.processor.current': '当前',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue