feat(测试计划): 测试计划表补充遗漏&细节

This commit is contained in:
xinxin.wu 2024-05-08 14:28:16 +08:00 committed by Craftsman
parent a872b43c9d
commit 4358a82355
13 changed files with 632 additions and 196 deletions

View File

@ -28,6 +28,8 @@ export default {
'common.updateSuccess': 'Update success', 'common.updateSuccess': 'Update success',
'common.updateFailed': 'Update failed', 'common.updateFailed': 'Update failed',
'common.deleteConfirm': 'Delete confirm', 'common.deleteConfirm': 'Delete confirm',
'common.deleteConfirmTitle': 'Delete confirm {name} 吗',
'common.archiveConfirmTitle': 'Archive confirm {name} 吗',
'common.deleteSuccess': 'Delete success', 'common.deleteSuccess': 'Delete success',
'common.deleteFailed': 'Delete failed', 'common.deleteFailed': 'Delete failed',
'common.addSuccess': 'Added successfully', 'common.addSuccess': 'Added successfully',
@ -121,6 +123,7 @@ export default {
'common.move': 'Move', 'common.move': 'Move',
'common.moveSuccess': 'Move successful', 'common.moveSuccess': 'Move successful',
'common.batchMove': 'Batch move', 'common.batchMove': 'Batch move',
'common.batchArchiveSuccess': 'Archive successful',
'common.batchCopy': 'Batch copy', 'common.batchCopy': 'Batch copy',
'common.batchCopySuccess': 'Batch copy successful', 'common.batchCopySuccess': 'Batch copy successful',
'common.batchMoveSuccess': 'Batch move successful', 'common.batchMoveSuccess': 'Batch move successful',

View File

@ -30,6 +30,7 @@ export default {
'common.updateFailed': '更新失败', 'common.updateFailed': '更新失败',
'common.deleteConfirm': '确认删除?', 'common.deleteConfirm': '确认删除?',
'common.deleteConfirmTitle': '确认删除 {name} 吗', 'common.deleteConfirmTitle': '确认删除 {name} 吗',
'common.archiveConfirmTitle': '确认归档 {name} 吗',
'common.deleteSuccess': '删除成功', 'common.deleteSuccess': '删除成功',
'common.deleteFailed': '删除失败', 'common.deleteFailed': '删除失败',
'common.addSuccess': '添加成功', 'common.addSuccess': '添加成功',
@ -122,6 +123,7 @@ export default {
'common.move': '移动', 'common.move': '移动',
'common.moveSuccess': '移动成功', 'common.moveSuccess': '移动成功',
'common.batchMove': '批量移动', 'common.batchMove': '批量移动',
'common.batchArchiveSuccess': '归档成功!',
'common.batchCopy': '批量复制', 'common.batchCopy': '批量复制',
'common.batchCopySuccess': '批量复制成功', 'common.batchCopySuccess': '批量复制成功',
'common.batchMoveSuccess': '批量移动成功', 'common.batchMoveSuccess': '批量移动成功',

View File

@ -1,12 +1,14 @@
import { BatchApiParams } from '../common'; import { BatchApiParams } from '../common';
export type planStatusType = 'PREPARED' | 'UNDERWAY' | 'COMPLETED' | 'ARCHIVED';
// 计划分页 // 计划分页
export interface TestPlanItem { export interface TestPlanItem {
id?: string; id?: string;
projectId: string; projectId: string;
num: number; num: number;
name: string; name: string;
status: string; status: planStatusType;
type: string; type: string;
tags: string[]; tags: string[];
schedule: string; // 是否定时 schedule: string; // 是否定时

View File

@ -0,0 +1,101 @@
<template>
<a-modal
v-model:visible="showModalVisible"
class="ms-modal-form ms-modal-no-padding ms-modal-small"
unmount-on-close
title-align="start"
:mask="true"
:mask-closable="false"
@close="cancelHandler"
>
<template #title>
<div class="flex items-center justify-start">
<MsIcon type="icon-icon_close_colorful" class="mr-[8px] text-[rgb(var(--danger-6))]" size="16" />
<div class="text-[var(--color-text-1)]">
{{ t('common.deleteConfirmTitle', { name: characterLimit(record?.name) }) }}
</div>
</div>
</template>
{{ contentTip }}
<template #footer>
<div class="flex justify-end">
<a-button type="secondary" :disabled="confirmLoading" @click="cancelHandler">
{{ t('common.cancel') }}
</a-button>
<a-button class="ml-3" type="primary" status="danger" :loading="confirmLoading" @click="confirmHandler(true)">
{{ t('common.confirmDelete') }}
</a-button>
<a-button
v-if="props.record?.status === 'COMPLETED'"
:loading="confirmLoading"
class="ml-3"
type="primary"
@click="confirmHandler(false)"
>
{{ t('common.archive') }}
</a-button>
</div>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useVModel } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
import { useI18n } from '@/hooks/useI18n';
import { characterLimit } from '@/utils';
import type { TestPlanItem } from '@/models/testPlan/testPlan';
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
// isScheduled: boolean; // TODO
record: TestPlanItem | undefined; // record
}>();
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void;
}>();
const showModalVisible = useVModel(props, 'visible', emit);
function cancelHandler() {
showModalVisible.value = false;
}
const confirmLoading = ref<boolean>(false);
function confirmHandler(isDelete: boolean) {
try {
Message.success(isDelete ? t('common.deleteSuccess') : t('common.batchArchiveSuccess'));
} catch (error) {
console.log(error);
}
}
function getDeleteTip() {
switch (props.record && props.record.status) {
case 'ARCHIVED':
return t('testPlan.testPlanIndex.deleteArchivedPlan');
case 'UNDERWAY':
return t('testPlan.testPlanIndex.deleteRunningPlan');
case 'COMPLETED':
return t('testPlan.testPlanIndex.deleteCompletedPlan');
default:
return t('testPlan.testPlanIndex.deletePendingPlan');
}
}
const contentTip = computed(() => {
return getDeleteTip();
});
</script>
<style scoped lang="less">
:deep(.ms-modal-form .arco-modal-body) {
padding: 0 !important;
}
</style>

View File

@ -0,0 +1,180 @@
<template>
<MsDialog
v-model:visible="isVisible"
dialog-size="small"
:title="t('testPlan.testPlanIndex.batchEdit', { number: props.batchParams.currentSelectCount })"
ok-text="common.update"
:confirm="confirmHandler"
:close="closeHandler"
unmount-on-close
:switch-props="{
switchName: t('caseManagement.featureCase.appendTag'),
switchTooltip: t('caseManagement.featureCase.enableTags'),
showSwitch: form.selectedAttrsId === 'tags' ? true : false,
enable: form.append,
}"
>
<div class="form">
<a-form ref="formRef" class="rounded-[4px]" :model="form" layout="vertical">
<a-form-item
field="selectedAttrsId"
:label="t('apiTestManagement.chooseAttr')"
:rules="[{ required: true, message: t('apiTestManagement.attrRequired') }]"
asterisk-position="end"
>
<a-select v-model="form.selectedAttrsId" :placeholder="t('common.pleaseSelect')">
<a-option v-for="item of attrOptions" :key="item.value" :value="item.value">
{{ t(item.name) }}
</a-option>
</a-select>
</a-form-item>
<a-form-item
v-if="form.selectedAttrsId === 'tags'"
field="values"
:label="t('apiTestManagement.batchUpdate')"
:validate-trigger="['blur', 'input']"
:rules="[{ required: true, message: t('apiTestManagement.valueRequired') }]"
asterisk-position="end"
class="mb-0"
required
>
<MsTagsInput
v-model:model-value="form.tags"
placeholder="common.tagsInputPlaceholder"
allow-clear
unique-value
retain-input-value
/>
</a-form-item>
<a-form-item
v-else
field="value"
:label="t('apiTestManagement.batchUpdate')"
:rules="[{ required: true, message: t('apiTestManagement.valueRequired') }]"
asterisk-position="end"
class="mb-0"
>
<a-select
v-model="form.value"
:placeholder="t('common.pleaseSelect')"
:disabled="form.selectedAttrsId === ''"
>
<a-option v-for="item of valueOptions" :key="item.value" :value="item.value">
{{ t(item.label) }}
</a-option>
</a-select>
</a-form-item>
</a-form>
</div>
</MsDialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { FormInstance } from '@arco-design/web-vue';
import MsDialog from '@/components/pure/ms-dialog/index.vue';
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { TableQueryParams } from '@/models/common';
import Message from '@arco-design/web-vue/es/message';
const isVisible = ref<boolean>(false);
const appStore = useAppStore();
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
batchParams: BatchActionQueryParams;
activeFolder: string;
offspringIds: string[];
condition?: TableQueryParams;
}>();
const emits = defineEmits<{
(e: 'update:visible', visible: boolean): void;
(e: 'success'): void;
}>();
const currentProjectId = computed(() => appStore.currentProjectId);
const initForm = {
selectedAttrsId: '',
append: false,
tags: [],
value: '',
};
const form = ref({ ...initForm });
const attrOptions = [
{
name: 'common.tag',
value: 'tags',
},
];
const formRef = ref<FormInstance | null>(null);
function closeHandler() {
isVisible.value = false;
formRef.value?.resetFields();
form.value = { ...initForm };
form.value.tags = [];
}
async function confirmHandler(enable: boolean | undefined) {
await formRef.value?.validate().then(async (error) => {
if (!error) {
try {
const customField = {
fieldId: '',
value: '',
};
const { selectedIds, selectAll, excludeIds } = props.batchParams;
const params: TableQueryParams = {
selectIds: selectedIds || [],
selectAll: !!selectAll,
excludeIds: excludeIds || [],
projectId: currentProjectId.value,
append: enable as boolean,
tags: form.value.tags,
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
customField: form.value.selectedAttrsId === 'systemTags' ? {} : customField,
condition: {
...props.condition,
},
};
Message.success(t('caseManagement.featureCase.editSuccess'));
closeHandler();
emits('success');
} catch (e) {
console.log(e);
}
} else {
return false;
}
});
}
const valueOptions = ref<{ value: string; label: string }[]>([]);
watch(
() => isVisible.value,
(val) => {
emits('update:visible', val);
}
);
watch(
() => props.visible,
(val) => {
isVisible.value = val;
}
);
</script>
<style scoped></style>

View File

@ -10,31 +10,47 @@
@refresh="fetchData" @refresh="fetchData"
> >
<template #left> <template #left>
<a-radio-group v-model="showType" type="button" class="file-show-type mr-2"> <!-- TODO 这个版本不上 -->
<!-- <a-radio-group v-model="showType" type="button" class="file-show-type mr-2">
<a-radio :value="testPlanTypeEnum.ALL" class="show-type-icon p-[2px]">{{ <a-radio :value="testPlanTypeEnum.ALL" class="show-type-icon p-[2px]">{{
t('testPlan.testPlanIndex.all') t('testPlan.testPlanIndex.all')
}}</a-radio> }}</a-radio>
<a-radio :value="testPlanTypeEnum.TEST_PLAN" class="show-type-icon p-[2px]">{{ <a-radio :value="testPlanTypeEnum.TEST_PLAN" class="show-type-icon p-[2px]">{{
t('testPlan.testPlanIndex.testPlan') t('testPlan.testPlanIndex.testPlan')
}}</a-radio> }}</a-radio>
<!-- <a-radio value="testPlanGroup" class="show-type-icon p-[2px]">{{ <a-radio value="testPlanGroup" class="show-type-icon p-[2px]">{{
t('testPlan.testPlanIndex.testPlanGroup') t('testPlan.testPlanIndex.testPlanGroup')
}}</a-radio> --> }}</a-radio>
</a-radio-group> </a-radio-group> -->
<a-popover title="" position="bottom">
<div class="flex">
<div class="one-line-text mr-1 max-h-[32px] max-w-[116px] text-[var(--color-text-1)]">
{{ props.activeFolder === 'all' ? t('testPlan.testPlanIndex.allTestPlan') : props.nodeName }}
</div>
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
</div>
<template #content>
<div class="max-w-[400px] text-[14px] font-medium text-[var(--color-text-1)]">
{{ props.nodeName }}
<span class="text-[var(--color-text-4)]">({{ props.modulesCount[props.activeFolder] || 0 }})</span>
</div>
</template>
</a-popover>
</template> </template>
</MsAdvanceFilter> </MsAdvanceFilter>
<MsBaseTable <MsBaseTable
v-bind="propsRes" v-bind="propsRes"
ref="tableRef" ref="tableRef"
class="mt-4" class="mt-4"
:action-config="tableBatchActions" :action-config="testPlanBatchActions"
:expanded-keys="expandedKeys"
filter-icon-align-left filter-icon-align-left
v-on="propsEvent" v-on="propsEvent"
@batch-action="handleTableBatch" @batch-action="handleTableBatch"
> >
<!-- :expanded-keys="expandedKeys" -->
<template #num="{ record }"> <template #num="{ record }">
<div class="flex items-center"> <!-- TODO 这个版本不做 -->
<!-- <div class="flex items-center">
<div v-if="record.childrenCount" class="mr-2 flex items-center" @click="expandHandler(record)"> <div v-if="record.childrenCount" class="mr-2 flex items-center" @click="expandHandler(record)">
<MsIcon <MsIcon
type="icon-icon_split-turn-down-left" type="icon-icon_split-turn-down-left"
@ -56,17 +72,30 @@
<div> <div>
<div>{{ t('testPlan.testPlanIndex.scheduledTaskOpened') }}</div> <div>{{ t('testPlan.testPlanIndex.scheduledTaskOpened') }}</div>
<div>{{ t('testPlan.testPlanIndex.nextExecutionTime') }}</div> <div>{{ t('testPlan.testPlanIndex.nextExecutionTime') }}</div>
<!-- TODO 缺少字段 -->
<div>---</div> <div>---</div>
</div> </div>
<!-- TODO 缺少字段 --> <div> {{ t('testPlan.testPlanIndex.scheduledTaskUnEnable') }} </div>
<!-- <div> {{ t('testPlan.testPlanIndex.scheduledTaskUnEnable') }} </div> -->
</template> </template>
</a-tooltip> </a-tooltip>
</div> </div> -->
<div class="flex items-center">
<div class="one-line-text cursor-pointer text-[rgb(var(--primary-5))]">{{ record.num }}</div>
<a-tooltip position="right" :disabled="!record.schedule" :mouse-enter-delay="300">
<MsTag v-if="record.schedule" size="small" type="link" theme="outline" class="ml-2">{{
t('testPlan.testPlanIndex.timing')
}}</MsTag>
<template #content>
<div>
<div>{{ t('testPlan.testPlanIndex.scheduledTaskOpened') }}</div>
<div>{{ t('testPlan.testPlanIndex.nextExecutionTime') }}</div>
</div>
<div> {{ t('testPlan.testPlanIndex.scheduledTaskUnEnable') }} </div>
</template>
</a-tooltip></div
>
</template> </template>
<template #statusFilter="{ columnConfig }"> <template #statusFilter="{ columnConfig }">
<a-trigger v-model:popup-visible="statusFilterVisible" trigger="click" @popup-visible-change="handleFilterHidden"> <a-trigger v-model:popup-visible="statusFilterVisible" @popup-visible-change="handleFilterHidden">
<a-button type="text" class="arco-btn-text--secondary" @click="statusFilterVisible = true"> <a-button type="text" class="arco-btn-text--secondary" @click="statusFilterVisible = true">
{{ t(columnConfig.title as string) }} {{ t(columnConfig.title as string) }}
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" /> <icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
@ -75,13 +104,13 @@
<div class="arco-table-filters-content"> <div class="arco-table-filters-content">
<div class="flex items-center justify-center px-[6px] py-[2px]"> <div class="flex items-center justify-center px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small"> <a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
<a-checkbox v-for="key of Object.keys(reviewStatusMap)" :key="key" :value="key"> <a-checkbox v-for="key of Object.keys(planStatusMap)" :key="key" :value="key">
<a-tag <a-tag
:color="reviewStatusMap[key as ReviewStatus].color" :color="planStatusMap[key as planStatusType].color"
:class="[reviewStatusMap[key as ReviewStatus].class, 'px-[4px]']" :class="[planStatusMap[key as planStatusType].class, 'px-[4px]']"
size="small" size="small"
> >
{{ t(reviewStatusMap[key as ReviewStatus].label) }} {{ t(planStatusMap[key as planStatusType].label) }}
</a-tag> </a-tag>
</a-checkbox> </a-checkbox>
</a-checkbox-group> </a-checkbox-group>
@ -103,38 +132,33 @@
<statusTag :status="record.status" /> <statusTag :status="record.status" />
</template> </template>
<template #passRate="{ record }"> <!-- <template #passRate="{ record }">
<div class="mr-[8px] w-[100px]"> <div class="mr-[8px] w-[100px]">
<StatusProgress :status-detail="record.statusDetail" height="5px" /> <StatusProgress :status-detail="record.statusDetail" height="5px" />
</div> </div>
<div class="text-[var(--color-text-1)]"> <div class="text-[var(--color-text-1)]">
{{ `${record.passRate || 0}%` }} {{ `${record.passRate || 0}%` }}
</div> </div>
</template> </template> -->
<template #passRateTitleSlot="{ columnConfig }"> <template #passRateTitleSlot="{ columnConfig }">
<div class="flex items-center text-[var(--color-text-3)]"> <div class="flex items-center text-[var(--color-text-3)]">
{{ t(columnConfig.title as string) }} {{ t(columnConfig.title as string) }}
<a-tooltip position="right"> <a-tooltip position="right" :content="t('testPlan.testPlanIndex.passRateTitleTip')">
<icon-question-circle <icon-question-circle
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]" class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
size="16" size="16"
/> />
<template #content>
<!-- TODO 需要提供文案 -->
<!-- <div>{{ t('apiTestDebug.encodeTip1') }}</div>
<div>{{ t('apiTestDebug.encodeTip2') }}</div> -->
</template>
</a-tooltip> </a-tooltip>
</div> </div>
</template> </template>
<template #useCount="{ record }"> <!-- <template #useCount="{ record }">
<a-popover position="bottom" content-class="p-[16px]" trigger="click"> <a-popover position="bottom" content-class="p-[16px]" trigger="click">
<div>{{ record.useCaseCount.caseCount }}</div> <div>{{ record.useCaseCount.caseCount }}</div>
<template #content> <template #content>
<table class="min-w-[144px]"> <table class="min-w-[144px]">
<tr> <tr>
<td class="popover-label-td"> <td class="popover-label-td">
<div>{{ t('project.testPlanIndex.TotalCases') }}</div> <div>{{ t('testPlan.testPlanIndex.TotalCases') }}</div>
</td> </td>
<td class="popover-value-td"> <td class="popover-value-td">
{{ record.useCaseCount.caseCount }} {{ record.useCaseCount.caseCount }}
@ -142,7 +166,7 @@
</tr> </tr>
<tr> <tr>
<td class="popover-label-td"> <td class="popover-label-td">
<div class="text-[var(--color-text-1)]">{{ t('project.testPlanIndex.functionalUseCase') }}</div> <div class="text-[var(--color-text-1)]">{{ t('testPlan.testPlanIndex.functionalUseCase') }}</div>
</td> </td>
<td class="popover-value-td"> <td class="popover-value-td">
{{ record.useCaseCount.caseCount }} {{ record.useCaseCount.caseCount }}
@ -150,7 +174,7 @@
</tr> </tr>
<tr> <tr>
<td class="popover-label-td"> <td class="popover-label-td">
<div class="text-[var(--color-text-1)]">{{ t('project.testPlanIndex.apiCase') }}</div> <div class="text-[var(--color-text-1)]">{{ t('testPlan.testPlanIndex.apiCase') }}</div>
</td> </td>
<td class="popover-value-td"> <td class="popover-value-td">
{{ record.useCaseCount.caseCount }} {{ record.useCaseCount.caseCount }}
@ -158,7 +182,7 @@
</tr> </tr>
<tr> <tr>
<td class="popover-label-td"> <td class="popover-label-td">
<div class="text-[var(--color-text-1)]">{{ t('project.testPlanIndex.apiScenarioCase') }}</div> <div class="text-[var(--color-text-1)]">{{ t('testPlan.testPlanIndex.apiScenarioCase') }}</div>
</td> </td>
<td class="popover-value-td"> <td class="popover-value-td">
{{ record.useCaseCount.caseCount }} {{ record.useCaseCount.caseCount }}
@ -167,7 +191,7 @@
</table> </table>
</template> </template>
</a-popover> </a-popover>
</template> </template> -->
<template #operation="{ record }"> <template #operation="{ record }">
<div class="flex items-center"> <div class="flex items-center">
@ -218,37 +242,51 @@
@save="handleMoveOrCopy" @save="handleMoveOrCopy"
/> />
<ScheduledModal v-model:visible="showScheduledTaskModal" /> <ScheduledModal v-model:visible="showScheduledTaskModal" />
<ActionModal v-model:visible="showStatusDeleteModal" :record="activeRecord" />
<BatchEditModal
v-model:visible="showEditModel"
:batch-params="batchParams"
:active-folder="props.activeFolder"
:offspring-ids="props.offspringIds"
:condition="conditionParams"
@success="successHandler"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter'; import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem } from '@/components/pure/ms-advance-filter/type'; import { FilterFormItem } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type'; import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue'; import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types'; import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue'; import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import ActionModal from './actionModal.vue';
import BatchEditModal from './batchEditModal.vue';
import BatchMoveOrCopy from './batchMoveOrCopy.vue'; import BatchMoveOrCopy from './batchMoveOrCopy.vue';
import ScheduledModal from './scheduledModal.vue'; import ScheduledModal from './scheduledModal.vue';
import StatusProgress from './statusProgress.vue'; import StatusProgress from './statusProgress.vue';
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue'; import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
import { getTestPlanList, getTestPlanModule } from '@/api/modules/test-plan/testPlan'; import { getTestPlanList, getTestPlanModule } from '@/api/modules/test-plan/testPlan';
import { reviewStatusMap } from '@/config/caseManagement';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { useAppStore, useTableStore } from '@/store'; import { useAppStore, useTableStore } from '@/store';
import { characterLimit } from '@/utils';
import { ReviewStatus } from '@/models/caseManagement/caseReview'; import { ReviewStatus } from '@/models/caseManagement/caseReview';
import type { planStatusType, TestPlanItem } from '@/models/testPlan/testPlan';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum'; import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { testPlanTypeEnum } from '@/enums/testPlanEnum'; import { testPlanTypeEnum } from '@/enums/testPlanEnum';
import { planStatusMap } from '../config';
const tableStore = useTableStore(); const tableStore = useTableStore();
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
@ -259,6 +297,7 @@
activeFolderType: 'folder' | 'module'; activeFolderType: 'folder' | 'module';
offspringIds: string[]; // id offspringIds: string[]; // id
modulesCount: Record<string, number>; // modulesCount: Record<string, number>; //
nodeName: string; //
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -413,13 +452,20 @@
const keyword = ref<string>(''); const keyword = ref<string>('');
const statusFilterVisible = ref(false); const statusFilterVisible = ref(false);
const statusFilters = ref<string[]>([]); const statusFilters = ref<string[]>([]);
const showType = ref<keyof typeof testPlanTypeEnum>(testPlanTypeEnum.TEST_PLAN);
const tableBatchActions = { const testPlanBatchActions = {
baseAction: [ baseAction: [
// TODO
// {
// label: 'testPlan.testPlanIndex.execute',
// eventTag: 'execute',
// permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
// },
{ {
label: 'testPlan.testPlanIndex.execute', label: 'common.edit',
eventTag: 'execute', eventTag: 'edit',
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'], permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
}, },
{ {
label: 'common.copy', label: 'common.copy',
@ -430,23 +476,23 @@
// label: 'common.export', // label: 'common.export',
// eventTag: 'export', // eventTag: 'export',
// }, // },
],
moreAction: [
// {
// label: 'testPlan.testPlanIndex.openTimingTask',
// eventTag: 'openTimingTask',
// permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
// },
// {
// label: 'testPlan.testPlanIndex.closeTimingTask',
// eventTag: 'closeTimingTask',
// permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
// },
{ {
label: 'common.move', label: 'common.move',
eventTag: 'move', eventTag: 'move',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'], permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
}, },
],
moreAction: [
{
label: 'testPlan.testPlanIndex.openTimingTask',
eventTag: 'openTimingTask',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
},
{
label: 'testPlan.testPlanIndex.closeTimingTask',
eventTag: 'closeTimingTask',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
},
{ {
label: 'common.archive', label: 'common.archive',
eventTag: 'archive', eventTag: 'archive',
@ -469,14 +515,15 @@
label: 'common.copy', label: 'common.copy',
eventTag: 'copy', eventTag: 'copy',
}, },
{ // TODO
label: 'testPlan.testPlanIndex.createScheduledTask', // {
eventTag: 'createScheduledTask', // label: 'testPlan.testPlanIndex.createScheduledTask',
}, // eventTag: 'createScheduledTask',
{ // },
label: 'testPlan.testPlanIndex.configuration', // {
eventTag: 'config', // label: 'testPlan.testPlanIndex.configuration',
}, // eventTag: 'config',
// },
{ {
label: 'common.archive', label: 'common.archive',
eventTag: 'archive', eventTag: 'archive',
@ -497,7 +544,7 @@
projectId: 'string', projectId: 'string',
num: '100944', num: '100944',
name: '系统示例', name: '系统示例',
status: 'PREPARED', status: 'COMPLETED',
tags: ['string'], tags: ['string'],
schedule: 'string', schedule: 'string',
createUser: 'string', createUser: 'string',
@ -522,74 +569,74 @@
apiCount: 3, apiCount: 3,
scenarioCount: 3, scenarioCount: 3,
}, },
children: [ // children: [
{ // {
id: '100945', // id: '100945',
projectId: 'string', // projectId: 'string',
num: '100945', // num: '100945',
name: '系统示例', // name: '',
status: 'COMPLETED', // status: 'COMPLETED',
tags: ['string'], // tags: ['string'],
schedule: 'string', // schedule: 'string',
createUser: 'string', // createUser: 'string',
createTime: 'string', // createTime: 'string',
moduleName: 'string', // moduleName: 'string',
moduleId: 'string', // moduleId: 'string',
testPlanItem: [], // testPlanItem: [],
testPlanGroupId: 'string', // testPlanGroupId: 'string',
passCount: 0, // passCount: 0,
unPassCount: 0, // unPassCount: 0,
reviewedCount: 0, // reviewedCount: 0,
underReviewedCount: 0, // underReviewedCount: 0,
childrenCount: 0, // childrenCount: 0,
useCaseCount: { // useCaseCount: {
caseCount: 3, // caseCount: 3,
apiCount: 3, // apiCount: 3,
scenarioCount: 3, // scenarioCount: 3,
}, // },
statusDetail: { // statusDetail: {
tolerance: 100, // tolerance: 100,
UNPENDING: 100, // UNPENDING: 100,
RUNNING: 30, // RUNNING: 30,
SUCCESS: 30, // SUCCESS: 30,
ERROR: 30, // ERROR: 30,
executionProgress: '100%', // executionProgress: '100%',
}, // },
}, // },
{ // {
id: '100955', // id: '100955',
projectId: 'string', // projectId: 'string',
num: '100955', // num: '100955',
name: '系统示例', // name: '',
status: 'COMPLETED', // status: 'COMPLETED',
tags: ['string'], // tags: ['string'],
schedule: 'string', // schedule: 'string',
createUser: 'string', // createUser: 'string',
createTime: 'string', // createTime: 'string',
moduleName: 'string', // moduleName: 'string',
moduleId: 'string', // moduleId: 'string',
testPlanItem: [], // testPlanItem: [],
testPlanGroupId: 'string', // testPlanGroupId: 'string',
passCount: 0, // passCount: 0,
unPassCount: 0, // unPassCount: 0,
reviewedCount: 0, // reviewedCount: 0,
underReviewedCount: 0, // underReviewedCount: 0,
childrenCount: 0, // childrenCount: 0,
useCaseCount: { // useCaseCount: {
caseCount: 3, // caseCount: 3,
apiCount: 3, // apiCount: 3,
scenarioCount: 3, // scenarioCount: 3,
}, // },
statusDetail: { // statusDetail: {
tolerance: 100, // tolerance: 100,
UNPENDING: 100, // UNPENDING: 100,
RUNNING: 30, // RUNNING: 30,
SUCCESS: 30, // SUCCESS: 30,
ERROR: 30, // ERROR: 30,
executionProgress: '100%', // executionProgress: '100%',
}, // },
}, // },
], // ],
}, },
]; ];
@ -623,8 +670,6 @@
combine: {}, combine: {},
}); });
const showType = ref<keyof typeof testPlanTypeEnum>('ALL');
async function initTableParams() { async function initTableParams() {
conditionParams.value = { conditionParams.value = {
keyword: keyword.value, keyword: keyword.value,
@ -740,7 +785,7 @@
title: t('testPlan.testPlanIndex.confirmBatchArchivePlan', { title: t('testPlan.testPlanIndex.confirmBatchArchivePlan', {
count: batchParams.value.currentSelectCount, count: batchParams.value.currentSelectCount,
}), }),
content: t('testPlan.testPlanIndex.confirmBatchDeletePlanContentTip'), content: t('testPlan.testPlanIndex.confirmBatchArchivePlanContent'),
okText: t('common.archive'), okText: t('common.archive'),
cancelText: t('common.cancel'), cancelText: t('common.cancel'),
okButtonProps: { okButtonProps: {
@ -749,7 +794,7 @@
onBeforeOk: async () => { onBeforeOk: async () => {
try { try {
const { selectedIds, selectAll, excludeIds } = batchParams.value; const { selectedIds, selectAll, excludeIds } = batchParams.value;
Message.success(t('common.deleteSuccess')); Message.success(t('common.batchArchiveSuccess'));
fetchData(); fetchData();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -759,7 +804,7 @@
}); });
} }
/** /**
* 归档 * 删除
*/ */
function handleDelete() { function handleDelete() {
openModal({ openModal({
@ -767,7 +812,7 @@
title: t('testPlan.testPlanIndex.confirmBatchDeletePlan', { title: t('testPlan.testPlanIndex.confirmBatchDeletePlan', {
count: batchParams.value.currentSelectCount, count: batchParams.value.currentSelectCount,
}), }),
content: t('testPlan.testPlanIndex.confirmBatchDeletePlanContentTip'), content: t('testPlan.testPlanIndex.confirmBatchDeletePlanContent'),
okText: t('common.confirmDelete'), okText: t('common.confirmDelete'),
cancelText: t('common.cancel'), cancelText: t('common.cancel'),
okButtonProps: { okButtonProps: {
@ -786,6 +831,19 @@
}); });
} }
/**
* 批量编辑
*/
const showEditModel = ref<boolean>(false);
function handleEdit() {
showEditModel.value = true;
}
function successHandler() {
fetchData();
}
/** /**
* 批量操作 * 批量操作
*/ */
@ -813,6 +871,9 @@
case 'delete': case 'delete':
handleDelete(); handleDelete();
break; break;
case 'edit':
handleEdit();
break;
default: default:
break; break;
@ -828,7 +889,36 @@
showScheduledTaskModal.value = true; showScheduledTaskModal.value = true;
} }
function handleMoreActionSelect(item: ActionsItem, record: any) { const showStatusDeleteModal = ref<boolean>(false);
const activeRecord = ref<TestPlanItem>();
function deleteStatusHandler(record: TestPlanItem) {
activeRecord.value = cloneDeep(record);
showStatusDeleteModal.value = true;
}
function archiveHandle(record: TestPlanItem) {
openModal({
type: 'warning',
title: t('common.archiveConfirmTitle', { name: characterLimit(record.name) }),
content: t('testPlan.testPlanIndex.confirmArchivePlan'),
okText: t('common.archive'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'normal',
},
onBeforeOk: async () => {
try {
Message.success(t('common.batchArchiveSuccess'));
fetchData();
} catch (error) {
console.log(error);
}
},
hideCancel: false,
});
}
function handleMoreActionSelect(item: ActionsItem, record: TestPlanItem) {
switch (item.eventTag) { switch (item.eventTag) {
case 'copy': case 'copy':
copyHandler(); copyHandler();
@ -836,6 +926,12 @@
case 'createScheduledTask': case 'createScheduledTask':
handleScheduledTask(); handleScheduledTask();
break; break;
case 'delete':
deleteStatusHandler(record);
break;
case 'archive':
archiveHandle(record);
break;
default: default:
break; break;
} }
@ -843,17 +939,18 @@
const expandedKeys = ref<string[]>([]); const expandedKeys = ref<string[]>([]);
function expandHandler(record: any) { // TODO
if (expandedKeys.value.includes(record.id)) { // function expandHandler(record: any) {
expandedKeys.value = expandedKeys.value.filter((key) => key !== record.id); // if (expandedKeys.value.includes(record.id)) {
} else { // expandedKeys.value = expandedKeys.value.filter((key) => key !== record.id);
expandedKeys.value = [...expandedKeys.value, record.id]; // } else {
} // expandedKeys.value = [...expandedKeys.value, record.id];
} // }
// }
function getIconClass(record: any) { // TODO
return expandedKeys.value.includes(record.id) ? 'text-[rgb(var(--primary-5))]' : 'text-[var(--color-text-4)]'; // function getIconClass(record: any) {
} // return expandedKeys.value.includes(record.id) ? 'text-[rgb(var(--primary-5))]' : 'text-[var(--color-text-4)]';
// }
function handleFilterHidden(val: boolean) { function handleFilterHidden(val: boolean) {
if (!val) { if (!val) {
@ -893,11 +990,6 @@
} }
); );
// TODO
// onMounted(() => {
// setProps({ data });
// });
onBeforeMount(() => { onBeforeMount(() => {
fetchData(); fetchData();
}); });
@ -906,18 +998,16 @@
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
:deep(.arco-table-cell-expand-icon .arco-table-cell-inline-icon) { // TODO
display: none; // :deep(.arco-table-cell-expand-icon .arco-table-cell-inline-icon) {
} // display: none;
:deep(.arco-table-cell-align-left) > span:first-child { // }
padding-left: 0 !important; // :deep(.arco-table-cell-align-left) > span:first-child {
} // padding-left: 0 !important;
.arrowIcon { // }
transform: scaleX(-1); // .arrowIcon {
} // transform: scaleX(-1);
:deep(.ms-modal-form .arco-modal-body) { // }
padding: 0 !important;
}
.popover-label-td { .popover-label-td {
@apply flex items-center; @apply flex items-center;

View File

@ -20,7 +20,7 @@
<template #footer> <template #footer>
<div class="mb-[6px] mt-[4px] p-[3px_8px]"> <div class="mb-[6px] mt-[4px] p-[3px_8px]">
<MsButton type="text" class="text-[rgb(var(--primary-5))]" @click="createCustomFrequency"> <MsButton type="text" class="text-[rgb(var(--primary-5))]" @click="createCustomFrequency">
{{ t('project.testPlanIndex.customFrequency') }} {{ t('testPlan.testPlanIndex.customFrequency') }}
</MsButton> </MsButton>
</div> </div>
</template> </template>
@ -28,14 +28,14 @@
</a-form-item> </a-form-item>
<a-radio-group v-model="form.env" class="mb-4"> <a-radio-group v-model="form.env" class="mb-4">
<a-radio value=""> <a-radio value="">
{{ t('project.testPlanIndex.defaultEnv') }} {{ t('testPlan.testPlanIndex.defaultEnv') }}
<span class="float-right mx-1 mt-[1px]"> <span class="float-right mx-1 mt-[1px]">
<a-tooltip :content="t('testPlan.testPlanIndex.envTip')" position="top"> <a-tooltip :content="t('testPlan.testPlanIndex.envTip')" position="top">
<IconQuestionCircle class="h-[16px] w-[16px] text-[--color-text-4] hover:text-[rgb(var(--primary-5))]" <IconQuestionCircle class="h-[16px] w-[16px] text-[--color-text-4] hover:text-[rgb(var(--primary-5))]"
/></a-tooltip> /></a-tooltip>
</span> </span>
</a-radio> </a-radio>
<a-radio value="new"> {{ t('project.testPlanIndex.newEnv') }}</a-radio> <a-radio value="new"> {{ t('testPlan.testPlanIndex.newEnv') }}</a-radio>
</a-radio-group> </a-radio-group>
<a-radio-group v-model="form.methods"> <a-radio-group v-model="form.methods">
<a-radio value="serial">{{ t('testPlan.testPlanIndex.serial') }}</a-radio> <a-radio value="serial">{{ t('testPlan.testPlanIndex.serial') }}</a-radio>
@ -59,7 +59,7 @@
<span class="text-[var(--color-text-4)]">CPU</span> <span class="text-[var(--color-text-4)]">CPU</span>
<span class="mx-2"> {{ item.cpuRate }}</span> <span class="mx-2"> {{ item.cpuRate }}</span>
<MsTag theme="outline" :type="item.status ? 'link' : 'success'" size="small"> <MsTag theme="outline" :type="item.status ? 'link' : 'success'" size="small">
{{ item.status ? t('project.testPlanIndex.doing') : t('project.testPlanIndex.inFreeTime') }} {{ item.status ? t('testPlan.testPlanIndex.doing') : t('testPlan.testPlanIndex.inFreeTime') }}
</MsTag> </MsTag>
</div> </div>
</div> </div>

View File

@ -4,7 +4,7 @@
<table class="min-w-[144px]"> <table class="min-w-[144px]">
<tr> <tr>
<td class="popover-label-td"> <td class="popover-label-td">
<div>{{ t('project.testPlanIndex.tolerance') }}</div> <div>{{ t('testPlan.testPlanIndex.tolerance') }}</div>
</td> </td>
<td class="popover-value-td"> <td class="popover-value-td">
{{ props.statusDetail.tolerance }} {{ props.statusDetail.tolerance }}
@ -12,7 +12,7 @@
</tr> </tr>
<tr> <tr>
<td class="popover-label-td"> <td class="popover-label-td">
<div>{{ t('project.testPlanIndex.executionProgress') }}</div> <div>{{ t('testPlan.testPlanIndex.executionProgress') }}</div>
</td> </td>
<td class="popover-value-td"> <td class="popover-value-td">
{{ props.statusDetail.executionProgress }} {{ props.statusDetail.executionProgress }}

View File

@ -105,7 +105,7 @@
modulesCount?: Record<string, number>; // modulesCount?: Record<string, number>; //
}>(); }>();
const emits = defineEmits(['update:selectedKeys', 'planTreeNodeSelect', 'init', 'dragUpdate']); const emits = defineEmits(['update:selectedKeys', 'planTreeNodeSelect', 'init', 'dragUpdate', 'getNodeName']);
const currentProjectId = computed(() => appStore.currentProjectId); const currentProjectId = computed(() => appStore.currentProjectId);
@ -213,7 +213,7 @@
offspringIds.push(e.id); offspringIds.push(e.id);
return e; return e;
}); });
emits('planTreeNodeSelect', selectedKeys, offspringIds); emits('planTreeNodeSelect', selectedKeys, offspringIds, node.name);
}; };
// //

View File

@ -0,0 +1,32 @@
import type { planStatusType } from '@/models/testPlan/testPlan';
export type PlanStatusMap = Record<
planStatusType,
{
label: string;
color: string;
class: string;
}
>;
export const planStatusMap: PlanStatusMap = {
PREPARED: {
label: 'caseManagement.caseReview.unStart',
color: 'var(--color-text-n8)',
class: '!text-[var(--color-text-1)]',
},
UNDERWAY: {
label: 'caseManagement.caseReview.going',
color: 'rgb(var(--link-2))',
class: '!text-[rgb(var(--link-6))]',
},
COMPLETED: {
label: 'caseManagement.caseReview.finished',
color: 'rgb(var(--success-2))',
class: '!text-[rgb(var(--success-6))]',
},
ARCHIVED: {
label: 'caseManagement.caseReview.archived',
color: 'var(--color-text-n8)',
class: '!text-[var(--color-text-4)]',
},
};

View File

@ -82,6 +82,7 @@
:offspring-ids="offspringIds" :offspring-ids="offspringIds"
:active-folder-type="activeCaseType" :active-folder-type="activeCaseType"
:modules-count="modulesCount" :modules-count="modulesCount"
:node-name="nodeName"
@init="initModulesCount" @init="initModulesCount"
/> />
</div> </div>
@ -177,11 +178,14 @@
const activeCaseType = ref<'folder' | 'module'>('folder'); // const activeCaseType = ref<'folder' | 'module'>('folder'); //
const offspringIds = ref<string[]>([]); const offspringIds = ref<string[]>([]);
const nodeName = ref<string>('');
// //
function planNodeSelect(keys: string[], _offspringIds: string[]) { function planNodeSelect(keys: string[], _offspringIds: string[], moduleName: string) {
[activeFolder.value] = keys; [activeFolder.value] = keys;
activeCaseType.value = 'module'; activeCaseType.value = 'module';
offspringIds.value = [..._offspringIds]; offspringIds.value = [..._offspringIds];
nodeName.value = moduleName;
} }
/** /**
@ -196,7 +200,7 @@
} }
/** /**
* 右侧表格数据刷新后若当前展示的是模块刷新模块树的统计数量 * 刷新模块树的统计数量
*/ */
function initModulesCount(params: any) {} function initModulesCount(params: any) {}

