feat(功能用例): 用例详情页面&附件接口联调
This commit is contained in:
parent
0bb49654df
commit
6e57a9df97
|
@ -110,7 +110,7 @@ export function createCaseRequest(data: Record<string, any>) {
|
|||
}
|
||||
// 编辑用例
|
||||
export function updateCaseRequest(data: Record<string, any>) {
|
||||
return MSR.uploadFile({ url: UpdateCaseUrl }, { request: data.request, fileList: data.fileList }, '', true);
|
||||
return MSR.uploadFile({ url: UpdateCaseUrl }, data, '', true);
|
||||
}
|
||||
// 用例详情
|
||||
export function getCaseDetail(id: string) {
|
||||
|
@ -195,14 +195,10 @@ export function cancelAssociationDemand(id: string) {
|
|||
|
||||
// 上传文件并关联用例
|
||||
export function uploadOrAssociationFile(data: Record<string, any>) {
|
||||
return MSR.uploadFile(
|
||||
{ url: UploadOrAssociationFileUrl },
|
||||
{ request: data.request, fileList: data.fileList },
|
||||
'file'
|
||||
);
|
||||
return MSR.uploadFile({ url: UploadOrAssociationFileUrl }, { request: data.request, fileList: [data.file] });
|
||||
}
|
||||
// 转存文件
|
||||
export function transferFile(data: OperationFile) {
|
||||
export function transferFileRequest(data: OperationFile) {
|
||||
return MSR.post({ url: TransferFileUrl, data });
|
||||
}
|
||||
|
||||
|
@ -212,13 +208,13 @@ export function previewFile(data: OperationFile) {
|
|||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFile(data: OperationFile) {
|
||||
return MSR.post({ url: DownloadFileUrl, data });
|
||||
export function downloadFileRequest(data: OperationFile) {
|
||||
return MSR.post({ url: DownloadFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
|
||||
}
|
||||
|
||||
// 删除文件或取消关联用例文件
|
||||
export function deleteFileOrCancelAssociation(data: any) {
|
||||
return MSR.post({ url: DownloadFileUrl, data });
|
||||
export function deleteFileOrCancelAssociation(data: OperationFile) {
|
||||
return MSR.post({ url: deleteFileOrCancelAssociationUrl, data });
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
{{ props.title }}
|
||||
</div>
|
||||
</a-tooltip>
|
||||
|
||||
<div class="ml-4 flex items-center">
|
||||
<slot name="titleLeft" :loading="loading" :detail="detail"></slot>
|
||||
</div>
|
||||
<MsPrevNextButton
|
||||
ref="prevNextButtonRef"
|
||||
v-model:loading="loading"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { FieldRule } from '@arco-design/web-vue';
|
||||
|
||||
import type { Rule } from '@form-create/arco-design';
|
||||
import { Rule } from '@form-create/arco-design';
|
||||
|
||||
export type FormItemType =
|
||||
| 'INPUT'
|
||||
|
@ -33,13 +33,12 @@ export interface FormItemDefaultOptions {
|
|||
export interface PropsRecord {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 内置formCreateRule所有配置的项
|
||||
export type FormRuleItem = any;
|
||||
// TODO
|
||||
// export type FormRuleItem = Rule & {
|
||||
// props: Record<string, any>;
|
||||
// [key: string]: any;
|
||||
// };
|
||||
export type FormRuleItem = Rule & {
|
||||
props: Record<string, any>;
|
||||
[key: string]: any;
|
||||
};
|
||||
// 表单配置项
|
||||
export interface FormItem {
|
||||
type: FormItemType;
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
</a-avatar>
|
||||
</template>
|
||||
<template #title>
|
||||
<div class="one-line-text max-w-[80%] font-normal">{{ item.file.name }}</div>
|
||||
<a-tooltip :content="item.file.name">
|
||||
<div class="one-line-text max-w-[80%] font-normal">{{ item.file.name }}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #description>
|
||||
<div v-if="item.status === UploadStatus.init" class="text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
||||
|
@ -82,7 +84,7 @@
|
|||
{{ t('ms.upload.reUpload') }}
|
||||
</MsButton>
|
||||
<MsButton type="button" status="danger" class="!mr-[4px]" @click="deleteFile(item)">
|
||||
{{ t('ms.upload.delete') }}
|
||||
{{ t(item.deleteContent) || t('ms.upload.delete') }}
|
||||
</MsButton>
|
||||
<slot name="actions" :item="item"></slot>
|
||||
</div>
|
||||
|
|
|
@ -36,6 +36,13 @@ export enum TableKeyEnum {
|
|||
CASE_MANAGEMENT_DEMAND = 'caseManagementDemand',
|
||||
CASE_MANAGEMENT_REVIEW = 'caseManagementReview',
|
||||
CASE_MANAGEMENT_REVIEW_CASE = 'caseManagementReviewCase',
|
||||
CASE_MANAGEMENT_TAB_DEFECT = 'caseManagementTabDefect',
|
||||
CASE_MANAGEMENT_TAB_DEFECT_TEST_PLAN = 'caseManagementTabTestPlan',
|
||||
CASE_MANAGEMENT_TAB_DEPENDENCY_PRE_CASE = 'caseManagementTabPreDependency',
|
||||
CASE_MANAGEMENT_TAB_DEPENDENCY_POST_CASE = 'caseManagementTabPostDependency',
|
||||
CASE_MANAGEMENT_TAB_REVIEW = 'caseManagementTabCaseReview',
|
||||
CASE_MANAGEMENT_TAB_TEST_PLAN = 'caseManagementTabTestPlan',
|
||||
CASE_MANAGEMENT_TAB_CHANGE_HISTORY = 'caseManagementTabChangeHistory',
|
||||
}
|
||||
|
||||
// 具有特殊功能的列
|
||||
|
|
|
@ -214,9 +214,10 @@ export interface CreateOrUpdateDemand {
|
|||
}
|
||||
// 转存文件
|
||||
export interface OperationFile {
|
||||
id?: string;
|
||||
projectId: string;
|
||||
caseId: string;
|
||||
fileId: string; // 文件id
|
||||
fileId?: string; // 文件id
|
||||
local: boolean; // 是否是本地
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
|
||||
const isContinueFlag = ref(false);
|
||||
const isShowTip = ref<boolean>(true);
|
||||
const createSuccessId = ref<string>('');
|
||||
|
||||
async function save() {
|
||||
try {
|
||||
|
@ -70,15 +71,20 @@
|
|||
await updateCaseRequest(caseDetailInfo.value);
|
||||
Message.success(t('caseManagement.featureCase.editSuccess'));
|
||||
} else {
|
||||
await createCaseRequest(caseDetailInfo.value);
|
||||
const res = await createCaseRequest(caseDetailInfo.value);
|
||||
createSuccessId.value = res.data.id;
|
||||
Message.success(route.params.mode === 'copy' ? t('ms.description.copySuccess') : t('common.addSuccess'));
|
||||
}
|
||||
router.push({ name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE, query: { ...route.query } });
|
||||
featureCaseStore.setIsAlreadySuccess(true);
|
||||
isShowTip.value = !getIsVisited();
|
||||
if (isShowTip.value) {
|
||||
if (isShowTip.value && !route.query.id) {
|
||||
router.push({
|
||||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_CREATE_SUCCESS,
|
||||
query: {
|
||||
id: createSuccessId.value,
|
||||
...route.query,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -119,7 +125,6 @@
|
|||
title.value = t('caseManagement.featureCase.creatingCase');
|
||||
}
|
||||
const gatewayAddress = `${window.location.protocol}//${window.location.hostname}:${window.location.port}`;
|
||||
console.log(gatewayAddress);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
:page-change="props.pageChange"
|
||||
@loaded="loadedCase"
|
||||
>
|
||||
<template #titleLeft>
|
||||
<div class="flex items-center"><caseLevel :case-level="(caseLevels as CaseLevel)" /></div>
|
||||
</template>
|
||||
<template #titleRight="{ loading }">
|
||||
<div class="rightButtons flex items-center">
|
||||
<MsButton
|
||||
|
@ -85,33 +88,33 @@
|
|||
<template #left>
|
||||
<div class="leftWrapper h-full">
|
||||
<div class="header h-[50px]">
|
||||
<a-tabs @change="changeTabs">
|
||||
<a-tab-pane key="detail">
|
||||
<template #title> {{ t('caseManagement.featureCase.detail') }}</template>
|
||||
<TabDetail v-if="activeTab === 'detail'" :form="detailInfo" @update-success="updateSuccess" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane v-for="tab of tabSetting" :key="tab.key">
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<span>{{ t(tab.title) }}</span>
|
||||
<a-badge
|
||||
class="ml-1"
|
||||
:class="activeTab === tab.key ? 'active' : ''"
|
||||
:count="1000"
|
||||
:max-count="99"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<Demand v-if="activeTab === 'requirement'" :case-id="props.detailId" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="setting">
|
||||
<template #title>
|
||||
<span @click="showMenuSetting">{{
|
||||
t('caseManagement.featureCase.detailDisplaySetting')
|
||||
}}</span></template
|
||||
>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-menu mode="horizontal" :default-selected-keys="[activeTab]" @menu-item-click="clickMenu">
|
||||
<a-menu-item key="detail">{{ t('caseManagement.featureCase.detail') }} </a-menu-item>
|
||||
<a-menu-item v-for="tab of tabSetting" :key="tab.key">
|
||||
<div class="flex items-center">
|
||||
<span>{{ t(tab.title) }}</span>
|
||||
<a-badge
|
||||
class="ml-1"
|
||||
:class="activeTab === tab.key ? 'active' : ''"
|
||||
:count="1000"
|
||||
:max-count="99"
|
||||
/> </div
|
||||
></a-menu-item>
|
||||
<a-menu-item key="setting">
|
||||
<span @click="showMenuSetting">{{
|
||||
t('caseManagement.featureCase.detailDisplaySetting')
|
||||
}}</span></a-menu-item
|
||||
>
|
||||
</a-menu>
|
||||
<div class="mt-4">
|
||||
<TabDetail v-if="activeTab === 'detail'" :form="detailInfo" @update-success="updateSuccess" />
|
||||
<TabDemand v-else-if="activeTab === 'requirement'" :case-id="props.detailId" />
|
||||
<TabDefect v-else-if="activeTab === 'bug'" />
|
||||
<TabDependency v-else-if="activeTab === 'dependency'" />
|
||||
<TabCaseReview v-else-if="activeTab === 'caseReview'" />
|
||||
<TabTestPlan v-else-if="activeTab === 'testPlan'" />
|
||||
<TabChangeHistory v-else-if="activeTab === 'changeHistory'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -161,10 +164,17 @@
|
|||
import type { FormItem } from '@/components/pure/ms-form-create/types';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import type { MsPaginationI } from '@/components/pure/ms-table/type';
|
||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||
import Demand from './demand.vue';
|
||||
import SettingDrawer from './settingDrawer.vue';
|
||||
import TabDetail from './tabDetail.vue';
|
||||
import SettingDrawer from './tabContent/settingDrawer.vue';
|
||||
import TabDefect from './tabContent/tabBug/tabDefect.vue';
|
||||
import TabCaseReview from './tabContent/tabCaseReview.vue';
|
||||
import TabChangeHistory from './tabContent/tabChangeHistory.vue';
|
||||
import TabDemand from './tabContent/tabDemand/demand.vue';
|
||||
import TabDependency from './tabContent/tabDependency/tabDependency.vue';
|
||||
import TabDetail from './tabContent/tabDetail.vue';
|
||||
import TabTestPlan from './tabContent/tabTestPlan.vue';
|
||||
|
||||
import { deleteCaseRequest, followerCaseRequest, getCaseDetail } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -216,23 +226,28 @@
|
|||
|
||||
const tabSetting = ref<TabItemType[]>([...tabSettingList.value]);
|
||||
const activeTab = ref<string | number>('detail');
|
||||
function changeTabs(key: string | number) {
|
||||
function clickMenu(key: string | number) {
|
||||
activeTab.value = key;
|
||||
switch (activeTab.value) {
|
||||
case 'setting':
|
||||
showMenuSetting();
|
||||
break;
|
||||
default:
|
||||
showSettingDrawer.value = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const detailInfo = ref<Record<string, any>>({});
|
||||
const customFields = ref<CustomAttributes[]>([]);
|
||||
|
||||
const caseLevels = ref(0);
|
||||
function loadedCase(detail: CaseManagementTable) {
|
||||
detailInfo.value = { ...detail };
|
||||
customFields.value = detailInfo.value.customFields;
|
||||
const caseLevelsValue = customFields.value.find((item) => item.fieldName === '用例等级')?.defaultValue;
|
||||
if (caseLevelsValue) {
|
||||
caseLevels.value = JSON.parse(caseLevelsValue).replaceAll('P', '') * 1;
|
||||
}
|
||||
}
|
||||
|
||||
const moduleName = computed(() => {
|
||||
|
@ -361,6 +376,16 @@
|
|||
}) as FormItem[];
|
||||
}
|
||||
|
||||
// const caseLevels = computed(() => {
|
||||
// let level = 0;
|
||||
// customFields.value.forEach((item) => {
|
||||
// if (item.fieldName === '用例等级') {
|
||||
// level = JSON.parse(item.defaultValue);
|
||||
// }
|
||||
// });
|
||||
// return level as CaseLevel;
|
||||
// });
|
||||
|
||||
watch(
|
||||
() => customFields.value,
|
||||
() => {
|
||||
|
@ -393,6 +418,15 @@
|
|||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.arco-menu-light) {
|
||||
height: 50px;
|
||||
background: none !important;
|
||||
.arco-menu-inner {
|
||||
overflow: hidden;
|
||||
padding: 14px 2px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
.leftWrapper {
|
||||
.header {
|
||||
padding: 0 16px;
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<FilterPanel v-show="isExpandFilter"></FilterPanel>
|
||||
<!-- 脑图开始 -->
|
||||
<MinderEditor
|
||||
v-if="showType === 'xMind'"
|
||||
|
@ -149,7 +148,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MinderEditor from '@/components/pure/minder-editor/minderEditor.vue';
|
||||
|
@ -162,7 +161,6 @@
|
|||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import FilterPanel from '@/components/business/ms-filter-panel/searchForm.vue';
|
||||
import BatchEditModal from './batchEditModal.vue';
|
||||
import CaseDetailDrawer from './caseDetailDrawer.vue';
|
||||
import FeatureCaseTree from './caseTree.vue';
|
||||
|
@ -194,6 +192,7 @@
|
|||
const { openModal } = useModal();
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const appStore = useAppStore();
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
const tableStore = useTableStore();
|
||||
|
@ -442,7 +441,7 @@
|
|||
],
|
||||
moreAction: [
|
||||
{
|
||||
label: 'featureTest.featureCase.addDemand',
|
||||
label: 'caseManagement.featureCase.addDemand',
|
||||
eventTag: 'addDemand',
|
||||
},
|
||||
{
|
||||
|
@ -720,7 +719,9 @@
|
|||
async function batchDelete() {
|
||||
openModal({
|
||||
type: 'error',
|
||||
title: t('caseManagement.featureCase.batchDelete', { number: (selectData.value || []).length }),
|
||||
title: t('caseManagement.featureCase.batchDelete', {
|
||||
number: batchParams.value.currentSelectCount,
|
||||
}),
|
||||
content: t('caseManagement.featureCase.beforeDeleteCase'),
|
||||
okText: t('common.confirmDelete'),
|
||||
cancelText: t('common.cancel'),
|
||||
|
@ -794,6 +795,7 @@
|
|||
const showDetailDrawer = ref(false);
|
||||
const activeDetailId = ref<string>('');
|
||||
const activeCaseIndex = ref<number>(0);
|
||||
|
||||
// 详情
|
||||
function showCaseDetail(id: string, index: number) {
|
||||
showDetailDrawer.value = true;
|
||||
|
@ -801,6 +803,13 @@
|
|||
activeCaseIndex.value = index;
|
||||
}
|
||||
|
||||
// 地址栏携带 id,自动打开资源池详情抽屉
|
||||
onMounted(() => {
|
||||
if (route.query.id) {
|
||||
showCaseDetail(route.query.id as string, 0);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => showType.value,
|
||||
() => {
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</div>
|
||||
<!-- 步骤描述 -->
|
||||
<div v-if="form.caseEditType === 'STEP'" class="w-full">
|
||||
<AddStep v-model:step-list="stepData" />
|
||||
<AddStep v-model:step-list="stepData" :is-disabled="true" />
|
||||
</div>
|
||||
<!-- 文本描述 -->
|
||||
<MsRichText v-else v-model:modelValue="form.textDescription" />
|
||||
|
@ -103,19 +103,34 @@
|
|||
<template #actions="{ item }">
|
||||
<!-- 本地文件 -->
|
||||
<div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
|
||||
<MsButton type="button" status="danger" class="!mr-[4px]" @click="transferFile(item)">
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="transferFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.storage') }}
|
||||
</MsButton>
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<!-- 关联文件 -->
|
||||
<div v-else class="flex flex-nowrap">
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="cancelAssociated(item)">
|
||||
{{ t('caseManagement.featureCase.cancelLink') }}
|
||||
</MsButton>
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
|
||||
<MsButton
|
||||
v-if="route.query.id"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
|
@ -191,7 +206,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { FormInstance } from '@arco-design/web-vue';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
||||
|
@ -339,7 +354,12 @@
|
|||
|
||||
const fileList = ref<MsFileItem[]>([]);
|
||||
|
||||
function beforeUpload() {
|
||||
function beforeUpload(file: File) {
|
||||
const _maxSize = 50 * 1024 * 1024;
|
||||
if (file.size > _maxSize) {
|
||||
Message.warning(t('ms.upload.overSize'));
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
|
@ -388,9 +408,17 @@
|
|||
.map((item: any) => item.id);
|
||||
});
|
||||
|
||||
// 取消关联文件id
|
||||
// 取消关联文件id TODO
|
||||
const unLinkFilesIds = computed(() => {
|
||||
return associateFileIds.value.filter((id: string) => !currentAlreadyAssociateFileList.value.includes(id));
|
||||
const deleteAssociateFileIds = fileList.value
|
||||
.filter(
|
||||
(item: any) =>
|
||||
!currentAlreadyAssociateFileList.value.includes(item.uid) && associateFileIds.value.includes(item.uid)
|
||||
)
|
||||
.map((item) => item.uid);
|
||||
return associateFileIds.value.filter(
|
||||
(id: string) => !currentAlreadyAssociateFileList.value.includes(id) && !deleteAssociateFileIds.includes(id)
|
||||
);
|
||||
});
|
||||
|
||||
// 处理详情字段
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
<div class="h-full">
|
||||
<div class="mt-8 text-center">
|
||||
<div class="flex justify-center"><svg-icon :width="'60px'" :height="'60px'" :name="'success'" /></div>
|
||||
<div class="mb-2 mt-6 text-[20px] font-medium"> {{ t('caseManagement.featureCase.editSuccess') }} </div>
|
||||
<div class="mb-2 mt-6 text-[20px] font-medium"> {{ t('caseManagement.featureCase.addSuccess') }} </div>
|
||||
<div
|
||||
><span class="mr-1 text-[rgb(var(--primary-5))]">{{ countDown }}</span
|
||||
><span class="text-[var(--color-text-4)]">{{ t('caseManagement.featureCase.countDownTip') }}</span></div
|
||||
>
|
||||
<div class="my-6">
|
||||
<a-button type="primary"> {{ t('caseManagement.featureCase.caseDetail') }} </a-button>
|
||||
<a-button type="primary" @click="goDetail"> {{ t('caseManagement.featureCase.caseDetail') }} </a-button>
|
||||
<a-button class="mx-3" type="outline" @click="continueCreate">
|
||||
{{ t('caseManagement.featureCase.addContinueCreate') }}
|
||||
</a-button>
|
||||
|
@ -53,7 +53,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsCardList from '@/components/business/ms-card-list/index.vue';
|
||||
|
@ -69,6 +69,7 @@
|
|||
const { addVisited } = useVisit(visitedKey);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const cardList = ref([
|
||||
{
|
||||
key: 'testPlanTemplate',
|
||||
|
@ -116,6 +117,13 @@
|
|||
});
|
||||
}
|
||||
|
||||
function goDetail() {
|
||||
router.push({
|
||||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE,
|
||||
query: route.query,
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => isNextTip.value,
|
||||
() => {
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<MsDrawer
|
||||
v-model:visible="showDrawer"
|
||||
:mask="false"
|
||||
:title="t('caseManagement.featureCase.createDefect')"
|
||||
:ok-text="t('common.confirm')"
|
||||
:ok-loading="drawerLoading"
|
||||
:width="800"
|
||||
unmount-on-close
|
||||
:show-continue="true"
|
||||
@confirm="handleDrawerConfirm"
|
||||
@cancel="handleDrawerCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="form" layout="vertical">
|
||||
<a-form-item
|
||||
field="name"
|
||||
:label="t('bugManagement.bugName')"
|
||||
:rules="[{ required: true, message: t('bugManagement.edit.nameIsRequired') }]"
|
||||
:placeholder="t('bugManagement.edit.pleaseInputBugName')"
|
||||
>
|
||||
<a-input v-model="form.name" :max-length="255" show-word-limit />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('bugManagement.edit.content')">
|
||||
<MsRichText v-model="form.content" />
|
||||
</a-form-item>
|
||||
<div class="mb-[8px] text-[var(--color-text-1)]">{{ t('bugManagement.edit.file') }}</div>
|
||||
<MsUpload
|
||||
v-model:file-list="fileList"
|
||||
:auto-upload="false"
|
||||
multiple
|
||||
draggable
|
||||
accept="unknown"
|
||||
is-limit
|
||||
size-unit="MB"
|
||||
:max-size="500"
|
||||
>
|
||||
<a-button type="outline">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
{{ t('bugManagement.edit.uploadFile') }}
|
||||
</a-button>
|
||||
</MsUpload>
|
||||
<div class="mb-[8px] mt-[2px] text-[var(--color-text-4)]">{{ t('bugManagement.edit.fileExtra') }}</div>
|
||||
<FileList
|
||||
:show-tab="false"
|
||||
:file-list="fileList"
|
||||
:upload-func="uploadFile"
|
||||
@delete-file="deleteFile"
|
||||
@reupload="reupload"
|
||||
@handle-preview="handlePreview"
|
||||
>
|
||||
</FileList>
|
||||
</a-form>
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { FileItem } from '@arco-design/web-vue';
|
||||
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import FileList from '@/components/pure/ms-upload/fileList.vue';
|
||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
const fileList = ref<FileItem[]>([]);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const form = ref({
|
||||
name: '',
|
||||
content: '',
|
||||
templateId: '',
|
||||
handleMan: [],
|
||||
status: '',
|
||||
severity: '',
|
||||
tag: [],
|
||||
});
|
||||
|
||||
// 上传文件
|
||||
const uploadFile = (file: File) => {
|
||||
const fileItem: FileItem = {
|
||||
uid: `${Date.now()}`,
|
||||
name: file.name,
|
||||
status: 'init',
|
||||
file,
|
||||
};
|
||||
fileList.value.push(fileItem);
|
||||
return Promise.resolve(fileItem);
|
||||
};
|
||||
|
||||
// 删除文件
|
||||
const deleteFile = (item: FileItem) => {
|
||||
fileList.value = fileList.value.filter((e) => e.uid !== item.uid);
|
||||
};
|
||||
|
||||
const reupload = (item: FileItem) => {
|
||||
fileList.value = fileList.value.map((e) => {
|
||||
if (e.uid === item.uid) {
|
||||
return {
|
||||
...e,
|
||||
status: 'init',
|
||||
};
|
||||
}
|
||||
return e;
|
||||
});
|
||||
};
|
||||
|
||||
// 预览文件
|
||||
const handlePreview = (item: FileItem) => {
|
||||
const { url } = item;
|
||||
window.open(url);
|
||||
};
|
||||
|
||||
const showDrawer = computed({
|
||||
get() {
|
||||
return props.visible;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:visible', value);
|
||||
},
|
||||
});
|
||||
|
||||
const drawerLoading = ref<boolean>(false);
|
||||
|
||||
function handleDrawerConfirm() {}
|
||||
function handleDrawerCancel() {}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<MsDrawer
|
||||
v-model:visible="showDrawer"
|
||||
:mask="false"
|
||||
:title="t('caseManagement.featureCase.linkDefect')"
|
||||
:ok-text="t('caseManagement.featureCase.associated')"
|
||||
:ok-loading="drawerLoading"
|
||||
:width="960"
|
||||
unmount-on-close
|
||||
:show-continue="false"
|
||||
@confirm="handleDrawerConfirm"
|
||||
@cancel="handleDrawerCancel"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="font-medium">{{ t('caseManagement.featureCase.defectList') }}</div>
|
||||
<div>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
></a-input-search
|
||||
></div>
|
||||
</div>
|
||||
<div>
|
||||
<ms-base-table ref="tableRef" v-bind="propsRes" v-on="propsEvent">
|
||||
<template #defectName="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</div>
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'id',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectName',
|
||||
slotName: 'defectName',
|
||||
dataIndex: 'defectName',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.updateUser',
|
||||
slotName: 'name',
|
||||
dataIndex: 'updateUser',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectState',
|
||||
slotName: 'defectState',
|
||||
dataIndex: 'defectState',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.IterationPlan',
|
||||
dataIndex: 'level',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: true,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEFECT,
|
||||
selectable: true,
|
||||
scroll: { x: 1000 },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
const drawerLoading = ref<boolean>(false);
|
||||
|
||||
function handleDrawerConfirm() {}
|
||||
function handleDrawerCancel() {}
|
||||
|
||||
const showDrawer = computed({
|
||||
get() {
|
||||
return props.visible;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:visible', value);
|
||||
},
|
||||
});
|
||||
|
||||
const keyword = ref<string>('');
|
||||
|
||||
function getFetch() {
|
||||
setLoadListParams({ keyword: keyword.value });
|
||||
loadList();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getFetch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,249 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div v-if="showType === 'link'">
|
||||
<a-button class="mr-3" type="primary" @click="linkDefect">
|
||||
{{ t('caseManagement.featureCase.linkDefect') }}
|
||||
</a-button>
|
||||
<a-button type="outline" @click="createDefect"> {{ t('caseManagement.featureCase.createDefect') }} </a-button>
|
||||
</div>
|
||||
<div v-else class="font-medium">{{ t('caseManagement.featureCase.testPlanLinkList') }}</div>
|
||||
<div>
|
||||
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type ml-[4px]">
|
||||
<a-radio value="link" class="show-type-icon p-[2px]">{{
|
||||
t('caseManagement.featureCase.directLink')
|
||||
}}</a-radio>
|
||||
<a-radio value="testPlan" class="show-type-icon p-[2px]">{{
|
||||
t('caseManagement.featureCase.testPlan')
|
||||
}}</a-radio>
|
||||
</a-radio-group>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
></a-input-search>
|
||||
</div>
|
||||
</div>
|
||||
<ms-base-table v-if="showType === 'link'" ref="tableRef" v-bind="linkPropsRes" v-on="linkTableEvent">
|
||||
<template #defectName="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<ms-base-table v-else v-bind="testPlanPropsRes" v-on="testPlanTableEvent">
|
||||
<template #defectName="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsButton @click="cancelLink(record)">{{ t('caseManagement.featureCase.cancelLink') }}</MsButton>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
|
||||
<AddDefectDrawer v-model:visible="showDrawer" />
|
||||
<LinkDefectDrawer v-model:visible="showLinkDrawer" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @description 用例管理-详情抽屉-tab-缺陷
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import AddDefectDrawer from './addDefectDrawer.vue';
|
||||
import LinkDefectDrawer from './linkDefectDrawer.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const showType = ref('link');
|
||||
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'id',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectName',
|
||||
slotName: 'defectName',
|
||||
dataIndex: 'defectName',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectState',
|
||||
slotName: 'defectState',
|
||||
dataIndex: 'defectState',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.updateUser',
|
||||
slotName: 'name',
|
||||
dataIndex: 'updateUser',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnLevel',
|
||||
dataIndex: 'level',
|
||||
showInTable: true,
|
||||
width: 200,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
];
|
||||
const {
|
||||
propsRes: linkPropsRes,
|
||||
propsEvent: linkTableEvent,
|
||||
loadList: loadLinkList,
|
||||
setLoadListParams: setLinkListParams,
|
||||
} = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEFECT,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
const testPlanColumns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'id',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectName',
|
||||
slotName: 'defectName',
|
||||
dataIndex: 'defectName',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.testPlan',
|
||||
slotName: 'testPlan',
|
||||
dataIndex: 'testPlan',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectState',
|
||||
slotName: 'defectState',
|
||||
dataIndex: 'defectState',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
];
|
||||
|
||||
const {
|
||||
propsRes: testPlanPropsRes,
|
||||
propsEvent: testPlanTableEvent,
|
||||
loadList: testPlanLinkList,
|
||||
setLoadListParams: setTestPlanListParams,
|
||||
} = useTable(getRecycleListRequest, {
|
||||
columns: testPlanColumns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEFECT_TEST_PLAN,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
function getFetch() {
|
||||
if (showType.value === 'link') {
|
||||
setLinkListParams({ keyword: keyword.value });
|
||||
loadLinkList();
|
||||
} else {
|
||||
setTestPlanListParams({ keyword: keyword.value });
|
||||
testPlanLinkList();
|
||||
}
|
||||
}
|
||||
// 取消关联
|
||||
function cancelLink(record: any) {}
|
||||
|
||||
const showDrawer = ref<boolean>(false);
|
||||
function createDefect() {
|
||||
showDrawer.value = true;
|
||||
}
|
||||
|
||||
const showLinkDrawer = ref<boolean>(false);
|
||||
|
||||
function linkDefect() {
|
||||
showLinkDrawer.value = true;
|
||||
}
|
||||
watch(
|
||||
() => showType.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
getFetch();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getFetch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="font-medium">{{ t('caseManagement.featureCase.caseReviewList') }}</div>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<a-button type="text" class="px-0">{{ record.name }}</a-button>
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<statusTag :status="record.status" />
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
sortIndex: 1,
|
||||
showTooltip: true,
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.caseReview.name',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.caseReview.status',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.reviewResult',
|
||||
slotName: 'reviewResult',
|
||||
dataIndex: 'reviewResult',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.reviewTime',
|
||||
slotName: 'reviewTime',
|
||||
dataIndex: 'reviewTime',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_REVIEW,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,216 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert v-if="isShowTip" class="mb-6" type="warning">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="w-[80%]">{{ t('caseManagement.featureCase.changeHistoryTip') }}</span>
|
||||
<span class="cursor-pointer text-[var(--color-text-2)]" @click="noRemindHandler">{{
|
||||
t('caseManagement.featureCase.noReminders')
|
||||
}}</span>
|
||||
</div>
|
||||
</a-alert>
|
||||
<ms-base-table v-bind="propsRes" v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<a-button type="text" class="px-0">{{ record.name }}</a-button>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsRemoveButton
|
||||
position="br"
|
||||
:title="
|
||||
t('caseManagement.featureCase.confirmRecoverChangeHistoryTitle', { name: characterLimit(record.name) })
|
||||
"
|
||||
:sub-title-tip="
|
||||
t('caseManagement.featureCase.recoverChangeHistoryTip', { name: characterLimit(record.name) })
|
||||
"
|
||||
:loading="recoverLoading"
|
||||
@ok="recoverHandler(record)"
|
||||
/>
|
||||
<MsButton @click="saveAsHandler(record)">{{ t('caseManagement.featureCase.saveAsVersion') }}</MsButton>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<a-modal
|
||||
v-model:visible="showModal"
|
||||
title-align="start"
|
||||
class="ms-modal-form ms-modal-medium"
|
||||
:ok-text="t('organization.member.Confirm')"
|
||||
:cancel-text="t('organization.member.Cancel')"
|
||||
unmount-on-close
|
||||
@close="handleCancel"
|
||||
>
|
||||
<template #title> {{ t('caseManagement.featureCase.saveAsVersion') }} </template>
|
||||
<div class="form">
|
||||
<a-form ref="versionFormRef" :model="form" size="large" layout="vertical">
|
||||
<a-form-item
|
||||
field="versionId"
|
||||
:label="t('caseManagement.featureCase.tableColumnVersion')"
|
||||
asterisk-position="end"
|
||||
:rules="[{ required: true, message: t('caseManagement.featureCase.saveAsVersionPlaceholder') }]"
|
||||
>
|
||||
<a-select
|
||||
v-model="form.versionId"
|
||||
multiple
|
||||
allow-clear
|
||||
:placeholder="t('organization.member.selectUserScope')"
|
||||
>
|
||||
<a-option v-for="item of versionOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
|
||||
</a-select>
|
||||
<MsFormItemSub :text="t('caseManagement.featureCase.saveAsVersionTip')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button>
|
||||
<a-button class="ml-[12px]" type="primary" :loading="confirmLoading" @click="handleOK">
|
||||
{{ t('common.save') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import { characterLimit } from '@/utils';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const visitedKey = 'notRemindChangeHistoryTip';
|
||||
const { addVisited } = useVisit(visitedKey);
|
||||
const { getIsVisited } = useVisit(visitedKey);
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.changeNumber',
|
||||
dataIndex: 'changeNumber',
|
||||
showTooltip: true,
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.changeType',
|
||||
slotName: 'changeType',
|
||||
dataIndex: 'changeType',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.operator',
|
||||
dataIndex: 'operator',
|
||||
slotName: 'operator',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnUpdateTime',
|
||||
slotName: 'updateTime',
|
||||
dataIndex: 'updateTime',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_CHANGE_HISTORY,
|
||||
scroll: { x: '100%' },
|
||||
selectable: true,
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
const form = ref({
|
||||
versionId: '',
|
||||
});
|
||||
|
||||
const versionOptions = ref([
|
||||
{
|
||||
id: '1001',
|
||||
name: 'v1.0',
|
||||
},
|
||||
{
|
||||
id: '1002',
|
||||
name: 'v1.1',
|
||||
},
|
||||
]);
|
||||
|
||||
const recoverLoading = ref<boolean>(false);
|
||||
// 恢复
|
||||
async function recoverHandler(record: any) {
|
||||
recoverLoading.value = true;
|
||||
try {
|
||||
Message.success(t('caseManagement.featureCase.recoveredSuccessfully'));
|
||||
resetSelector();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
recoverLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const showModal = ref<boolean>(false);
|
||||
|
||||
// 另存为版本
|
||||
function saveAsHandler(record: any) {
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
showModal.value = false;
|
||||
};
|
||||
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
const versionFormRef = ref<FormInstance | null>(null);
|
||||
|
||||
const handleOK = () => {
|
||||
versionFormRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
confirmLoading.value = true;
|
||||
Message.success(t('common.saveSuccess'));
|
||||
handleCancel();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isShowTip = ref<boolean>(true);
|
||||
|
||||
const noRemindHandler = () => {
|
||||
isShowTip.value = false;
|
||||
addVisited();
|
||||
};
|
||||
|
||||
const doCheckIsTip = () => {
|
||||
isShowTip.value = !getIsVisited();
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
doCheckIsTip();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -57,9 +57,12 @@
|
|||
|
||||
import { addDemandRequest, updateDemand } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import type { CreateOrUpdateDemand, DemandFormList, DemandItem } from '@/models/caseManagement/featureCase';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
caseId: string;
|
||||
|
@ -71,11 +74,11 @@
|
|||
(e: 'update:visible', v: boolean): void;
|
||||
(e: 'success'): void;
|
||||
}>();
|
||||
|
||||
const pageConfig = computed(() => appStore.pageConfig);
|
||||
const form = ref<CreateOrUpdateDemand>({
|
||||
id: '',
|
||||
caseId: props.caseId,
|
||||
demandPlatform: 'LOCAL',
|
||||
demandPlatform: pageConfig.value.platformName,
|
||||
});
|
||||
|
||||
const updateName = ref<string>('');
|
|
@ -0,0 +1,447 @@
|
|||
<template>
|
||||
<MsDrawer v-model:visible="innerVisible" :title="title" :width="1200" :footer="false" no-content-padding>
|
||||
<div class="flex h-full">
|
||||
<div class="w-[292px] border-r border-[var(--color-text-n8)] p-[16px]">
|
||||
<a-input
|
||||
v-model:model-value="moduleKeyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchTip')"
|
||||
allow-clear
|
||||
class="mb-[16px]"
|
||||
/>
|
||||
<div class="folder">
|
||||
<div :class="getFolderClass('all')" @click="setActiveFolder('all')">
|
||||
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||
<div class="folder-name">{{ t('caseManagement.featureCase.allCase') }}</div>
|
||||
<div class="folder-count">({{ allFileCount }})</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-divider class="my-[8px]" />
|
||||
<a-spin class="w-full" :loading="moduleLoading">
|
||||
<MsTree
|
||||
v-model:selected-keys="selectedModuleKeys"
|
||||
:data="folderTree"
|
||||
:keyword="moduleKeyword"
|
||||
:empty-text="t('caseManagement.featureCase.caseEmptyRecycle')"
|
||||
:virtual-list-props="virtualListProps"
|
||||
:field-names="{
|
||||
title: 'name',
|
||||
key: 'id',
|
||||
children: 'children',
|
||||
count: 'count',
|
||||
}"
|
||||
block-node
|
||||
title-tooltip-position="left"
|
||||
@select="folderNodeSelect"
|
||||
>
|
||||
<template #title="nodeData">
|
||||
<div class="inline-flex w-full">
|
||||
<div class="one-line-text w-[calc(100%-32px)] text-[var(--color-text-1)]">{{ nodeData.name }}</div>
|
||||
<div class="ml-[4px] text-[var(--color-text-4)]">({{ nodeData.count || 0 }})</div>
|
||||
</div>
|
||||
</template>
|
||||
</MsTree>
|
||||
</a-spin>
|
||||
</div>
|
||||
<div class="flex w-[calc(100%-293px)] flex-col p-[16px]">
|
||||
<div class="mb-[16px] flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-[4px] text-[var(--color-text-1)]">{{ activeFolderName }}</div>
|
||||
<div class="text-[var(--color-text-4)]">({{ activeFolderName }})</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-select
|
||||
v-model:model-value="version"
|
||||
:options="versionOptions"
|
||||
:placeholder="t('ms.case.associate.versionPlaceholder')"
|
||||
class="w-[200px]"
|
||||
allow-clear
|
||||
/>
|
||||
<a-input-search
|
||||
v-model="keyword"
|
||||
:placeholder="t('ms.case.associate.searchPlaceholder')"
|
||||
allow-clear
|
||||
class="w-[200px]"
|
||||
@press-enter="searchCase"
|
||||
@search="searchCase"
|
||||
/>
|
||||
<a-button type="outline" class="arco-btn-outline--secondary px-[8px]">
|
||||
<MsIcon type="icon-icon-filter" class="mr-[4px] text-[var(--color-text-4)]" />
|
||||
<div class="text-[var(--color-text-4)]">{{ t('common.filter') }}</div>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
<template #caseLevel="{ record }">
|
||||
<caseLevel :case-level="record.caseLevel" />
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<div class="footer">
|
||||
<div class="flex flex-1 items-center">
|
||||
<slot name="footerLeft"></slot>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<slot name="footerRight">
|
||||
<a-button type="secondary" :disabled="loading" class="mr-[12px]" @click="cancel">{{
|
||||
t('common.cancel')
|
||||
}}</a-button>
|
||||
<a-button type="primary" :loading="loading" @click="handleConfirm">
|
||||
{{ t('common.add') }}
|
||||
</a-button>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
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 caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||
|
||||
import { getModules } from '@/api/modules/project-management/fileManagement';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { mapTree } from '@/utils';
|
||||
|
||||
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
showType: 'preposition' | 'postPosition';
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'update:project', val: string): void;
|
||||
(e: 'init', val: string[]): void;
|
||||
(e: 'folderNodeSelect', ids: (string | number)[], springIds: string[]): void;
|
||||
(e: 'success', val: string[]): void;
|
||||
(e: 'close'): void;
|
||||
}>();
|
||||
|
||||
const innerVisible = computed({
|
||||
get() {
|
||||
return props.visible;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:visible', value);
|
||||
},
|
||||
});
|
||||
|
||||
const selectedModuleKeys = ref<string[]>([]);
|
||||
|
||||
const title = computed(() => {
|
||||
return props.showType === 'preposition' ? '添加前置用例' : '添加后置用例';
|
||||
});
|
||||
|
||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||
const moduleLoading = ref(false);
|
||||
|
||||
const moduleKeyword = ref('');
|
||||
|
||||
const virtualListProps = computed(() => {
|
||||
return {
|
||||
height: 'calc(100vh - 251px)',
|
||||
};
|
||||
});
|
||||
|
||||
const activeFolder = ref('all');
|
||||
const activeFolderName = ref(t('ms.case.associate.allCase'));
|
||||
/**
|
||||
* 处理文件夹树节点选中事件
|
||||
*/
|
||||
function folderNodeSelect(_selectedKeys: (string | number)[], node: MsTreeNodeData) {
|
||||
selectedModuleKeys.value = _selectedKeys as string[];
|
||||
activeFolder.value = node.id;
|
||||
activeFolderName.value = node.name;
|
||||
const offspringIds: string[] = [];
|
||||
mapTree(node.children || [], (e) => {
|
||||
offspringIds.push(e.id);
|
||||
return e;
|
||||
});
|
||||
|
||||
emit('folderNodeSelect', _selectedKeys, offspringIds);
|
||||
}
|
||||
const allFileCount = ref(0);
|
||||
|
||||
function getFolderClass(id: string) {
|
||||
return activeFolder.value === id ? 'folder-text folder-text--active' : 'folder-text';
|
||||
}
|
||||
|
||||
function setActiveFolder(id: string) {
|
||||
activeFolder.value = id;
|
||||
activeFolderName.value = t('ms.case.associate.allCase');
|
||||
selectedModuleKeys.value = [];
|
||||
emit('folderNodeSelect', [id], []);
|
||||
}
|
||||
const keyword = ref('');
|
||||
const version = ref('');
|
||||
const versionOptions = ref([
|
||||
{
|
||||
label: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
label: '版本1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '版本2',
|
||||
value: '2',
|
||||
},
|
||||
]);
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
sortIndex: 1,
|
||||
showTooltip: true,
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnName',
|
||||
dataIndex: 'name',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
showTooltip: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'ms.case.associate.caseLevel',
|
||||
dataIndex: 'caseLevel',
|
||||
slotName: 'caseLevel',
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnVersion',
|
||||
slotName: 'version',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnTag',
|
||||
dataIndex: 'tags',
|
||||
isTag: true,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
|
||||
() =>
|
||||
Promise.resolve({
|
||||
list: [
|
||||
{
|
||||
id: 'ded3d43',
|
||||
name: '测试评审1',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 0, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 100,
|
||||
passCount: 0,
|
||||
failCount: 10,
|
||||
reviewCount: 20,
|
||||
reviewingCount: 25,
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
{
|
||||
id: 'g545hj4',
|
||||
name: '测试评审2',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 1, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 105,
|
||||
passCount: 50,
|
||||
failCount: 10,
|
||||
reviewCount: 20,
|
||||
reviewingCount: 25,
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
{
|
||||
id: 'hj65b54',
|
||||
name: '测试评审3',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 2, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 125,
|
||||
passCount: 70,
|
||||
failCount: 10,
|
||||
reviewCount: 20,
|
||||
reviewingCount: 25,
|
||||
passRate: '80%',
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
{
|
||||
id: 'wefwefw',
|
||||
name: '测试评审4',
|
||||
creator: '张三',
|
||||
reviewer: '李四',
|
||||
module: '模块1',
|
||||
caseLevel: 3, // 未开始、进行中、已完成、已归档
|
||||
caseCount: 130,
|
||||
passCount: 70,
|
||||
failCount: 10,
|
||||
reviewCount: 0,
|
||||
reviewingCount: 50,
|
||||
passRate: '80%',
|
||||
tags: ['标签1', '标签2'],
|
||||
type: 'single',
|
||||
desc: 'douifd9304',
|
||||
cycle: [1700200794229, 1700200994229],
|
||||
},
|
||||
],
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 2,
|
||||
}),
|
||||
{
|
||||
columns,
|
||||
scroll: {
|
||||
x: '100%',
|
||||
},
|
||||
showSetting: false,
|
||||
selectable: true,
|
||||
showSelectAll: true,
|
||||
},
|
||||
(item) => {
|
||||
return {
|
||||
...item,
|
||||
tags: item.tags?.map((e: string) => ({ id: e, name: e })) || [],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
loading.value = true;
|
||||
Message.success(t('ms.case.associate.associateSuccess'));
|
||||
innerVisible.value = false;
|
||||
emit('success', Array.from(propsRes.value.selectedKeys));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
innerVisible.value = false;
|
||||
emit('close');
|
||||
}
|
||||
/**
|
||||
* 初始化模块树
|
||||
* @param isSetDefaultKey 是否设置第一个节点为选中节点
|
||||
*/
|
||||
async function initModules(isSetDefaultKey = false) {
|
||||
try {
|
||||
moduleLoading.value = true;
|
||||
const res = await getModules(appStore.currentProjectId);
|
||||
folderTree.value = res;
|
||||
if (isSetDefaultKey) {
|
||||
selectedModuleKeys.value = [folderTree.value[0].id];
|
||||
activeFolderName.value = folderTree.value[0].name;
|
||||
const offspringIds: string[] = [];
|
||||
mapTree(folderTree.value[0].children || [], (e) => {
|
||||
offspringIds.push(e.id);
|
||||
return e;
|
||||
});
|
||||
|
||||
emit('folderNodeSelect', selectedModuleKeys.value, offspringIds);
|
||||
}
|
||||
emit(
|
||||
'init',
|
||||
folderTree.value.map((e) => e.name)
|
||||
);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
moduleLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function searchCase() {
|
||||
setLoadListParams({
|
||||
version: version.value,
|
||||
keyword: keyword.value,
|
||||
});
|
||||
loadList();
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
searchCase();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
initModules,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.folder {
|
||||
@apply flex cursor-pointer items-center justify-between;
|
||||
|
||||
padding: 8px 4px;
|
||||
border-radius: var(--border-radius-small);
|
||||
&:hover {
|
||||
background-color: rgb(var(--primary-1));
|
||||
}
|
||||
.folder-text {
|
||||
@apply flex cursor-pointer items-center;
|
||||
.folder-icon {
|
||||
margin-right: 4px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.folder-name {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.folder-count {
|
||||
margin-left: 4px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
}
|
||||
.folder-text--active {
|
||||
.folder-icon,
|
||||
.folder-name,
|
||||
.folder-count {
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
@apply flex items-center justify-between;
|
||||
|
||||
margin: auto -16px -16px;
|
||||
padding: 12px 16px;
|
||||
box-shadow: 0 -1px 4px 0 rgb(31 35 41 / 10%);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,171 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<a-button v-if="showType === 'preposition'" class="mr-3" type="primary" @click="addCase('preposition')">
|
||||
{{ t('caseManagement.featureCase.addPresetCase') }}
|
||||
</a-button>
|
||||
<a-button v-else type="primary" @click="addCase('postPosition')">
|
||||
{{ t('caseManagement.featureCase.addPostCase') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div>
|
||||
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type ml-[4px]">
|
||||
<a-radio value="preposition" class="show-type-icon p-[2px]">{{
|
||||
t('caseManagement.featureCase.preCase')
|
||||
}}</a-radio>
|
||||
<a-radio value="postPosition" class="show-type-icon p-[2px]">{{
|
||||
t('caseManagement.featureCase.postCase')
|
||||
}}</a-radio>
|
||||
</a-radio-group>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
></a-input-search>
|
||||
</div>
|
||||
</div>
|
||||
<ms-base-table v-if="showType === 'preposition'" ref="tableRef" v-bind="prePropsRes" v-on="preTableEvent">
|
||||
<template #operation="{ record }">
|
||||
<MsButton @click="cancelDependency(record)">{{ t('caseManagement.featureCase.cancelDependency') }}</MsButton>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<ms-base-table v-else v-bind="postPropsRes" v-on="postTableEvent">
|
||||
<template #operation="{ record }">
|
||||
<MsButton @click="cancelDependency(record)">{{ t('caseManagement.featureCase.cancelDependency') }}</MsButton>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<PreAndPostCaseDrawer ref="drawerRef" v-model:visible="showDrawer" :show-type="showType" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import PreAndPostCaseDrawer from './preAndPostCaseDrawer.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
export type types = 'preposition' | 'postPosition';
|
||||
|
||||
const showType = ref<types>('preposition');
|
||||
const { t } = useI18n();
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'id',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnName',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnVersion',
|
||||
slotName: 'defectState',
|
||||
dataIndex: 'defectState',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateUser',
|
||||
slotName: 'createUser',
|
||||
dataIndex: 'createUser',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
];
|
||||
|
||||
const {
|
||||
propsRes: prePropsRes,
|
||||
propsEvent: preTableEvent,
|
||||
loadList: loadPreList,
|
||||
setLoadListParams: setPreListParams,
|
||||
} = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEPENDENCY_PRE_CASE,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
const {
|
||||
propsRes: postPropsRes,
|
||||
propsEvent: postTableEvent,
|
||||
loadList: loadPostList,
|
||||
setLoadListParams: setPostListParams,
|
||||
} = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEPENDENCY_POST_CASE,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
// 取消依赖
|
||||
function cancelDependency(record: any) {}
|
||||
|
||||
function getFetch() {
|
||||
if (showType.value === 'preposition') {
|
||||
setPreListParams({ keyword: keyword.value });
|
||||
loadPreList();
|
||||
} else {
|
||||
setPostListParams({ keyword: keyword.value });
|
||||
loadPostList();
|
||||
}
|
||||
}
|
||||
|
||||
const showDrawer = ref<boolean>(false);
|
||||
const drawerRef = ref();
|
||||
// 添加前后置用例
|
||||
function addCase(type: string) {
|
||||
showDrawer.value = true;
|
||||
drawerRef.value.initModules();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => showType.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
getFetch();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -92,7 +92,6 @@
|
|||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
@change="handleChange"
|
||||
>
|
||||
<template #upload-button>
|
||||
<a-button type="text" class="!text-[var(--color-text-1)]">
|
||||
|
@ -116,23 +115,42 @@
|
|||
</a-form>
|
||||
<!-- 文件列表开始 -->
|
||||
<div class="w-[90%]">
|
||||
<MsFileList ref="fileListRef" v-model:file-list="fileList" mode="static">
|
||||
<MsFileList
|
||||
ref="fileListRef"
|
||||
v-model:file-list="fileList"
|
||||
:show-tab="false"
|
||||
:request-params="{
|
||||
caseId: detailForm.id,
|
||||
projectId: currentProjectId,
|
||||
}"
|
||||
:upload-func="uploadOrAssociationFile"
|
||||
:handle-delete="deleteFileHandler"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
<!-- 本地文件 -->
|
||||
<div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
|
||||
<MsButton type="button" status="danger" class="!mr-[4px]" @click="transferFile(item)">
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="transferFile(item)">
|
||||
{{ t('caseManagement.featureCase.storage') }}
|
||||
</MsButton>
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
|
||||
<MsButton
|
||||
v-if="item.status === 'done'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<!-- 关联文件 -->
|
||||
<div v-else class="flex flex-nowrap">
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="cancelAssociated(item)">
|
||||
{{ t('caseManagement.featureCase.cancelLink') }}
|
||||
</MsButton>
|
||||
<MsButton type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
|
||||
<MsButton
|
||||
v-if="item.status === 'done'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
|
@ -140,20 +158,6 @@
|
|||
</MsFileList>
|
||||
</div>
|
||||
</div>
|
||||
<MsUpload
|
||||
v-model:file-list="fileList"
|
||||
accept="none"
|
||||
:auto-upload="false"
|
||||
:sub-text="acceptType === 'jar' ? '' : t('project.fileManagement.normalFileSubText', { size: 50 })"
|
||||
multiple
|
||||
draggable
|
||||
size-unit="MB"
|
||||
:max-size="50"
|
||||
:is-all-screen="true"
|
||||
class="mb-[16px]"
|
||||
:cut-height="50"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -163,21 +167,26 @@
|
|||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||
import type { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
import AddStep from './addStep.vue';
|
||||
import AddStep from '../addStep.vue';
|
||||
|
||||
import { updateCaseRequest } from '@/api/modules/case-management/featureCase';
|
||||
import {
|
||||
deleteFileOrCancelAssociation,
|
||||
downloadFileRequest,
|
||||
transferFileRequest,
|
||||
updateCaseRequest,
|
||||
uploadOrAssociationFile,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useFormCreateStore from '@/store/modules/form-create/form-create';
|
||||
import { getGenerateId } from '@/utils';
|
||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
|
||||
import type { StepList } from '@/models/caseManagement/featureCase';
|
||||
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||
|
||||
import { convertToFile } from './utils';
|
||||
import { convertToFile } from '../utils';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
|
||||
const formCreateStore = useFormCreateStore();
|
||||
|
@ -254,30 +263,10 @@
|
|||
const fileList = ref<MsFileItem[]>([]);
|
||||
const acceptType = ref('none'); // 模块-上传文件类型
|
||||
|
||||
function beforeUpload() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
||||
fileList.value = _fileList.map((e) => {
|
||||
return {
|
||||
...e,
|
||||
enable: true, // 是否启用
|
||||
local: true, // 是否本地文件
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const showDrawer = ref<boolean>(false);
|
||||
function associatedFile() {
|
||||
showDrawer.value = true;
|
||||
}
|
||||
// 转存
|
||||
function transferFile(item: any) {}
|
||||
// 下载
|
||||
function downloadFile(item: any) {}
|
||||
// 取消关联
|
||||
function cancelAssociated(item: any) {}
|
||||
|
||||
const attachmentsList = ref([]);
|
||||
|
||||
|
@ -319,13 +308,22 @@
|
|||
|
||||
// 取消关联文件id
|
||||
const unLinkFilesIds = computed(() => {
|
||||
return associateFileIds.value.filter((id: string) => !currentAlreadyAssociateFileList.value.includes(id));
|
||||
const deleteAssociateFileIds = fileList.value
|
||||
.filter(
|
||||
(item: any) =>
|
||||
!currentAlreadyAssociateFileList.value.includes(item.uid) && associateFileIds.value.includes(item.uid)
|
||||
)
|
||||
.map((item) => item.uid);
|
||||
return associateFileIds.value.filter(
|
||||
(id: string) => !currentAlreadyAssociateFileList.value.includes(id) && !deleteAssociateFileIds.includes(id)
|
||||
);
|
||||
});
|
||||
|
||||
const formRuleList = computed(() =>
|
||||
formCreateStore.formCreateRuleMap.get(FormCreateKeyEnum.CASE_CUSTOM_ATTRS_DETAIL)
|
||||
);
|
||||
|
||||
// 处理编辑详情参数
|
||||
function getParams() {
|
||||
const steps = stepData.value.map((item, index) => {
|
||||
return {
|
||||
|
@ -347,7 +345,7 @@
|
|||
deleteFileMetaIds: deleteFileMetaIds.value,
|
||||
unLinkFilesIds: unLinkFilesIds.value,
|
||||
newAssociateFileListIds: newAssociateFileListIds.value,
|
||||
tags: JSON.parse(detailForm.value.tags),
|
||||
tags: detailForm.value.tags.length ? JSON.parse(detailForm.value.tags) : detailForm.value.tags,
|
||||
customFields: customFieldsMaps,
|
||||
},
|
||||
fileList: fileList.value.filter((item: any) => item.status === 'init'), // 总文件列表
|
||||
|
@ -379,6 +377,73 @@
|
|||
isEditPreposition.value = false;
|
||||
}
|
||||
|
||||
const fileListRef = ref<InstanceType<typeof MsFileList>>();
|
||||
async function startUpload() {
|
||||
await fileListRef.value?.startUpload();
|
||||
emit('updateSuccess');
|
||||
}
|
||||
|
||||
function beforeUpload(file: File) {
|
||||
const _maxSize = 50 * 1024 * 1024;
|
||||
if (file.size > _maxSize) {
|
||||
Message.warning(t('ms.upload.overSize'));
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
// 删除本地文件
|
||||
async function deleteFileHandler(item: MsFileItem) {
|
||||
try {
|
||||
const params = {
|
||||
id: item.uid,
|
||||
local: item.local,
|
||||
caseId: detailForm.value.id,
|
||||
projectId: currentProjectId.value,
|
||||
};
|
||||
await deleteFileOrCancelAssociation(params);
|
||||
Message.success(
|
||||
item.local ? t('caseManagement.featureCase.deleteSuccess') : t('caseManagement.featureCase.cancelLinkSuccess')
|
||||
);
|
||||
emit('updateSuccess');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 转存
|
||||
async function transferFile(item: MsFileItem) {
|
||||
try {
|
||||
const prams = {
|
||||
projectId: currentProjectId.value,
|
||||
caseId: detailForm.value.id,
|
||||
fileId: item.uid,
|
||||
local: true,
|
||||
};
|
||||
await transferFileRequest(prams);
|
||||
Message.success(t('caseManagement.featureCase.transferFileSuccess'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
async function downloadFile(item: MsFileItem) {
|
||||
try {
|
||||
const prams = {
|
||||
projectId: currentProjectId.value,
|
||||
caseId: detailForm.value.id,
|
||||
fileId: item.uid,
|
||||
local: true,
|
||||
};
|
||||
const res = await downloadFileRequest(prams);
|
||||
downloadByteFile(res, `${item.name}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取详情
|
||||
function getDetails() {
|
||||
const { steps, attachments } = detailForm.value;
|
||||
if (steps) {
|
||||
|
@ -437,6 +502,20 @@
|
|||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 文件列表单个上传
|
||||
watch(
|
||||
() => fileList.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (val.filter((item) => item.status === 'init').length) {
|
||||
setTimeout(() => {
|
||||
startUpload();
|
||||
}, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="font-medium">{{ t('caseManagement.featureCase.testPlanList') }}</div>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<a-button type="text" class="px-0">{{ record.name }}</a-button>
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<statusTag :status="record.status" />
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
||||
|
||||
import { getRecycleListRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.defectID',
|
||||
dataIndex: 'id',
|
||||
showTooltip: true,
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.testPlanName',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.projectName',
|
||||
dataIndex: 'projectName',
|
||||
slotName: 'projectName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.planStatus',
|
||||
slotName: 'planStatus',
|
||||
dataIndex: 'planStatus',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnExecutionResult',
|
||||
slotName: 'executionResult',
|
||||
dataIndex: 'executionResult',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.executionTime',
|
||||
slotName: 'executionTime',
|
||||
dataIndex: 'executionTime',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getRecycleListRequest, {
|
||||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_TEST_PLAN,
|
||||
scroll: { x: '100%' },
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -115,6 +115,8 @@ export function convertToFile(fileInfo: AssociatedList): MsFileItem {
|
|||
uid: fileInfo.id,
|
||||
url: `${gatewayAddress}/${fileInfo.filePath || ''}`,
|
||||
local: fileInfo.local,
|
||||
deleteContent: fileInfo.local ? '' : 'caseManagement.featureCase.cancelLink',
|
||||
};
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -126,8 +126,6 @@
|
|||
const activeCaseType = ref<'folder' | 'module'>('folder'); // 激活用例类型
|
||||
const rootModulesName = ref<string[]>([]); // 根模块名称列表
|
||||
|
||||
const publicCaseCount = ref<number>(0); // 公共用例数量
|
||||
|
||||
// 全部展开或折叠
|
||||
const expandHandler = () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
|
|
|
@ -155,4 +155,40 @@ export default {
|
|||
'caseManagement.featureCase.contentEdit': 'Content Edit',
|
||||
'caseManagement.featureCase.followSuccess': 'Followed Success',
|
||||
'caseManagement.featureCase.cancelFollowSuccess': 'Cancel success',
|
||||
'caseManagement.featureCase.transferFileSuccess': 'Successful transfer',
|
||||
'caseManagement.featureCase.defectName': 'Defect name',
|
||||
'caseManagement.featureCase.defectState': 'Defect state',
|
||||
'caseManagement.featureCase.createDefect': 'Create defect',
|
||||
'caseManagement.featureCase.testPlanLinkList': 'Test plan association list',
|
||||
'caseManagement.featureCase.directLink': 'Direct correlation',
|
||||
'caseManagement.featureCase.linkDefect': 'Associated defect',
|
||||
'caseManagement.featureCase.IterationPlan': 'Iteration plan',
|
||||
'caseManagement.featureCase.cancelDependency': 'Cancel dependencies',
|
||||
'caseManagement.featureCase.reviewTime': 'Review time',
|
||||
'caseManagement.featureCase.defectID': 'Defect ID',
|
||||
'caseManagement.featureCase.testPlanName': 'Plan Name',
|
||||
'caseManagement.featureCase.projectName': 'Project',
|
||||
'caseManagement.featureCase.planStatus': 'Planned status',
|
||||
'caseManagement.featureCase.executionTime': 'Execution time',
|
||||
'caseManagement.featureCase.noReminders': 'No longer remind',
|
||||
'caseManagement.featureCase.changeNumber': 'Change sequence',
|
||||
'caseManagement.featureCase.changeType': 'type',
|
||||
'caseManagement.featureCase.operator': 'operator',
|
||||
'caseManagement.featureCase.cancelLinkSuccess': 'Cancel association successfully',
|
||||
'caseManagement.featureCase.preview': 'Preview',
|
||||
'caseManagement.featureCase.defectList': 'Defect list',
|
||||
'caseManagement.featureCase.addPresetCase': 'Add preset use case',
|
||||
'caseManagement.featureCase.addPostCase': 'Add the rear case',
|
||||
'caseManagement.featureCase.preCase': 'Prerequisite use case',
|
||||
'caseManagement.featureCase.postCase': 'Post use case',
|
||||
'caseManagement.featureCase.caseReviewList': 'Use case review list',
|
||||
'caseManagement.featureCase.changeHistoryTip': `View and compare historical changes. According to the administrator's setting rules, historical changes will be automatically deleted`,
|
||||
'caseManagement.featureCase.recoverChangeHistoryTip':
|
||||
'Restores the current use case to the history of {name}, adding a recovery to the change list',
|
||||
'caseManagement.featureCase.confirmRecoverChangeHistoryTitle': 'Confirm the recovery sequence {name}',
|
||||
'caseManagement.featureCase.saveAsVersion': 'Save as version',
|
||||
'caseManagement.featureCase.saveAsVersionPlaceholder': 'Please select the saved version',
|
||||
'caseManagement.featureCase.saveAsVersionTip': `After saving, add a use case with the current serial number content to the selected version`,
|
||||
'caseManagement.featureCase.testPlanList': 'Test plan list',
|
||||
'caseManagement.featureCase.reviewResult': 'review Result',
|
||||
};
|
||||
|
|
|
@ -153,4 +153,39 @@ export default {
|
|||
'caseManagement.featureCase.contentEdit': '内容编辑',
|
||||
'caseManagement.featureCase.followSuccess': '关注成功',
|
||||
'caseManagement.featureCase.cancelFollowSuccess': '取消成功',
|
||||
'caseManagement.featureCase.transferFileSuccess': '转存成功',
|
||||
'caseManagement.featureCase.defectName': '缺陷名称',
|
||||
'caseManagement.featureCase.defectState': '缺陷状态',
|
||||
'caseManagement.featureCase.createDefect': '创建缺陷',
|
||||
'caseManagement.featureCase.testPlanLinkList': '测试计划关联列表',
|
||||
'caseManagement.featureCase.directLink': '直接关联',
|
||||
'caseManagement.featureCase.linkDefect': '关联缺陷',
|
||||
'caseManagement.featureCase.IterationPlan': '迭代计划',
|
||||
'caseManagement.featureCase.cancelDependency': '取消依赖',
|
||||
'caseManagement.featureCase.reviewTime': '评审时间',
|
||||
'caseManagement.featureCase.defectID': '缺陷ID',
|
||||
'caseManagement.featureCase.testPlanName': '计划名称',
|
||||
'caseManagement.featureCase.projectName': '所属项目',
|
||||
'caseManagement.featureCase.planStatus': '计划状态',
|
||||
'caseManagement.featureCase.executionTime': '执行时间',
|
||||
'caseManagement.featureCase.noReminders': '不再提醒',
|
||||
'caseManagement.featureCase.changeNumber': '变更序号',
|
||||
'caseManagement.featureCase.changeType': '类型',
|
||||
'caseManagement.featureCase.operator': '操作人',
|
||||
'caseManagement.featureCase.cancelLinkSuccess': '取消关联成功',
|
||||
'caseManagement.featureCase.preview': '预览',
|
||||
'caseManagement.featureCase.defectList': '缺陷列表',
|
||||
'caseManagement.featureCase.addPresetCase': '添加前置用例',
|
||||
'caseManagement.featureCase.addPostCase': '添加后置用例',
|
||||
'caseManagement.featureCase.preCase': '前置用例',
|
||||
'caseManagement.featureCase.postCase': '后置用例',
|
||||
'caseManagement.featureCase.caseReviewList': '用例评审列表',
|
||||
'caseManagement.featureCase.changeHistoryTip': '查看、对比历史修改,根据管理员设置规则,变更历史数据将自动删除',
|
||||
'caseManagement.featureCase.recoverChangeHistoryTip': '将当前用例恢复到 {name} 的历史,在变更列表增加一条恢复数据',
|
||||
'caseManagement.featureCase.confirmRecoverChangeHistoryTitle': '确认恢复序号 {name} 吗',
|
||||
'caseManagement.featureCase.saveAsVersion': '另存为版本',
|
||||
'caseManagement.featureCase.saveAsVersionPlaceholder': '请选择另存的版本',
|
||||
'caseManagement.featureCase.saveAsVersionTip': '另存后,选择的版本内,增加一条当前序号内容的用例',
|
||||
'caseManagement.featureCase.testPlanList': '测试计划列表',
|
||||
'caseManagement.featureCase.reviewResult': '评审结果',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue