feat(项目管理): 环境管理相关问题修复和调整交互
This commit is contained in:
parent
44f998dc25
commit
fdabab9052
|
@ -36,4 +36,9 @@ export function reportBathDelete(moduleType: string, data: TableQueryParams) {
|
|||
return MSR.post({ url: reportUrl.ApiBatchDeleteUrl, data });
|
||||
}
|
||||
|
||||
// 报告详情
|
||||
export function reportDetail(reportId: string) {
|
||||
return MSR.get<Record<string, any>>({ url: `${reportUrl.ScenarioReportDetailUrl}/${reportId}` });
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -88,6 +88,10 @@ export function groupDeleteEnv(data: EnvListItem) {
|
|||
export function groupProjectEnv(organizationId: string) {
|
||||
return MSR.get<ProjectOptionItem[]>({ url: envURL.groupProjectEnvUrl + organizationId });
|
||||
}
|
||||
// 获取项目组的项目
|
||||
export function groupCategoryEnvList(projectId: string) {
|
||||
return MSR.get<ProjectOptionItem[]>({ url: `${envURL.getProjectEnvCategoryUrl}/${projectId}` });
|
||||
}
|
||||
|
||||
/** 项目管理-环境-全局参数-更新or新增 */
|
||||
export function updateOrAddGlobalParam(data: GlobalParams) {
|
||||
|
|
|
@ -15,3 +15,6 @@ export const ApiReportRenameUrl = '/api/report/case/rename';
|
|||
export const ApiDeleteUrl = '/api/report/case/delete';
|
||||
// 批量删除接口用例报告
|
||||
export const ApiBatchDeleteUrl = '/api/report/case/batch/delete';
|
||||
|
||||
// 场景报告拔高详情获取
|
||||
export const ScenarioReportDetailUrl = '/api/report/scenario/get';
|
||||
|
|
|
@ -24,3 +24,5 @@ export const addGlobalParamUrl = '/project/global/params/add';
|
|||
export const importGlobalParamUrl = '/project/global/params/import';
|
||||
export const detailGlobalParamUrl = '/project/global/params/get/';
|
||||
export const exportGlobalParamUrl = '/project/global/params/export/';
|
||||
// 获取环境目录列表
|
||||
export const getProjectEnvCategoryUrl = '/project/environment/get-options';
|
||||
|
|
|
@ -386,7 +386,6 @@
|
|||
getCurrentItemState.value =
|
||||
assertions.value.find((item: any) => item.id === activeKey.value) || assertions.value[0] || {};
|
||||
activeKey.value = getCurrentItemState.value.id;
|
||||
console.log(getCurrentItemState.value, 'getCurrentItemState.value');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -372,9 +372,10 @@ export default defineComponent(
|
|||
}
|
||||
emit('update:modelValue', value);
|
||||
emit('change', value);
|
||||
|
||||
emit(
|
||||
'changeObject',
|
||||
remoteOriginOptions.value.filter((e) => value === e[props.valueKey || 'value'])
|
||||
remoteOriginOptions.value.find((e) => value === e[props.valueKey || 'value'])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
function selectItem(index: any) {
|
||||
const item = props.items[index];
|
||||
if (item) {
|
||||
props.command({ id: item.id, label: `${item.name}` } as any);
|
||||
props.command({ id: item.id, label: `${item.name}`, style: 'color:blur' } as any);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
const isError = computed(
|
||||
() =>
|
||||
innerInputValue.value.length > props.maxLength ||
|
||||
innerModelValue.value.some((item) => item.toString().length > props.maxLength)
|
||||
(innerModelValue.value || []).some((item) => item.toString().length > props.maxLength)
|
||||
);
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
size="16"
|
||||
/>
|
||||
<template #content>
|
||||
<div>{{ t('apiTestDebug.batchAddParamsTip1') }}</div>
|
||||
<div>{{ props?.addTypeText || t('apiTestDebug.batchAddParamsTip1') }}</div>
|
||||
<div v-if="!props.noParamType">{{ t('apiTestDebug.batchAddParamsTip2') }}</div>
|
||||
<div>{{ t('apiTestDebug.batchAddParamsTip3') }}</div>
|
||||
</template>
|
||||
|
@ -53,6 +53,7 @@
|
|||
params: Record<string, any>[];
|
||||
defaultParamItem?: Record<string, any>; // 默认参数项
|
||||
noParamType?: boolean; // 是否有参数类型
|
||||
addTypeText?: string; // 添加类型文案
|
||||
}>(),
|
||||
{
|
||||
noParamType: false,
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
{{ t('common.clear') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="!props.isBuildIn"
|
||||
v-if="!props.isBuildIn && !props.showPrePostRequest"
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary p-[0_8px]"
|
||||
size="mini"
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
<template #tag="{ record, columnConfig, rowIndex }">
|
||||
<a-popover
|
||||
position="tl"
|
||||
:disabled="record[columnConfig.dataIndex as string].length === 0"
|
||||
:disabled="(record[columnConfig.dataIndex as string]||[]).length === 0"
|
||||
class="ms-params-input-popover"
|
||||
>
|
||||
<template #content>
|
||||
|
@ -307,15 +307,13 @@
|
|||
<template #arrow-icon>
|
||||
<icon-caret-down />
|
||||
</template>
|
||||
<a-tooltip
|
||||
v-for="project of appStore.projectList"
|
||||
:key="project.id"
|
||||
:mouse-enter-delay="500"
|
||||
:content="project.name"
|
||||
>
|
||||
|
||||
<a-tooltip v-for="project of disProjectList" :key="project.id" :mouse-enter-delay="500" :content="project.name">
|
||||
<a-option
|
||||
:key="project.id"
|
||||
:value="project.id"
|
||||
:class="project.id === appStore.currentProjectId ? 'arco-select-option-selected' : ''"
|
||||
:disabled="project.disabled"
|
||||
>
|
||||
{{ project.name }}
|
||||
</a-option>
|
||||
|
@ -343,17 +341,13 @@
|
|||
<span v-else></span>
|
||||
</template>
|
||||
<template #host="{ record }">
|
||||
<span v-if="!record.domain || record.domain.length === 0"></span>
|
||||
<span v-else-if="Array.isArray(record.domain) && record.domain.length === 1" class="text-[var(--color-text-4)]">{{
|
||||
record.domain[0].protocol + '://' + (record.domain[0].hostname || '')
|
||||
}}</span>
|
||||
<span
|
||||
v-if="Array.isArray(record.domain) && record.domain.length > 1"
|
||||
class="cursor-pointer text-[var(--color-text-4)]"
|
||||
<MsTagGroup
|
||||
v-if="Array.isArray(record.domain)"
|
||||
:tag-list="getDomain(record.domain)"
|
||||
size="small"
|
||||
@click="showHostModal(record)"
|
||||
>
|
||||
{{ t('common.more') }}
|
||||
</span>
|
||||
/>
|
||||
<div v-else class="text-[var(--color-text-1)]">{{ '-' }}</div>
|
||||
</template>
|
||||
<template #operation="{ record, rowIndex, columnConfig }">
|
||||
<div class="flex flex-row items-center" :class="{ 'justify-end': columnConfig.align === 'right' }">
|
||||
|
@ -450,9 +444,10 @@
|
|||
:max-length="1000"
|
||||
></a-textarea>
|
||||
</a-modal>
|
||||
<a-modal v-model:visible="hostVisible" :title="t('project.environmental.host')" @close="hostModalClose">
|
||||
<!-- <a-modal v-model:visible="hostVisible" :title="t('project.environmental.host')" @close="hostModalClose">
|
||||
<a-table :columns="hostColumn" :data="hostData" />
|
||||
</a-modal>
|
||||
</a-modal> -->
|
||||
<DomainModal v-model:visible="hostVisible" :data="hostData" />
|
||||
</template>
|
||||
|
||||
<script async setup lang="ts">
|
||||
|
@ -464,18 +459,20 @@
|
|||
import MsFormTable, { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import MsTagGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
|
||||
import MsTagsGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
import MsSelect from '@/components/business/ms-select/index';
|
||||
import paramDescInput from './paramDescInput.vue';
|
||||
import DomainModal from '@/views/project-management/environmental/components/envParams/popUp/domain.vue';
|
||||
|
||||
import { groupProjectEnv, listEnv } from '@/api/modules/project-management/envManagement';
|
||||
import { groupCategoryEnvList, groupProjectEnv } from '@/api/modules/project-management/envManagement';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import { ModuleTreeNode, TransferFileParams } from '@/models/common';
|
||||
import { ProjectOptionItem } from '@/models/projectManagement/environmental';
|
||||
import { HttpForm, ProjectOptionItem } from '@/models/projectManagement/environmental';
|
||||
import { RequestBodyFormat, RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
|
@ -645,15 +642,18 @@
|
|||
const res = await groupProjectEnv(appStore.currentOrgId);
|
||||
sourceProjectOptions.value = res;
|
||||
};
|
||||
// 获取所有环境目录
|
||||
const envDomainList = ref<ProjectOptionItem[]>([]);
|
||||
// 获取环境的options
|
||||
const initEnvOptions = async (params: Record<string, any>) => {
|
||||
const { projectId, keyword } = params;
|
||||
const res = await listEnv({ projectId, keyword });
|
||||
const { projectId } = params;
|
||||
const res = await groupCategoryEnvList(projectId);
|
||||
envDomainList.value = res;
|
||||
return res;
|
||||
};
|
||||
|
||||
const handleEnvironment = (obj: Record<string, any>, record: Record<string, any>) => {
|
||||
record.domain = {};
|
||||
record.domain = obj.domain;
|
||||
emitChange('handleEnvironment');
|
||||
};
|
||||
|
||||
|
@ -663,6 +663,8 @@
|
|||
{
|
||||
title: t('project.environmental.http.host'),
|
||||
dataIndex: 'host',
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
title: t('project.environmental.http.desc'),
|
||||
|
@ -672,10 +674,6 @@
|
|||
|
||||
const showHostModal = (record: Record<string, any>) => {
|
||||
hostVisible.value = true;
|
||||
record.domain?.forEach((e: any) => {
|
||||
e.host = `${e.protocol} :// ${e.hostname || ''}`;
|
||||
});
|
||||
|
||||
hostData.value = record.domain || [];
|
||||
};
|
||||
|
||||
|
@ -899,6 +897,29 @@
|
|||
addTableLine(rowIndex);
|
||||
}
|
||||
|
||||
const disProjectList = computed(() => {
|
||||
const selectProjectIds = (props.params || []).map((item) => item.projectId).filter((item) => item);
|
||||
return appStore.projectList.map((item: any) => {
|
||||
if (selectProjectIds.includes(item.id)) {
|
||||
return {
|
||||
...item,
|
||||
disabled: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
});
|
||||
});
|
||||
function getDomain(domain: HttpForm[]) {
|
||||
return (domain || []).map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.hostname,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
addTableLine,
|
||||
});
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
<template>
|
||||
<MsDetailDrawer
|
||||
ref="detailDrawerRef"
|
||||
v-model:visible="showDrawer"
|
||||
:width="960"
|
||||
:footer="false"
|
||||
:title="t('project.fileManagement.detail')"
|
||||
:detail-id="props.reportId"
|
||||
:detail-index="props.activeReportIndex"
|
||||
:get-detail-func="reportDetail"
|
||||
:pagination="props.pagination"
|
||||
:table-data="props.tableData"
|
||||
:page-change="props.pageChange"
|
||||
show-full-screen
|
||||
:unmount-on-close="true"
|
||||
@loaded="loadedReport"
|
||||
>
|
||||
<template #titleRight="{ loading }">
|
||||
<div class="rightButtons flex items-center">
|
||||
<MsButton
|
||||
type="icon"
|
||||
status="secondary"
|
||||
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||
:disabled="loading"
|
||||
:loading="shareLoading"
|
||||
@click="shareHandler"
|
||||
>
|
||||
<MsIcon type="icon-icon_share1" class="mr-2 font-[16px]" />
|
||||
{{ t('common.share') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
type="icon"
|
||||
status="secondary"
|
||||
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||
:disabled="loading"
|
||||
:loading="exportLoading"
|
||||
@click="exportHandler"
|
||||
>
|
||||
<MsIcon type="icon-icon_move_outlined" class="mr-2 font-[16px]" />
|
||||
{{ t('common.export') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="report-container h-full">
|
||||
<!-- 报告参数开始 -->
|
||||
<div class="report-header flex items-center justify-between">
|
||||
<!-- TODO 虚拟数据替换接口后边 -->
|
||||
<span>
|
||||
dev环境
|
||||
<a-divider direction="vertical" :margin="4"></a-divider>
|
||||
66 资源池
|
||||
<a-divider direction="vertical" :margin="4"></a-divider>
|
||||
1000ms
|
||||
<a-divider direction="vertical" :margin="4"></a-divider>
|
||||
admin
|
||||
</span>
|
||||
<span>
|
||||
<span class="text-[var(--color-text-4)]">执行时间</span>
|
||||
2023-08-10 17:53:03
|
||||
<span class="text-[var(--color-text-4)]">至</span>
|
||||
2023-08-10 17:53:03
|
||||
</span>
|
||||
</div>
|
||||
<!-- 报告参数结束 -->
|
||||
<!-- 报告步骤分析和请求分析开始 -->
|
||||
<div class="analyze">
|
||||
<div class="step-analyze min-w-[522px]">
|
||||
<div class="block-title">步骤分析</div>
|
||||
<div class="mb-2 flex items-center">
|
||||
<!-- 总数 -->
|
||||
<div class="countItem">
|
||||
<span class="mr-2 text-[var(--color-text-4)]"> {{ t('report.detail.stepTotal') }}</span>
|
||||
{{ reportStepDetail.stepTotal }}
|
||||
</div>
|
||||
<!-- 通过 -->
|
||||
<div class="countItem">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--success-6))]"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.successCount') }}</div>
|
||||
{{ reportStepDetail.successCount }}
|
||||
</div>
|
||||
<!-- 误报 -->
|
||||
<div class="countItem">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--warning-6))]"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.fakeErrorCount') }}</div>
|
||||
{{ reportStepDetail.fakeErrorCount }}
|
||||
</div>
|
||||
<!-- 失败 -->
|
||||
<div class="countItem">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--danger-6))]"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.errorCount') }}</div>
|
||||
{{ reportStepDetail.errorCount }}
|
||||
</div>
|
||||
<!-- 未执行 -->
|
||||
<div class="countItem">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[var(--color-text-input-border)]"></div>
|
||||
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.pendingCount') }}</div>
|
||||
{{ reportStepDetail.pendingCount }}
|
||||
</div>
|
||||
</div>
|
||||
<StepProgress :report-detail="reportStepDetail" height="8px" radius="var(--border-radius-mini)" />
|
||||
</div>
|
||||
|
||||
<div class="request-analyze"></div>
|
||||
</div>
|
||||
<!-- 报告步骤分析和请求分析结束 -->
|
||||
<!-- 报告明细开始 -->
|
||||
<div class="report-info"></div>
|
||||
<!-- 报告明细结束 -->
|
||||
</div>
|
||||
</template>
|
||||
</MsDetailDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import type { MsPaginationI } from '@/components/pure/ms-table/type';
|
||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||
import StepProgress from './stepProgress.vue';
|
||||
|
||||
import { reportDetail } from '@/api/modules/api-test/report';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
reportId: string;
|
||||
activeReportIndex: number;
|
||||
tableData: any[];
|
||||
pagination: MsPaginationI;
|
||||
pageChange: (page: number) => Promise<void>;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
}>();
|
||||
|
||||
const showDrawer = computed({
|
||||
get() {
|
||||
return props.visible;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:visible', val);
|
||||
},
|
||||
});
|
||||
const innerFileId = ref(props.reportId);
|
||||
function loadedReport(detail: Record<string, any>) {
|
||||
innerFileId.value = detail.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享share
|
||||
*/
|
||||
const shareLoading = ref<boolean>(false);
|
||||
function shareHandler() {}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*/
|
||||
const exportLoading = ref<boolean>(false);
|
||||
function exportHandler() {}
|
||||
|
||||
const reportStepDetail = ref({
|
||||
stepTotal: 8,
|
||||
errorCount: 2,
|
||||
fakeErrorCount: 8,
|
||||
pendingCount: 9,
|
||||
successCount: 9,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.report-container {
|
||||
padding: 16px;
|
||||
height: calc(100vh - 56px);
|
||||
background: var(--color-text-n9);
|
||||
.report-header {
|
||||
padding: 0 16px;
|
||||
height: 54px;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
@apply mb-4 bg-white;
|
||||
}
|
||||
.analyze {
|
||||
padding: 16px;
|
||||
min-height: 196px;
|
||||
border-radius: 4px;
|
||||
@apply flex justify-between bg-white;
|
||||
.step-analyze {
|
||||
@apply mb-4 h-full;
|
||||
.countItem {
|
||||
@apply mr-6 flex items-center;
|
||||
}
|
||||
}
|
||||
.request-analyze {
|
||||
@apply h-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
.block-title {
|
||||
@apply mb-4 font-medium;
|
||||
}
|
||||
</style>
|
|
@ -24,6 +24,11 @@
|
|||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #name="{ record, rowIndex }">
|
||||
<a-button type="text" class="flex w-full" @click="showReportDetail(record.id, rowIndex)">{{
|
||||
record.name
|
||||
}}</a-button>
|
||||
</template>
|
||||
<!-- 报告类型 -->
|
||||
<template #integrated="{ record }">
|
||||
<MsTag theme="light" :type="record.integrated ? 'primary' : undefined">
|
||||
|
@ -101,6 +106,14 @@
|
|||
>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<ReportDetailDrawer
|
||||
v-model:visible="showDetailDrawer"
|
||||
:report-id="activeDetailId"
|
||||
:active-report-index="activeReportIndex"
|
||||
:table-data="propsRes.data"
|
||||
:page-change="propsEvent.pageChange"
|
||||
:pagination="propsRes.msPagination!"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -114,6 +127,7 @@
|
|||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import ReportDetailDrawer from './reportDetailDrawer.vue';
|
||||
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
||||
|
||||
import { reportBathDelete, reportDelete, reportList, reportRename } from '@/api/modules/api-test/report';
|
||||
|
@ -361,6 +375,18 @@
|
|||
initData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 报告详情 showReportDetail
|
||||
*/
|
||||
const activeDetailId = ref<string>('');
|
||||
const activeReportIndex = ref<number>(0);
|
||||
const showDetailDrawer = ref<boolean>(false);
|
||||
function showReportDetail(id: string, rowIndex: number) {
|
||||
showDetailDrawer.value = true;
|
||||
activeDetailId.value = id;
|
||||
activeReportIndex.value = rowIndex;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.moduleType,
|
||||
(val) => {
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<MsColorLine :color-data="colorData" :height="props.height" :radius="props.radius">
|
||||
<template #popoverContent>
|
||||
<table class="min-w-[144px]">
|
||||
<tr>
|
||||
<td class="popover-label-td">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--success-6))]"></div>
|
||||
<div>{{ t('report.detail.successCount') }}</div>
|
||||
</td>
|
||||
<td class="popover-value-td">
|
||||
{{ props.reportDetail.successCount }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="popover-label-td">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--warning-6))]"></div>
|
||||
<div>{{ t('report.detail.fakeErrorCount') }}</div>
|
||||
</td>
|
||||
<td class="popover-value-td">
|
||||
{{ props.reportDetail.fakeErrorCount }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="popover-label-td">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--danger-6))]"></div>
|
||||
<div>{{ t('report.detail.errorCount') }}</div>
|
||||
</td>
|
||||
<td class="popover-value-td">
|
||||
{{ props.reportDetail.errorCount }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="popover-label-td">
|
||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[var(--color-text-input-border)]"></div>
|
||||
<div>{{ t('report.detail.pendingCount') }}</div>
|
||||
</td>
|
||||
<td class="popover-value-td">
|
||||
{{ props.reportDetail.pendingCount }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
</MsColorLine>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsColorLine from '@/components/pure/ms-color-line/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
reportDetail: {
|
||||
stepTotal: number;
|
||||
errorCount: number;
|
||||
fakeErrorCount: number;
|
||||
pendingCount: number;
|
||||
successCount: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
height: string;
|
||||
radius?: string;
|
||||
}>();
|
||||
const { t } = useI18n();
|
||||
|
||||
const colorData = computed(() => {
|
||||
if (
|
||||
props.reportDetail.status === 'ERROR' ||
|
||||
(props.reportDetail.successCount === 0 &&
|
||||
props.reportDetail.errorCount === 0 &&
|
||||
props.reportDetail.fakeErrorCount === 0 &&
|
||||
props.reportDetail.pendingCount === 0)
|
||||
) {
|
||||
return [
|
||||
{
|
||||
percentage: 100,
|
||||
color: 'var(--color-text-n8)',
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
percentage: (props.reportDetail.successCount / props.reportDetail.stepTotal) * 100,
|
||||
color: 'rgb(var(--success-6))',
|
||||
},
|
||||
{
|
||||
percentage: (props.reportDetail.errorCount / props.reportDetail.stepTotal) * 100,
|
||||
color: 'rgb(var(--danger-6))',
|
||||
},
|
||||
{
|
||||
percentage: (props.reportDetail.fakeErrorCount / props.reportDetail.stepTotal) * 100,
|
||||
color: 'rgb(var(--warning-6))',
|
||||
},
|
||||
{
|
||||
percentage: (props.reportDetail.pendingCount / props.reportDetail.stepTotal) * 100,
|
||||
color: 'var(--color-text-input-border)',
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.popover-label-td {
|
||||
@apply flex items-center;
|
||||
|
||||
padding: 8px 8px 0 0;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.popover-value-td {
|
||||
@apply font-medium;
|
||||
|
||||
padding-top: 8px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
</style>
|
|
@ -23,4 +23,9 @@ export default {
|
|||
'report.trigger.interface': 'API',
|
||||
'report.trigger.batch.execution': 'Batch',
|
||||
'report.delete.tip': 'Are you sure you want to delete {count} selected reports?',
|
||||
'report.detail.successCount': 'pass',
|
||||
'report.detail.errorCount': 'Failure',
|
||||
'report.detail.fakeErrorCount': 'False alarm',
|
||||
'report.detail.pendingCount': 'Not executed',
|
||||
'report.detail.stepTotal': 'total',
|
||||
};
|
||||
|
|
|
@ -23,4 +23,9 @@ export default {
|
|||
'report.trigger.interface': 'API 执行',
|
||||
'report.trigger.batch.execution': '批量执行',
|
||||
'report.delete.tip': '确认删除已选中的 {count} 个报告吗?',
|
||||
'report.detail.successCount': '通过',
|
||||
'report.detail.errorCount': '失败',
|
||||
'report.detail.fakeErrorCount': '误报',
|
||||
'report.detail.pendingCount': '未执行',
|
||||
'report.detail.stepTotal': '总数',
|
||||
};
|
||||
|
|
|
@ -91,13 +91,15 @@
|
|||
title: 'project.environmental.env',
|
||||
dataIndex: 'environmentId',
|
||||
slotName: 'environment',
|
||||
width: 200,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.host',
|
||||
dataIndex: 'host',
|
||||
slotName: 'host',
|
||||
width: 200,
|
||||
showTooltip: true,
|
||||
isTag: true,
|
||||
width: 456,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.desc',
|
||||
|
|
|
@ -94,11 +94,14 @@
|
|||
|
||||
import { getEnvPlugin, updateOrAddEnv } from '@/api/modules/project-management/envManagement';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import { useAppStore } from '@/store';
|
||||
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
|
||||
|
||||
import { ContentTabItem, EnvPluginListItem } from '@/models/projectManagement/environmental';
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
setState(false);
|
||||
const emit = defineEmits<{
|
||||
(e: 'ok'): void;
|
||||
(e: 'resetEnv'): void;
|
||||
|
@ -200,6 +203,7 @@
|
|||
loading.value = true;
|
||||
store.currentEnvDetailInfo.mock = true;
|
||||
await updateOrAddEnv({ fileList: [], request: store.currentEnvDetailInfo });
|
||||
setState(true);
|
||||
Message.success(t('common.saveSuccess'));
|
||||
emit('ok');
|
||||
} catch (error) {
|
||||
|
|
|
@ -14,14 +14,19 @@
|
|||
</span>
|
||||
</template>
|
||||
</a-input-search>
|
||||
<batchAddKeyVal :params="innerParams" @apply="handleBatchParamApply" />
|
||||
<batchAddKeyVal
|
||||
:add-type-text="t('project.environmental.env.constantBatchAddTip')"
|
||||
:params="innerParams"
|
||||
no-param-type
|
||||
@apply="handleBatchParamApply"
|
||||
/>
|
||||
</div>
|
||||
<paramsTable
|
||||
v-model:params="innerParams"
|
||||
:table-key="props.tableKey"
|
||||
:columns="columns"
|
||||
show-setting
|
||||
:selectable="false"
|
||||
:selectable="true"
|
||||
:default-param-item="defaultParamItem"
|
||||
@change="handleParamTableChange"
|
||||
/>
|
||||
|
@ -67,6 +72,7 @@
|
|||
value: '',
|
||||
description: '',
|
||||
tags: [],
|
||||
enable: true,
|
||||
};
|
||||
|
||||
const columns: ParamTableColumn[] = [
|
||||
|
@ -84,7 +90,7 @@
|
|||
slotName: 'paramType',
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
hasRequired: true,
|
||||
hasRequired: false,
|
||||
columnSelectorDisabled: true,
|
||||
typeOptions: [
|
||||
{
|
||||
|
@ -162,7 +168,9 @@
|
|||
if (!searchValue.value) {
|
||||
innerParams.value = [...backupParams.value];
|
||||
} else {
|
||||
const result = backupParams.value.filter((item) => item.key.includes(searchValue.value));
|
||||
const result = backupParams.value.filter(
|
||||
(item) => item.key.includes(searchValue.value) || item.tags.includes(searchValue.value)
|
||||
);
|
||||
innerParams.value = [...result];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,18 +23,26 @@
|
|||
t('project.environmental.nonClose')
|
||||
}}</span></a-divider
|
||||
>
|
||||
<div v-for="element in couldCloseColumn" :key="element.value" class="column-drag-item">
|
||||
<div class="flex w-[90%] items-center">
|
||||
<span class="ml-[8px]">{{ t(element.label) }}</span>
|
||||
<VueDraggable
|
||||
v-model="couldCloseColumn"
|
||||
class="ms-assertion-body-left"
|
||||
ghost-class="ghost"
|
||||
handle=".column-drag-item"
|
||||
>
|
||||
<div v-for="element in couldCloseColumn" :key="element.value" class="column-drag-item">
|
||||
<div class="flex w-[90%] items-center">
|
||||
<span class="ml-[8px]">{{ t(element.label) }}</span>
|
||||
</div>
|
||||
<a-switch v-model="element.isShow" size="small" type="line" @change="handleSwitchChange" />
|
||||
</div>
|
||||
<a-switch v-model="element.isShow" size="small" type="line" @change="handleSwitchChange" />
|
||||
</div>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineModel, onBeforeMount, ref } from 'vue';
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
t('project.environmental.addHttp')
|
||||
}}</a-button>
|
||||
<div class="flex flex-row gap-[8px]">
|
||||
<a-input-number v-model:model-value="form.requestTimeout" class="w-[180px]">
|
||||
<a-input-number v-model:model-value="form.requestTimeout" :min="0" class="w-[180px]">
|
||||
<template #prefix>
|
||||
<span class="text-[var(--color-text-3)]">{{ t('project.environmental.http.linkTimeOut') }}</span>
|
||||
</template>
|
||||
</a-input-number>
|
||||
<a-input-number v-model:model-value="form.responseTimeout" class="w-[180px]">
|
||||
<a-input-number v-model:model-value="form.responseTimeout" :min="0" class="w-[180px]">
|
||||
<template #prefix>
|
||||
<span class="text-[var(--color-text-3)]">{{ t('project.environmental.http.linkTimeOut') }}</span>
|
||||
<span class="text-[var(--color-text-3)]">{{ t('project.environmental.http.resTimeOut') }}</span>
|
||||
</template>
|
||||
</a-input-number>
|
||||
<!-- TOTO 第一个版本不做 -->
|
||||
|
@ -130,11 +130,11 @@
|
|||
];
|
||||
await tableStore.initColumn(TableKeyEnum.PROJECT_MANAGEMENT_ENV_ENV_HTTP, columns);
|
||||
const { propsRes, propsEvent } = useTable(undefined, {
|
||||
tableKey: TableKeyEnum.PROJECT_MANAGEMENT_ENV_ENV_HTTP,
|
||||
columns,
|
||||
scroll: { x: '100%' },
|
||||
selectable: false,
|
||||
noDisable: true,
|
||||
showSetting: true,
|
||||
showSetting: false,
|
||||
showPagination: false,
|
||||
enableDrag: true,
|
||||
showMode: false,
|
||||
|
@ -255,12 +255,30 @@
|
|||
}
|
||||
await initModuleTree();
|
||||
|
||||
const OPERATOR_MAP = [
|
||||
{
|
||||
value: 'CONTAINS',
|
||||
label: '包含',
|
||||
},
|
||||
{
|
||||
value: 'EQUALS',
|
||||
label: '等于',
|
||||
},
|
||||
];
|
||||
|
||||
function getCondition(condition: string) {
|
||||
return OPERATOR_MAP.find((item) => item.value === condition)?.label;
|
||||
}
|
||||
|
||||
function getModuleName(record: HttpForm) {
|
||||
if (record.type === 'MODULE') {
|
||||
const moduleIds: string[] = record.moduleMatchRule.modules.map((item) => item.moduleId);
|
||||
const result = findNodeNames(moduleTree.value, moduleIds);
|
||||
return result.join(',');
|
||||
}
|
||||
if (record.type === 'PATH') {
|
||||
return `${getCondition(record.pathMatchRule.condition)}${record.pathMatchRule.path}`;
|
||||
}
|
||||
return '-';
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<RequestHeader v-model:params="form.headers" />
|
||||
<RequestHeader v-model:params="form.headers" :no-param-type="true" />
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="innerVisible"
|
||||
class="ms-modal-form ms-modal-large"
|
||||
title-align="start"
|
||||
:ok-text="t('system.userGroup.add')"
|
||||
unmount-on-close
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template #title> 域名列表 </template>
|
||||
<div>
|
||||
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
|
||||
<template #type="{ record }">
|
||||
<span>{{ getEnableScope(record.type) }}</span>
|
||||
</template>
|
||||
<template #moduleValue="{ record }">
|
||||
<a-tooltip :content="getModuleName(record)" position="left">
|
||||
<span class="one-line-text max-w-[300px]">{{ getModuleName(record) }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button type="secondary" @click="handleCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleCancel">
|
||||
{{ t('common.confirm') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
|
||||
import { getEnvModules } from '@/api/modules/api-test/management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore, useTableStore } from '@/store';
|
||||
import { findNodeNames } from '@/utils';
|
||||
|
||||
import type { ModuleTreeNode } from '@/models/common';
|
||||
import { HttpForm } from '@/models/projectManagement/environmental';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
data: HttpForm[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean);
|
||||
}>();
|
||||
|
||||
const innerVisible = useVModel(props, 'visible', emit);
|
||||
|
||||
function handleCancel() {
|
||||
innerVisible.value = false;
|
||||
}
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.environmental.http.host',
|
||||
dataIndex: 'hostname',
|
||||
slotName: 'hostname',
|
||||
showTooltip: true,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.http.desc',
|
||||
dataIndex: 'description',
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.http.enableScope',
|
||||
dataIndex: 'type',
|
||||
slotName: 'type',
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.http.value',
|
||||
dataIndex: 'value',
|
||||
slotName: 'moduleValue',
|
||||
showTooltip: false,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent } = useTable(undefined, {
|
||||
columns,
|
||||
scroll: { x: '100%' },
|
||||
selectable: false,
|
||||
noDisable: true,
|
||||
showSetting: false,
|
||||
showPagination: false,
|
||||
enableDrag: false,
|
||||
showMode: false,
|
||||
heightUsed: 644,
|
||||
debug: true,
|
||||
});
|
||||
|
||||
function getEnableScope(type: string) {
|
||||
switch (type) {
|
||||
case 'NONE':
|
||||
return t('project.environmental.http.none');
|
||||
case 'MODULE':
|
||||
return t('project.environmental.http.module');
|
||||
case 'PATH':
|
||||
return t('project.environmental.http.path');
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const moduleTree = ref<ModuleTreeNode[]>([]);
|
||||
async function initModuleTree() {
|
||||
try {
|
||||
const res = await getEnvModules({
|
||||
projectId: appStore.currentProjectId,
|
||||
});
|
||||
moduleTree.value = res.moduleTree;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
const OPERATOR_MAP = [
|
||||
{
|
||||
value: 'CONTAINS',
|
||||
label: '包含',
|
||||
},
|
||||
{
|
||||
value: 'EQUALS',
|
||||
label: '等于',
|
||||
},
|
||||
];
|
||||
|
||||
function getCondition(condition: string) {
|
||||
return OPERATOR_MAP.find((item) => item.value === condition)?.label;
|
||||
}
|
||||
function getModuleName(record: HttpForm) {
|
||||
if (record.type === 'MODULE') {
|
||||
const moduleIds: string[] = record.moduleMatchRule.modules.map((item) => item.moduleId);
|
||||
const result = findNodeNames(moduleTree.value, moduleIds);
|
||||
return result.join(',');
|
||||
}
|
||||
if (record.type === 'PATH') {
|
||||
return `${getCondition(record.pathMatchRule.condition)}${record.pathMatchRule.path}`;
|
||||
}
|
||||
return '-';
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(val) => {
|
||||
if (val) {
|
||||
propsRes.value.data = val;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<div class="font-medium">{{ t('apiTestDebug.header') }}</div>
|
||||
<batchAddKeyVal :params="innerParams" @apply="handleBatchParamApply" />
|
||||
<batchAddKeyVal :no-param-type="props.noParamType" :params="innerParams" @apply="handleBatchParamApply" />
|
||||
</div>
|
||||
<paramsTable
|
||||
v-model:params="innerParams"
|
||||
|
@ -30,6 +30,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
params: any[];
|
||||
noParamType?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', value: any[]): void;
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
'project.environmental.httpNoWarning': 'No warning',
|
||||
'project.environmental.addHttp': 'Add HTTP',
|
||||
'project.environmental.http.linkTimeOut': 'Link Timeout (ms):',
|
||||
'project.environmental.http.timeTimeOut': 'Timeout Time (ms):',
|
||||
'project.environmental.http.resTimeOut': 'Response timeout (ms) :',
|
||||
'project.environmental.http.authType': 'Authentication Type:',
|
||||
'project.environmental.http.host': 'Host',
|
||||
'project.environmental.http.desc': 'Description',
|
||||
|
@ -96,11 +96,11 @@ export default {
|
|||
'project.environmental.postScriptBefore': 'Post script front',
|
||||
'project.environmental.postScriptAfter': 'After the script',
|
||||
'project.environmental.http.preTextPreTip':
|
||||
'Before pre script, environment scripts executed before buy scripts in the request;',
|
||||
'Before the pre-script, the script in the environment is executed before the requested pre-script is executed.',
|
||||
'project.environmental.http.preTextPostTip':
|
||||
'After the preset script, the script in the environment is executed after the requested preset script is executed;',
|
||||
'project.environmental.http.postTextPreTip':
|
||||
'The script in the environment is executed before the requested script is executed.',
|
||||
'The script in the environment is executed before the requested postscript is executed;',
|
||||
'project.environmental.http.postTextPostTip':
|
||||
'After rear script, environment a prerequisite for the script in the request after the script execution;',
|
||||
'project.environmental.preOrPost.ignoreProtocols': 'Ignore request:',
|
||||
|
@ -111,4 +111,5 @@ export default {
|
|||
'project.environmental.env.selectableTitle': 'Optional Environments',
|
||||
'project.environmental.env.systemTitle': 'environment',
|
||||
'project.environmental.env.selectedTitle': 'Selected environment',
|
||||
'project.environmental.env.constantBatchAddTip': 'Only supports batch adding const type',
|
||||
};
|
||||
|
|
|
@ -45,8 +45,8 @@ export default {
|
|||
'project.environmental.httpTitle': '当满足多个启用条件时,按从上到下的顺序匹配',
|
||||
'project.environmental.httpNoWarning': '不在提醒',
|
||||
'project.environmental.addHttp': '添加 HTTP',
|
||||
'project.environmental.http.linkTimeOut': '链接超时(ms):',
|
||||
'project.environmental.http.timeTimeOut': '超时时间(ms):',
|
||||
'project.environmental.http.linkTimeOut': '连接超时(ms):',
|
||||
'project.environmental.http.resTimeOut': '响应超时(ms):',
|
||||
'project.environmental.http.authType': '认证方式:',
|
||||
'project.environmental.http.host': '域名',
|
||||
'project.environmental.http.desc': '描述',
|
||||
|
@ -109,9 +109,9 @@ export default {
|
|||
'project.environmental.preScriptAfter': '前置脚本后',
|
||||
'project.environmental.postScriptBefore': '后置脚本前',
|
||||
'project.environmental.postScriptAfter': '后置脚本后',
|
||||
'project.environmental.http.preTextPreTip': '前置脚本前,环境中脚本在请求的置脚本执行前执行;',
|
||||
'project.environmental.http.preTextPreTip': '前置脚本前,环境中脚本在请求的前置脚本执行前执行;',
|
||||
'project.environmental.http.preTextPostTip': '前置置脚本后,环境中脚本在请求的前置脚本执行后执行;',
|
||||
'project.environmental.http.postTextPreTip': '后置脚本前,环境中脚本在请求的置脚本执行前执行;',
|
||||
'project.environmental.http.postTextPreTip': '后置脚本前,环境中脚本在请求的后置脚本执行前执行;',
|
||||
'project.environmental.http.postTextPostTip': '后置脚本后,环境中脚本在请求的前置脚本执行后执行;',
|
||||
'project.environmental.preOrPost.ignoreProtocols': '忽略请求:',
|
||||
'project.environmental.preOrPost.pre': '脚本前',
|
||||
|
@ -122,4 +122,5 @@ export default {
|
|||
'project.environmental.env.selectableTitle': '可选环境',
|
||||
'project.environmental.env.systemTitle': '环境',
|
||||
'project.environmental.env.selectedTitle': '已选环境',
|
||||
'project.environmental.env.constantBatchAddTip': '只支持常量类型批量添加',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue