feat(接口测试): 接口测试diff对比同步以及状态接口联调部分

This commit is contained in:
xinxin.wu 2024-08-07 11:06:23 +08:00 committed by 刘瑞斌
parent 5bdbb92b14
commit a7a50d1516
20 changed files with 354 additions and 89 deletions

View File

@ -20,7 +20,9 @@ import {
BatchRecoverCaseUrl,
BatchUpdateDefinitionUrl,
CasePageUrl,
caseTableBatchSyncUrl,
CheckDefinitionScheduleUrl,
clearThisChangeUrl,
ConvertJsonSchemaToJsonUrl,
CopyMockUrl,
DebugCaseUrl,
@ -35,6 +37,7 @@ import {
DeleteModuleUrl,
DeleteRecycleApiUrl,
DeleteRecycleCaseUrl,
diffDataUrl,
ExecuteCaseUrl,
ExportDefinitionUrl,
GetCaseDetailUrl,
@ -54,6 +57,7 @@ import {
GetPoolOptionUrl,
GetTrashModuleCountUrl,
GetTrashModuleTreeUrl,
ignoreEveryTimeApiChangeUrl,
ImportDefinitionUrl,
JsonSchemaAutoGenerateUrl,
MockDetailUrl,
@ -137,6 +141,7 @@ import {
MoveModules,
TransferFileParams,
} from '@/models/common';
import { TableQueryParams } from '@/models/common';
import { ResourcePoolItem } from '@/models/setting/resourcePool';
// 更新模块
@ -313,6 +318,22 @@ export function convertJsonSchemaToJson(data: JsonSchema) {
export function jsonSchemaAutoGenerate(data: JsonSchema) {
return MSR.post({ url: JsonSchemaAutoGenerateUrl, data });
}
// 接口定义-用例接口对比-清除本次变更
export function clearThisChange(id: string) {
return MSR.get({ url: `${clearThisChangeUrl}/${id}` });
}
// 接口定义-用例接口对比-忽略每次变更
export function ignoreEveryTimeChange(id: string, ignore: boolean) {
return MSR.get({ url: `${ignoreEveryTimeApiChangeUrl}/${id}`, params: { ignore } });
}
// 接口测试-接口管理-接口用例-批量同步编辑
export function caseTableBatchSync(data: TableQueryParams) {
return MSR.post({ url: caseTableBatchSyncUrl, data });
}
// // 接口测试-接口用例-定义对比用例
export function diffDataRequest(id: string) {
return MSR.get({ url: `${diffDataUrl}/${id}` });
}
/**
* Mock

View File

@ -38,6 +38,10 @@ export const RecoverOperationHistoryUrl = '/api/definition/operation-history/rec
export const DefinitionReferenceUrl = '/api/definition/get-reference'; // 获取接口引用关系
export const ConvertJsonSchemaToJsonUrl = '/api/definition/json-schema/preview'; // 将json-schema转换为 json 数据
export const JsonSchemaAutoGenerateUrl = '/api/definition/json-schema/auto-generate'; // 将json-schema转换为 json 数据
export const clearThisChangeUrl = '/api/case/api-change/clear'; // 接口定义-变更对比-清除本次变更
export const caseTableBatchSyncUrl = '/api/case/batch/api-change/sync'; // 接口测试-接口管理-接口用例-批量同步
export const ignoreEveryTimeApiChangeUrl = '/api/case/api-change/ignore'; // 接口测试-接口用例-忽略每次接口变更
export const diffDataUrl = '/api/case/api/compare'; // 接口测试-接口用例-定义对比用例
/**
* Mock

View File

@ -549,7 +549,7 @@
return totalWidth + tablePadding;
};
//
// TODO
const getMaxRowTagWidth = (rows: TableData[], dataIndex: string) => {
const allTags = ((rows as TableData) || []).map((row: TableData) => row[dataIndex] || []);

View File

@ -328,6 +328,8 @@ export interface ApiCaseDetail extends ExecuteRequestParams {
deleteTime: number;
deleteUser: string;
deleteName: string;
apiChange: boolean; // 接口定义参数变更标识
inconsistentWithApi: boolean; // 与接口定义不一致
}
// 批量操作参数
export interface ApiCaseBatchParams extends BatchApiParams {
@ -401,3 +403,19 @@ export interface ApiCaseExecuteHistoryItem {
triggerMode: string;
deleted: boolean;
}
export interface syncItem {
header: boolean;
body: boolean;
query: boolean;
rest: boolean;
}
// 批量同步
export interface batchSyncForm {
notificationConfig: {
apiCreator: boolean;
scenarioCreator: boolean;
};
// 同步项目
syncItems: syncItem;
deleteRedundantParam: boolean;
}

View File

@ -1002,7 +1002,6 @@
() => requestMethodsOptions.value,
() => {
initFilterColumn();
apiTableRef.value.initColumn(columns);
}
);
</script>

View File

@ -12,19 +12,12 @@
<icon-right :size="10" class="block" />
</div>
</div>
<MsTag
v-if="props.detail.inconsistentWithApi"
class="cursor-pointer"
type="warning"
theme="light"
:tooltip-disabled="true"
@click.stop="showDiffDrawer"
>
<template #icon>
<MsIcon type="icon-icon_warning_colorful" size="16" />
</template>
<span class="ml-[8px]"> {{ statusText }}</span>
</MsTag>
<ApiChangeTag
:ignore-api-change="props.detail.ignoreApiChange"
:ignore-api-diff="props.detail.ignoreApiDiff"
:inconsistent-with-api="props.detail.inconsistentWithApi"
@show-diff="showDiffDrawer"
/>
</div>
</template>
<div class="detail-collapse-item">
@ -435,10 +428,10 @@
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsJsonSchema from '@/components/pure/ms-json-schema/index.vue';
import { parseSchemaToJsonSchemaTableData } from '@/components/pure/ms-json-schema/utils';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import { ResponseItem } from '@/views/api-test/components/requestComposition/response/edit.vue';
import responseCodeTimeSize from '@/views/api-test/components/requestComposition/response/responseCodeTimeSize.vue';
import Result from '@/views/api-test/components/requestComposition/response/result.vue';
import ApiChangeTag from '@/views/api-test/management/components/management/case/apiChangeTag.vue';
import { getPluginScript } from '@/api/modules/api-test/common';
import { useI18n } from '@/hooks/useI18n';
@ -898,13 +891,6 @@
}
}
const statusText = computed(() => {
if (props.detail.inconsistentWithApi) {
return t('case.definitionInconsistent');
}
// TODO
});
// diff
function showDiffDrawer() {
emit('showDiff');

View File

@ -87,6 +87,8 @@
:active-api-case-id="activeApiCaseId"
:active-defined-id="activeDefinedId"
@close="closeDifferent"
@clear-this-change="clearThisChangeHandler"
@sync="syncHandler"
/>
</div>
<tab-case-dependency v-else-if="activeKey === 'reference'" :source-id="caseDetail.id" />
@ -396,11 +398,20 @@
}
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
const caseId = ref<string>(route.query.id as string);
//
async function clearThisChangeHandler() {
getCaseDetailInfo(caseId.value);
}
function syncHandler(id: string) {
// TODO
createAndEditCaseDrawerRef.value?.open(id, caseDetail.value, false);
}
onBeforeMount(() => {
initProtocolList();
const caseId = route.query.id;
getCaseDetailInfo(caseId as string);
getCaseDetailInfo(caseId.value);
});
</script>

View File

@ -0,0 +1,58 @@
<template>
<MsTag
v-if="props.inconsistentWithApi"
class="cursor-pointer font-normal"
:type="props.ignoreApiDiff ? 'default' : 'warning'"
theme="light"
:tooltip-disabled="true"
max-width="160px"
@click.stop="showDiffDrawer"
>
<template #icon>
<div class="!text-[var(--color-text-4)]">
<MsIcon v-if="props.ignoreApiDiff" type="icon-icon_warning_filled" size="16" />
<MsIcon v-else class="!text-[var(--color-text-4)]" type="icon-icon_warning_colorful" size="16" />
</div>
</template>
<span class="ml-[4px]"> {{ statusText }}</span>
</MsTag>
</template>
<script setup lang="ts">
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const props = defineProps<{
inconsistentWithApi?: boolean;
ignoreApiDiff?: boolean;
ignoreApiChange?: boolean;
}>();
const emit = defineEmits<{
(e: 'showDiff'): void;
}>();
function showDiffDrawer() {
emit('showDiff');
}
const statusText = computed(() => {
//
if (props.ignoreApiChange) {
return t('case.eachHasBeenIgnored');
}
//
if (props.ignoreApiDiff) {
return t('case.haveIgnoredTheChange');
}
//
if (props.inconsistentWithApi) {
return t('case.definitionInconsistent');
}
});
</script>
<style scoped></style>

View File

@ -73,6 +73,8 @@
:active-api-case-id="activeApiCaseId"
:active-defined-id="activeDefinedId"
@close="closeDifferent"
@clear-this-change="clearThisChangeHandler"
@sync="syncHandler"
/>
</a-tab-pane>
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
@ -132,9 +134,11 @@
isDrawer?: boolean; //
detail: RequestParam;
}>();
const emit = defineEmits<{
(e: 'updateFollow'): void;
(e: 'deleteCase', id: string): void;
(e: 'loadCase', id: string): void;
}>();
const appStore = useAppStore();
@ -342,6 +346,16 @@
activeDefinedId.value = '';
}
//
async function clearThisChangeHandler() {
emit('loadCase', props.detail.id as string);
}
function syncHandler(id: string) {
// TODO
createAndEditCaseDrawerRef.value?.open(id, caseDetail.value, false);
}
watch(
() => props.detail,
() => {

View File

@ -275,6 +275,7 @@
</a-modal>
<createAndEditCaseDrawer
ref="createAndEditCaseDrawerRef"
v-model:visible="showCaseVisible"
:api-detail="apiDetail"
@load-case="loadCaseListAndResetSelector()"
@show-diff="showDifferences"
@ -300,13 +301,21 @@
<!-- 执行结果抽屉 -->
<caseAndScenarioReportDrawer v-model:visible="showExecuteResult" :report-id="activeReportId" />
<!-- 同步抽屉 -->
<SyncModal v-model:visible="showSyncModal" :batch-params="batchParams" />
<SyncModal
ref="syncModalRef"
v-model:visible="showSyncModal"
:loading="syncLoading"
:batch-params="batchParams"
@batch-sync="handleBatchSync"
/>
<!-- diff对比抽屉 -->
<DifferentDrawer
v-model:visible="showDifferentDrawer"
:active-api-case-id="activeApiCaseId"
:active-defined-id="activeDefinedId"
@close="closeDifferent"
@clear-this-change="handleClearThisChange"
@sync="syncHandler"
/>
</template>
@ -339,6 +348,7 @@
batchDeleteCase,
batchEditCase,
batchExecuteCase,
caseTableBatchSync,
deleteCase,
dragSort,
getCaseDetail,
@ -353,6 +363,7 @@
import { characterLimit, operationWidth } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import type { batchSyncForm } from '@/models/apiTest/management';
import { ApiCaseDetail } from '@/models/apiTest/management';
import { DragSortParams } from '@/models/common';
import { RequestCaseStatus } from '@/enums/apiEnum';
@ -860,7 +871,6 @@
const batchConditionParams = ref<any>();
const showSyncModal = ref<boolean>(false);
// TODO
function syncParams() {
showSyncModal.value = true;
}
@ -975,7 +985,6 @@
const activeDefinedId = ref<string>('');
const showDifferentDrawer = ref<boolean>(false);
// TODO
async function showDifferences(record: ApiCaseDetail) {
activeApiCaseId.value = record.id;
activeDefinedId.value = record.apiDefinitionId;
@ -987,6 +996,48 @@
activeDefinedId.value = '';
}
const syncLoading = ref<boolean>(false);
const syncModalRef = ref<InstanceType<typeof SyncModal>>();
//
async function handleBatchSync(syncForm: batchSyncForm) {
try {
syncLoading.value = true;
const selectModules = await getModuleIds();
const params = await genBatchConditionParams();
await caseTableBatchSync({
selectIds: batchParams.value?.selectedIds || [],
selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
...params,
...syncForm,
moduleIds: selectModules,
});
Message.success(t('bugManagement.syncSuccess'));
syncModalRef.value?.resetForm();
resetSelector();
loadCaseListAndResetSelector();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
syncLoading.value = false;
}
}
const showCaseVisible = ref(false);
//
async function handleClearThisChange() {
await loadCaseList();
await getCaseDetailInfo(activeApiCaseId.value);
if (showCaseVisible.value) {
createAndEditCaseDrawerRef.value?.open(caseDetail.value.apiDefinitionId, caseDetail.value as RequestParam, false);
}
}
//
function syncHandler(definedId: string) {
// TODO
createAndEditCaseDrawerRef.value?.open(definedId, caseDetail.value as RequestParam, false);
}
defineExpose({
loadCaseList,
});

View File

@ -71,20 +71,12 @@
</a-form>
<div class="flex items-center justify-between">
<div class="px-[16px] font-medium">{{ t('apiTestManagement.requestParams') }}</div>
<!-- 与定义不一致 TODO 等待联调 -->
<MsTag
v-if="detailForm.inconsistentWithApi"
class="cursor-pointer"
type="warning"
theme="light"
:tooltip-disabled="true"
@click="showDiffDrawer"
>
<template #icon>
<MsIcon type="icon-icon_warning_colorful" size="16" />
</template>
<span class="ml-[8px]"> {{ t('case.definitionInconsistent') }}</span>
</MsTag>
<ApiChangeTag
:ignore-api-change="detailForm.ignoreApiChange"
:ignore-api-diff="detailForm.ignoreApiDiff"
:inconsistent-with-api="detailForm.inconsistentWithApi"
@show-diff="showDiffDrawer"
/>
</div>
<requestComposition
@ -111,7 +103,6 @@
import MsDetailCard, { type Description } from '@/components/pure/ms-detail-card/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
@ -120,6 +111,7 @@
import apiStatus from '@/views/api-test/components/apiStatus.vue';
import executeButton from '@/views/api-test/components/executeButton.vue';
import requestComposition, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import ApiChangeTag from '@/views/api-test/management/components/management/case/apiChangeTag.vue';
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
import {
@ -153,7 +145,10 @@
const { t } = useI18n();
const appStore = useAppStore();
const innerVisible = ref(false);
const innerVisible = defineModel<boolean>('visible', {
default: false,
});
const drawerLoading = ref(false);
const apiDefinitionId = ref('');

View File

@ -14,7 +14,7 @@
<div>{{ t('case.apiAndCaseDiff') }}</div>
<div class="flex items-center text-[14px]">
<div class="-mt-[2px] mr-[8px]"> {{ t('case.syncItem') }}</div>
<a-checkbox-group v-model="form.checkType">
<a-checkbox-group v-model="checkType">
<a-checkbox v-for="item of checkList" :key="item.value" :value="item.value">
<div class="flex items-center"
>{{ item.label }}
@ -31,17 +31,21 @@
</a-checkbox>
</a-checkbox-group>
<a-divider direction="vertical" :margin="0" class="!mr-[8px]"></a-divider>
<a-switch v-model:model-value="form.ignoreUpdate" size="small" />
<a-switch
v-model:model-value="form.ignoreApiChange"
:before-change="(val) => changeIgnore(val)"
size="small"
/>
<div class="ml-[8px]">{{ t('case.ignoreAllChange') }}</div>
<a-divider direction="vertical" :margin="8"></a-divider>
<a-switch v-model:model-value="form.deleteParams" size="small" />
<a-switch v-model:model-value="form.deleteRedundantParam" size="small" />
<div class="ml-[8px] font-normal text-[var(--color-text-1)]">{{ t('case.deleteNotCorrespondValue') }}</div>
<a-divider direction="vertical" :margin="0" class="!ml-[8px]"></a-divider>
<a-button class="mx-[12px]" type="secondary" @click="cancel">{{ t('common.cancel') }}</a-button>
<a-button class="mr-[12px]" type="outline">
{{ t('case.ignoreAllChange') }}
<a-button class="mr-[12px]" type="outline" @click="clearThisChangeHandler">
{{ t('case.ignoreThisChange') }}
</a-button>
<a-button type="primary" :loading="syncLoading" :disabled="!form.checkType.length" @click="confirmBatchSync">
<a-button type="primary" :loading="syncLoading" :disabled="!checkType.length" @click="confirmSync">
{{ t('case.apiSyncChange') }}
</a-button>
</div>
@ -87,6 +91,7 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
@ -94,10 +99,17 @@
import DiffItem from './diffItem.vue';
import DiffRequestBody from './diffRequestBody.vue';
import { getCaseDetail, getDefinitionDetail } from '@/api/modules/api-test/management';
import {
clearThisChange,
diffDataRequest,
getCaseDetail,
getDefinitionDetail,
ignoreEveryTimeChange,
} from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import { EnableKeyValueParam, ExecuteRequestCommonParam } from '@/models/apiTest/common';
import type { syncItem } from '@/models/apiTest/management';
import { ApiDefinitionDetail } from '@/models/apiTest/management';
import { RequestBodyFormat, RequestComposition } from '@/enums/apiEnum';
@ -113,6 +125,8 @@
const emit = defineEmits<{
(e: 'close'): void;
(e: 'clearThisChange'): void;
(e: 'sync', activeDefinedId: string): void;
}>();
const showDiffVisible = defineModel<boolean>('visible', {
@ -140,18 +154,21 @@
]);
const initForm = {
deleteParams: false,
checkType: [],
deleteRedundantParam: false,
syncItems: {
header: false,
body: false,
query: false,
rest: false,
},
noticeApiCaseCreator: true,
noticeApiScenarioCreator: true,
ignoreUpdate: false,
ignoreUpdateType: ['THIS_TIME'],
ignoreApiChange: false,
};
const form = ref({ ...initForm });
const checkType = ref([]);
//
function changeIgnoreType() {}
const form = ref({ ...initForm });
function cancel() {
showDiffVisible.value = false;
@ -159,7 +176,16 @@
}
const syncLoading = ref<boolean>(false);
//
function confirmBatchSync() {}
function confirmSync() {
//
checkType.value.forEach((e: any) => {
const key = e.toLowerCase() as keyof syncItem;
form.value.syncItems[key] = true;
});
emit('sync', props.activeDefinedId);
showDiffVisible.value = false;
}
const defaultCaseParams = inject<RequestParam>('defaultCaseParams');
const caseDetail = ref<Record<string, any>>({});
@ -297,12 +323,12 @@
caseDetail.value = {
...cloneDeep(defaultCaseParams as RequestParam),
...({
...res.request,
...res,
url: res.path,
...parseRequestBodyResult,
} as Partial<TabItem>),
};
form.value.ignoreApiChange = caseDetail.value.ignoreApiChange;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -313,24 +339,68 @@
try {
const detail = await getDefinitionDetail(apiDefinitionId);
apiDetailInfo.value = detail as ApiDefinitionDetail;
apiDefinedRequest.value = detail.request as unknown as RequestParam;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
async function getDiffDataRequest(activeApiCaseId: string) {
try {
const result = await diffDataRequest(activeApiCaseId);
const { caseRequest, apiRequest } = result;
caseDetail.value = {
...caseDetail.value,
...caseRequest,
num: caseDetail.value.num,
};
apiDefinedRequest.value = apiRequest;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
const loading = ref<boolean>(false);
async function getRequestDetail(definedId: string, apiCaseId: string) {
loading.value = true;
try {
await Promise.all([getApiDetail(definedId), getCaseDetailInfo(apiCaseId)]);
await getDiffDataRequest(props.activeApiCaseId);
processData();
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
} finally {
loading.value = false;
}
}
//
async function clearThisChangeHandler() {
if (props.activeApiCaseId) {
try {
await clearThisChange(props.activeApiCaseId);
getRequestDetail(props.activeDefinedId, props.activeApiCaseId);
emit('clearThisChange');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
}
//
async function changeIgnore(newValue: string | number | boolean) {
try {
await ignoreEveryTimeChange(props.activeApiCaseId, newValue as boolean);
Message.success(newValue ? t('case.eachHasBeenIgnored') : t('case.eachHasBeenIgnoredClosed'));
getRequestDetail(props.activeDefinedId, props.activeApiCaseId);
emit('clearThisChange');
return false;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
watchEffect(() => {
if (props.activeDefinedId && props.activeApiCaseId) {

View File

@ -4,7 +4,7 @@
title-align="start"
class="ms-modal-upload ms-modal-medium"
:width="600"
@close="cancel"
@close="resetForm"
>
<template #title>
{{ t('case.apiSyncChange') }}
@ -21,7 +21,7 @@
<div class="mb-[8px]">
{{ t('case.syncItem') }}
</div>
<a-checkbox-group v-model="form.checkType">
<a-checkbox-group v-model="checkType">
<a-checkbox v-for="item of checkList" :key="item.value" :value="item.value">
<div class="flex items-center">
{{ item.label }}
@ -37,7 +37,7 @@
</a-checkbox>
</a-checkbox-group>
<div class="my-[16px] flex items-center">
<a-switch v-model:model-value="form.deleteParams" size="small" />
<a-switch v-model:model-value="form.deleteRedundantParam" size="small" />
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('case.deleteNotCorrespondValue') }}</div>
</div>
@ -53,17 +53,17 @@
</a-tooltip>
</div>
<div class="my-[16px] flex items-center">
<a-switch v-model:model-value="form.noticeApiCaseCreator" size="small" />
<a-switch v-model:model-value="form.notificationConfig.apiCreator" size="small" />
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('case.NoticeApiCaseCreator') }}</div>
</div>
<div class="my-[16px] flex items-center">
<a-switch v-model:model-value="form.noticeApiScenarioCreator" size="small" />
<a-switch v-model:model-value="form.notificationConfig.scenarioCreator" size="small" />
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('case.NoticeApiScenarioCreator') }}</div>
</div>
<template #footer>
<a-button type="secondary" @click="cancel">{{ t('common.cancel') }}</a-button>
<a-button type="primary" :loading="syncLoading" :disabled="!form.checkType.length" @click="confirmBatchSync">
<a-button type="secondary" @click="resetForm">{{ t('common.cancel') }}</a-button>
<a-button type="primary" :loading="props.loading" :disabled="!checkType.length" @click="confirmBatchSync">
{{ t('case.apiSyncChange') }}
</a-button>
</template>
@ -72,31 +72,47 @@
<script setup lang="ts">
import { ref } from 'vue';
import { cloneDeep } from 'lodash-es';
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
import { useI18n } from '@/hooks/useI18n';
import type { batchSyncForm, syncItem } from '@/models/apiTest/management';
import { RequestComposition } from '@/enums/apiEnum';
const { t } = useI18n();
const props = defineProps<{
batchParams: BatchActionQueryParams;
loading: boolean;
}>();
const emit = defineEmits<{
(e: 'batchSync', form: batchSyncForm): void;
}>();
const showBatchSyncModal = defineModel<boolean>('visible', {
required: true,
});
const initForm = {
deleteParams: false,
checkType: [],
noticeApiCaseCreator: true,
noticeApiScenarioCreator: true,
const initForm: batchSyncForm = {
notificationConfig: {
apiCreator: false,
scenarioCreator: false,
},
//
syncItems: {
header: false,
body: false,
query: false,
rest: false,
},
deleteRedundantParam: false,
};
const form = ref({ ...initForm });
const form = ref<batchSyncForm>(cloneDeep(initForm));
const checkType = ref([]);
const checkList = ref([
{
@ -118,13 +134,24 @@
},
]);
const syncLoading = ref<boolean>(false);
function cancel() {
function resetForm() {
form.value = cloneDeep(initForm);
checkType.value = [];
showBatchSyncModal.value = false;
}
// TODO
function confirmBatchSync() {}
function confirmBatchSync() {
checkType.value.forEach((e: string) => {
const key = e.toLowerCase() as keyof syncItem;
form.value.syncItems[key] = true;
});
emit('batchSync', form.value);
}
defineExpose({
resetForm,
});
</script>
<style scoped></style>

View File

@ -228,6 +228,7 @@ export default {
'case.definitionInconsistent': 'Inconsistent with the definition',
'case.haveIgnoredTheChange': 'Have ignored the change',
'case.eachHasBeenIgnored': 'Each change difference has been ignored',
'case.eachHasBeenIgnoredClosed': 'Closed Ignore each change difference',
'case.apiCaseList': 'List',
'case.apiCaseDetail': 'Use case details',
'case.belongingApi': 'Belonging interface',

View File

@ -215,6 +215,7 @@ export default {
'case.definitionInconsistent': '与定义不一致',
'case.haveIgnoredTheChange': '已忽略本次变更差异',
'case.eachHasBeenIgnored': '已忽略每次变更差异',
'case.eachHasBeenIgnoredClosed': '已关闭忽略每次变更差异',
'case.apiCaseList': '列表',
'case.apiCaseDetail': '用例详情',
'case.belongingApi': '所属接口',

View File

@ -52,7 +52,7 @@
</span>
</template>
<template #name="{ record }">
<div class="one-line-text">{{ characterLimit(record.name) }}</div>
<div class="one-line-text">{{ record.name }}</div>
</template>
<template #caseLevel="{ record }">
<a-select

View File

@ -29,8 +29,6 @@
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
@ -61,7 +59,7 @@
(e: 'handleSummary', content: string): void;
}>();
const innerSummary = useVModel(props, 'richText', emit);
const innerSummary = ref(props.richText);
function handleCancel() {
emit('cancel');
@ -135,6 +133,18 @@
emit('handleClick');
}
}
watch(
() => props.richText,
(val) => {
if (val) {
innerSummary.value = { ...val };
}
},
{
deep: true,
}
);
</script>
<style scoped></style>

View File

@ -173,7 +173,7 @@
:is-preview="props.isPreview"
/>
<Summary
v-else-if="item.value === ReportCardTypeEnum.SUMMARY"
v-if="item.value === ReportCardTypeEnum.SUMMARY"
:rich-text="getContent(item)"
:share-id="shareId"
:is-preview="props.isPreview"

View File

@ -89,9 +89,9 @@ export default {
'testPlan.planForm.repeatCaseTip1': 'Enable: Repeatedly associate the same case',
'testPlan.planForm.repeatCaseTip2': 'Close: Cannot be associated with the same case repeatedly',
'testPlan.planForm.enableAutomaticStatusTip':
'Enable: function cases associated interface/UI/case execution successful performance, function of the use case status automatically updated to success',
'Enable: function case execution successful cases associated interface, function of the use case status automatically updated to success',
'testPlan.planForm.closeAutomaticStatusTip':
'Close: the function of use case execution result is not affected by the interface/UI/performance',
'Close: The result of execution of a functional use case is not affected by the interface',
'testPlan.planForm.passThresholdTip': 'If the pass rate reaches the specified pass threshold, the result is passed',
'testPlan.planForm.pickCases': 'Select cases',
'testPlan.testPlanDetail.executed': 'Executed',

View File

@ -85,9 +85,8 @@ export default {
'testPlan.planForm.selectPlanGroup': '选择计划组',
'testPlan.planForm.repeatCaseTip1': '开启:可重复关联同一个用例',
'testPlan.planForm.repeatCaseTip2': '关闭:不可重复关联同一用例',
'testPlan.planForm.enableAutomaticStatusTip':
'开启:功能用例关联的接口/UI/性能用例执行成功,功能用例的状态自动更新为成功',
'testPlan.planForm.closeAutomaticStatusTip': '关闭:功能用例的执行结果不受接口/UI/性能的影响',
'testPlan.planForm.enableAutomaticStatusTip': '开启:功能用例关联的接口用例执行成功,功能用例的状态自动更新为成功',
'testPlan.planForm.closeAutomaticStatusTip': '关闭:功能用例的执行结果不受接口的影响',
'testPlan.planForm.passThresholdTip': '通过率达到设置的通过阈值时,报告结果为通过',
'testPlan.planForm.pickCases': '选择用例',
'testPlan.testPlanDetail.executed': '已执行',