View File

@ -36,6 +36,7 @@ export default {
'testPlan.testPlanIndex.confirmBatchDeletePlan': 'Are you sure to delete {count} test plans?', 'testPlan.testPlanIndex.confirmBatchDeletePlan': 'Are you sure to delete {count} test plans?',
'testPlan.testPlanIndex.confirmBatchDeletePlanContentTip': 'testPlan.testPlanIndex.confirmBatchDeletePlanContentTip':
'Only sub-plans within the test plan and plan group are deleted', 'Only sub-plans within the test plan and plan group are deleted',
'testPlan.testPlanIndex.confirmBatchDeletePlanContent': 'After delete, data unrecoverable, please careful operation.',
'testPlan.testPlanIndex.confirmBatchArchivePlan': 'Are you sure to archive {count} test plans?', 'testPlan.testPlanIndex.confirmBatchArchivePlan': 'Are you sure to archive {count} test plans?',
'testPlan.testPlanIndex.confirmBatchArchivePlanContent': 'testPlan.testPlanIndex.confirmBatchArchivePlanContent':
'Only completed test plans can be archived! \n after filing, implement information no longer update and editing, data unrecoverable, please careful operation.', 'Only completed test plans can be archived! \n after filing, implement information no longer update and editing, data unrecoverable, please careful operation.',
@ -49,17 +50,28 @@ export default {
'testPlan.testPlanIndex.timingState': 'Timing state', 'testPlan.testPlanIndex.timingState': 'Timing state',
'testPlan.testPlanIndex.timingStateEnable': 'Enable: Executes scheduled tasks', 'testPlan.testPlanIndex.timingStateEnable': 'Enable: Executes scheduled tasks',
'testPlan.testPlanIndex.timingStateClose': 'Close: Stops a scheduled task', 'testPlan.testPlanIndex.timingStateClose': 'Close: Stops a scheduled task',
'project.testPlanIndex.customFrequency': 'Custom frequency', 'testPlan.testPlanIndex.customFrequency': 'Custom frequency',
'project.testPlanIndex.doing': 'Doing', 'testPlan.testPlanIndex.doing': 'Doing',
'project.testPlanIndex.inFreeTime': 'In free time', 'testPlan.testPlanIndex.inFreeTime': 'In free time',
'project.testPlanIndex.defaultEnv': 'Default Environment', 'testPlan.testPlanIndex.defaultEnv': 'Default Environment',
'project.testPlanIndex.newEnv': 'New Environment', 'testPlan.testPlanIndex.newEnv': 'New Environment',
'project.testPlanIndex.executionProgress': 'Execution progress', 'testPlan.testPlanIndex.executionProgress': 'Execution progress',
'project.testPlanIndex.tolerance': 'tolerance', 'testPlan.testPlanIndex.tolerance': 'tolerance',
'project.testPlanIndex.TotalCases': 'Total use cases', 'testPlan.testPlanIndex.TotalCases': 'Total use cases',
'project.testPlanIndex.functionalUseCase': 'case', 'testPlan.testPlanIndex.functionalUseCase': 'case',
'project.testPlanIndex.apiCase': 'Api use case', 'testPlan.testPlanIndex.apiCase': 'Api use case',
'project.testPlanIndex.apiScenarioCase': 'Api scenario use cases', 'testPlan.testPlanIndex.apiScenarioCase': 'Api scenario use cases',
'testPlan.testPlanIndex.deleteArchivedPlan':
'After the program has been archived, delete data unrecoverable, please careful operation.',
'testPlan.testPlanIndex.deletePendingPlan':
'The plan is not executed, the data cannot be recovered after deletion, please operate carefully!',
'testPlan.testPlanIndex.deleteRunningPlan':
'Scheduled tasks are stopped and deleted. Exercise caution when performing this operation',
'testPlan.testPlanIndex.deleteCompletedPlan':
'The proposed plan is completed, the option is archived, and the use case information and execution results are retained;If you continue to delete, the data will not be restored, please be careful!',
'testPlan.testPlanIndex.confirmArchivePlan':
'After filing, implement information no longer update and editing, data unrecoverable, please careful operation',
'testPlan.testPlanIndex.passRateTitleTip': 'Passed use cases/all use cases *100%',
'testPlan.planForm.namePlaceholder': 'Please enter the name of the test plan', 'testPlan.planForm.namePlaceholder': 'Please enter the name of the test plan',
'testPlan.planForm.nameRequired': 'Test plan name cannot be empty', 'testPlan.planForm.nameRequired': 'Test plan name cannot be empty',
'testPlan.planForm.planStartAndEndTime': 'Planned start and end time', 'testPlan.planForm.planStartAndEndTime': 'Planned start and end time',

View File

@ -36,9 +36,10 @@ export default {
'testPlan.testPlanIndex.parallel': '并行', 'testPlan.testPlanIndex.parallel': '并行',
'testPlan.testPlanIndex.confirmBatchDeletePlan': '确认删除 {count} 个测试计划吗?', 'testPlan.testPlanIndex.confirmBatchDeletePlan': '确认删除 {count} 个测试计划吗?',
'testPlan.testPlanIndex.confirmBatchDeletePlanContentTip': '仅删除测试计划和计划组内的子计划', 'testPlan.testPlanIndex.confirmBatchDeletePlanContentTip': '仅删除测试计划和计划组内的子计划',
'testPlan.testPlanIndex.confirmBatchDeletePlanContent': '删除后,数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.confirmBatchArchivePlan': '确认归档 {count} 个测试计划吗?', 'testPlan.testPlanIndex.confirmBatchArchivePlan': '确认归档 {count} 个测试计划吗?',
'testPlan.testPlanIndex.confirmBatchArchivePlanContent': 'testPlan.testPlanIndex.confirmBatchArchivePlanContent':
'仅 已完成 测试计划可归档!\n 归档后,执行信息不再更新且不可编辑,数据不可恢复,请谨慎操作!', '仅 已完成 测试计划可归档!\r\n 归档后,执行信息不再更新且不可编辑,数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.selectedCount': '(已选 {count} 项数据)', 'testPlan.testPlanIndex.selectedCount': '(已选 {count} 项数据)',
'testPlan.testPlanIndex.createScheduledTask': '创建定时任务', 'testPlan.testPlanIndex.createScheduledTask': '创建定时任务',
'testPlan.testPlanIndex.updateScheduledTask': '更新定时任务', 'testPlan.testPlanIndex.updateScheduledTask': '更新定时任务',
@ -49,17 +50,26 @@ export default {
'testPlan.testPlanIndex.timingState': '定时状态', 'testPlan.testPlanIndex.timingState': '定时状态',
'testPlan.testPlanIndex.timingStateEnable': '开启:执行定时任务', 'testPlan.testPlanIndex.timingStateEnable': '开启:执行定时任务',
'testPlan.testPlanIndex.timingStateClose': '关闭:停止定时任务', 'testPlan.testPlanIndex.timingStateClose': '关闭:停止定时任务',
'project.testPlanIndex.customFrequency': '自定义频率', 'testPlan.testPlanIndex.customFrequency': '自定义频率',
'project.testPlanIndex.doing': '进行中', 'testPlan.testPlanIndex.doing': '进行中',
'project.testPlanIndex.inFreeTime': '空闲中', 'testPlan.testPlanIndex.inFreeTime': '空闲中',
'project.testPlanIndex.defaultEnv': '默认环境', 'testPlan.testPlanIndex.defaultEnv': '默认环境',
'project.testPlanIndex.newEnv': '新环境', 'testPlan.testPlanIndex.newEnv': '新环境',
'project.testPlanIndex.executionProgress': '执行进度', 'testPlan.testPlanIndex.executionProgress': '执行进度',
'project.testPlanIndex.tolerance': '容错率', 'testPlan.testPlanIndex.tolerance': '容错率',
'project.testPlanIndex.TotalCases': '用例总数', 'testPlan.testPlanIndex.TotalCases': '用例总数',
'project.testPlanIndex.functionalUseCase': '功能用例', 'testPlan.testPlanIndex.functionalUseCase': '功能用例',
'project.testPlanIndex.apiCase': '接口用例', 'testPlan.testPlanIndex.apiCase': '接口用例',
'project.testPlanIndex.apiScenarioCase': '接口场景用例', 'testPlan.testPlanIndex.apiScenarioCase': '接口场景用例',
'testPlan.testPlanIndex.deleteArchivedPlan': '计划 已归档,删除后数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.deletePendingPlan': '计划 未执行,删除后数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.deleteRunningPlan':
'计划 进行中,删除后,终止执行且不可恢复,定时任务停止并删除,请谨慎操作!',
'testPlan.testPlanIndex.deleteCompletedPlan':
'建议计划 已完成 ,选择归档,用例信息及执行结果都将被保留;若继续删除,数据将不会恢复,请谨慎操作!',
'testPlan.testPlanIndex.confirmArchivePlan': '归档后,执行信息不再更新且不可编辑,数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.passRateTitleTip': '已通过用例/全部用例*100%',
'testPlan.testPlanIndex.batchEdit': '批量编辑 (已选 { number } 项数据)',
'testPlan.planForm.namePlaceholder': '请输入测试计划名称', 'testPlan.planForm.namePlaceholder': '请输入测试计划名称',
'testPlan.planForm.nameRequired': '测试计划名称不能为空', 'testPlan.planForm.nameRequired': '测试计划名称不能为空',
'testPlan.planForm.planStartAndEndTime': '计划起止时间', 'testPlan.planForm.planStartAndEndTime': '计划起止时间',