feat(功能用例): 详情和详情编辑显示设置页面
This commit is contained in:
parent
b4d8149181
commit
7e42c5cc3b
|
@ -1,21 +1,26 @@
|
||||||
import MSR from '@/api/http/index';
|
import MSR from '@/api/http/index';
|
||||||
import {
|
import {
|
||||||
|
AddDemandUrl,
|
||||||
|
BatchAssociationDemandUrl,
|
||||||
BatchCopyCaseUrl,
|
BatchCopyCaseUrl,
|
||||||
BatchDeleteCaseUrl,
|
BatchDeleteCaseUrl,
|
||||||
BatchDeleteRecycleCaseListUrl,
|
BatchDeleteRecycleCaseListUrl,
|
||||||
BatchEditCaseUrl,
|
BatchEditCaseUrl,
|
||||||
BatchMoveCaseUrl,
|
BatchMoveCaseUrl,
|
||||||
|
CancelAssociationDemandUrl,
|
||||||
CreateCaseModuleTreeUrl,
|
CreateCaseModuleTreeUrl,
|
||||||
CreateCaseUrl,
|
CreateCaseUrl,
|
||||||
DeleteCaseModuleTreeUrl,
|
DeleteCaseModuleTreeUrl,
|
||||||
DeleteCaseUrl,
|
DeleteCaseUrl,
|
||||||
DeleteRecycleCaseListUrl,
|
DeleteRecycleCaseListUrl,
|
||||||
DetailCaseUrl,
|
DetailCaseUrl,
|
||||||
|
FollowerCaseUrl,
|
||||||
GetAssociatedFilePageUrl,
|
GetAssociatedFilePageUrl,
|
||||||
GetCaseListUrl,
|
GetCaseListUrl,
|
||||||
GetCaseModulesCountUrl,
|
GetCaseModulesCountUrl,
|
||||||
GetCaseModuleTreeUrl,
|
GetCaseModuleTreeUrl,
|
||||||
GetDefaultTemplateFieldsUrl,
|
GetDefaultTemplateFieldsUrl,
|
||||||
|
GetDemandListUrl,
|
||||||
GetRecycleCaseListUrl,
|
GetRecycleCaseListUrl,
|
||||||
GetRecycleCaseModulesCountUrl,
|
GetRecycleCaseModulesCountUrl,
|
||||||
GetTrashCaseModuleTreeUrl,
|
GetTrashCaseModuleTreeUrl,
|
||||||
|
@ -24,6 +29,7 @@ import {
|
||||||
RestoreCaseListUrl,
|
RestoreCaseListUrl,
|
||||||
UpdateCaseModuleTreeUrl,
|
UpdateCaseModuleTreeUrl,
|
||||||
UpdateCaseUrl,
|
UpdateCaseUrl,
|
||||||
|
UpdateDemandUrl,
|
||||||
} from '@/api/requrls/case-management/featureCase';
|
} from '@/api/requrls/case-management/featureCase';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -33,8 +39,10 @@ import type {
|
||||||
BatchMoveOrCopyType,
|
BatchMoveOrCopyType,
|
||||||
CaseManagementTable,
|
CaseManagementTable,
|
||||||
CaseModuleQueryParams,
|
CaseModuleQueryParams,
|
||||||
|
CreateOrUpdateDemand,
|
||||||
CreateOrUpdateModule,
|
CreateOrUpdateModule,
|
||||||
DeleteCaseType,
|
DeleteCaseType,
|
||||||
|
DemandItem,
|
||||||
ModulesTreeType,
|
ModulesTreeType,
|
||||||
MoveModules,
|
MoveModules,
|
||||||
UpdateModule,
|
UpdateModule,
|
||||||
|
@ -86,6 +94,10 @@ export function getCaseDefaultFields(projectId: string) {
|
||||||
export function getAssociatedFileListUrl(data: TableQueryParams) {
|
export function getAssociatedFileListUrl(data: TableQueryParams) {
|
||||||
return MSR.post<CommonList<AssociatedList>>({ url: GetAssociatedFilePageUrl, data });
|
return MSR.post<CommonList<AssociatedList>>({ url: GetAssociatedFilePageUrl, data });
|
||||||
}
|
}
|
||||||
|
// 关注用例
|
||||||
|
export function followerCaseRequest(data: { userId: string; functionalCaseId: string }) {
|
||||||
|
return MSR.post({ url: FollowerCaseUrl, data });
|
||||||
|
}
|
||||||
// 创建用例
|
// 创建用例
|
||||||
export function createCaseRequest(data: Record<string, any>) {
|
export function createCaseRequest(data: Record<string, any>) {
|
||||||
return MSR.uploadFile({ url: CreateCaseUrl }, { request: data.request, fileList: data.fileList }, '', true);
|
return MSR.uploadFile({ url: CreateCaseUrl }, { request: data.request, fileList: data.fileList }, '', true);
|
||||||
|
@ -146,4 +158,31 @@ export function recoverRecycleCase(id: string) {
|
||||||
export function deleteRecycleCaseList(id: string) {
|
export function deleteRecycleCaseList(id: string) {
|
||||||
return MSR.get({ url: `${DeleteRecycleCaseListUrl}/${id}` });
|
return MSR.get({ url: `${DeleteRecycleCaseListUrl}/${id}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关联需求
|
||||||
|
|
||||||
|
// 已关联需求列表
|
||||||
|
export function getDemandList(data: TableQueryParams) {
|
||||||
|
return MSR.post<CommonList<DemandItem[]>>({ url: GetDemandListUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加需求
|
||||||
|
export function addDemandRequest(data: CreateOrUpdateDemand) {
|
||||||
|
return MSR.post({ url: AddDemandUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新需求
|
||||||
|
export function updateDemand(data: CreateOrUpdateDemand) {
|
||||||
|
return MSR.post({ url: UpdateDemandUrl, data });
|
||||||
|
}
|
||||||
|
// 批量关联需求
|
||||||
|
export function batchAssociationDemand(data: CreateOrUpdateDemand) {
|
||||||
|
return MSR.post({ url: BatchAssociationDemandUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消关联
|
||||||
|
export function cancelAssociationDemand(id: string) {
|
||||||
|
return MSR.get({ url: `${CancelAssociationDemandUrl}/${id}` });
|
||||||
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -59,4 +59,29 @@ export const RecoverRecycleCaseListUrl = '/functional/case/trash/recover';
|
||||||
// 删除回收站单个用例
|
// 删除回收站单个用例
|
||||||
export const DeleteRecycleCaseListUrl = '/functional/case/trash/delete';
|
export const DeleteRecycleCaseListUrl = '/functional/case/trash/delete';
|
||||||
|
|
||||||
|
// 关联需求
|
||||||
|
|
||||||
|
// 已关联需求列表
|
||||||
|
export const GetDemandListUrl = '/functional/case/demand/page';
|
||||||
|
// 添加需求
|
||||||
|
export const AddDemandUrl = '/functional/case/demand/add';
|
||||||
|
// 更新需求
|
||||||
|
export const UpdateDemandUrl = '/functional/case/demand/update';
|
||||||
|
// 批量关联需求
|
||||||
|
export const BatchAssociationDemandUrl = '/functional/case/demand/batch/relevance';
|
||||||
|
// 取消关联
|
||||||
|
export const CancelAssociationDemandUrl = '/functional/case/demand/cancel';
|
||||||
|
|
||||||
|
// 附件管理
|
||||||
|
// 上传文件并关联用例
|
||||||
|
export const UploadOrAssociationFileUrl = '/attachment/upload/file';
|
||||||
|
// 转存文件
|
||||||
|
export const TransferFileUrl = '/attachment/transfer';
|
||||||
|
// 预览文件
|
||||||
|
export const PreviewFileUrl = '/attachment/preview';
|
||||||
|
// 下载文件
|
||||||
|
export const DownloadFileUrl = '/attachment/download';
|
||||||
|
// 删除文件或取消关联用例文件
|
||||||
|
export const deleteFileOrCancelAssociationUrl = '/attachment/delete/file';
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -757,3 +757,12 @@
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 徽标 ****/
|
||||||
|
.arco-badge-number {
|
||||||
|
width: 30px !important;
|
||||||
|
height: 16px !important;
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
background: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,12 @@
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="flex w-full items-center">
|
<div class="flex w-full items-center">
|
||||||
{{ props.title }}
|
<a-tooltip :content="props.title" position="bottom">
|
||||||
|
<div class="one-line-text max-w-[300px]">
|
||||||
|
{{ props.title }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
<MsPrevNextButton
|
<MsPrevNextButton
|
||||||
ref="prevNextButtonRef"
|
ref="prevNextButtonRef"
|
||||||
v-model:loading="loading"
|
v-model:loading="loading"
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
isLimit: boolean; // 是否限制文件大小
|
isLimit: boolean; // 是否限制文件大小
|
||||||
draggable: boolean; // 是否支持拖拽上传
|
draggable: boolean; // 是否支持拖拽上传
|
||||||
isAllScreen?: boolean; // 是否是全屏显示拖拽上传
|
isAllScreen?: boolean; // 是否是全屏显示拖拽上传
|
||||||
|
cutHeight: number; // 被剪切高度
|
||||||
}> & {
|
}> & {
|
||||||
accept: UploadType;
|
accept: UploadType;
|
||||||
fileList: MsFileItem[];
|
fileList: MsFileItem[];
|
||||||
|
@ -95,6 +96,7 @@
|
||||||
showSubText: true,
|
showSubText: true,
|
||||||
isLimit: true,
|
isLimit: true,
|
||||||
isAllScreen: false,
|
isAllScreen: false,
|
||||||
|
cutHeight: 110,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:fileList', 'change']);
|
const emit = defineEmits(['update:fileList', 'change']);
|
||||||
|
@ -144,7 +146,7 @@
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
total.value = '100vh';
|
total.value = '100vh';
|
||||||
other.value = '110px';
|
other.value = `${props.cutHeight}px`;
|
||||||
showDropArea.value = false;
|
showDropArea.value = false;
|
||||||
} else {
|
} else {
|
||||||
total.value = '154px';
|
total.value = '154px';
|
||||||
|
|
|
@ -9,6 +9,8 @@ export enum FormCreateKeyEnum {
|
||||||
CASE_MANAGEMENT_FIELD = 'caseManagementFields',
|
CASE_MANAGEMENT_FIELD = 'caseManagementFields',
|
||||||
// 自定义属性
|
// 自定义属性
|
||||||
CASE_CUSTOM_ATTRS = 'caseCustomAttributes',
|
CASE_CUSTOM_ATTRS = 'caseCustomAttributes',
|
||||||
|
// 用例tab详情字段
|
||||||
|
CASE_CUSTOM_ATTRS_DETAIL = 'caseCustomAttributesDetail',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -33,6 +33,7 @@ export enum TableKeyEnum {
|
||||||
CASE_MANAGEMENT_DETAIL_TABLE = 'caseManagementDetailTable',
|
CASE_MANAGEMENT_DETAIL_TABLE = 'caseManagementDetailTable',
|
||||||
CASE_MANAGEMENT_ASSOCIATED_TABLE = 'caseManagementAssociatedFileTable',
|
CASE_MANAGEMENT_ASSOCIATED_TABLE = 'caseManagementAssociatedFileTable',
|
||||||
BUG_MANAGEMENT = 'bugManagement',
|
BUG_MANAGEMENT = 'bugManagement',
|
||||||
|
CASE_MANAGEMENT_DEMAND = 'caseManagementDemand',
|
||||||
CASE_MANAGEMENT_REVIEW = 'caseManagementReview',
|
CASE_MANAGEMENT_REVIEW = 'caseManagementReview',
|
||||||
CASE_MANAGEMENT_REVIEW_CASE = 'caseManagementReviewCase',
|
CASE_MANAGEMENT_REVIEW_CASE = 'caseManagementReviewCase',
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,9 +176,41 @@ export interface CaseModuleQueryParams extends TableQueryParams {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export interface BatchParams extends BatchMoveOrCopyType {
|
export interface TabItemType {
|
||||||
// selectIds: string[];
|
key: string;
|
||||||
// selectAll: boolean;
|
title: string;
|
||||||
// moduleIds: string[];
|
enable: boolean;
|
||||||
// projectId: string;
|
}
|
||||||
// }
|
|
||||||
|
// 需求
|
||||||
|
export interface DemandItem {
|
||||||
|
id: string;
|
||||||
|
caseId: string; // 功能用例ID
|
||||||
|
demandId: string; // 需求ID
|
||||||
|
demandName: string; // 需求标题
|
||||||
|
demandUrl: string; // 需求地址
|
||||||
|
demandPlatform: string; // 需求所属平台
|
||||||
|
createTime: string;
|
||||||
|
updateTime: string;
|
||||||
|
createUser: string;
|
||||||
|
updateUser: string;
|
||||||
|
children: DemandItem[]; // 平台下对应的需求
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平台需求列表
|
||||||
|
export interface DemandFormList {
|
||||||
|
demandId: string;
|
||||||
|
demandName: string;
|
||||||
|
demandUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建需求&编辑需求
|
||||||
|
export interface CreateOrUpdateDemand {
|
||||||
|
id?: string;
|
||||||
|
caseId: string;
|
||||||
|
demandPlatform: string;
|
||||||
|
demandList?: DemandFormList[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DemandList = DemandItem[];
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { getCaseModulesCounts, getRecycleModulesCounts } from '@/api/modules/case-management/featureCase';
|
import { getCaseModulesCounts, getRecycleModulesCounts } from '@/api/modules/case-management/featureCase';
|
||||||
|
|
||||||
import type { CaseModuleQueryParams } from '@/models/caseManagement/featureCase';
|
import type { CaseModuleQueryParams, TabItemType } from '@/models/caseManagement/featureCase';
|
||||||
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
import { ModuleTreeNode } from '@/models/projectManagement/file';
|
||||||
|
|
||||||
const useFeatureCaseStore = defineStore('featureCase', {
|
const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
|
@ -14,6 +14,7 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
modulesCount: Record<string, any>; // 用例树模块数量
|
modulesCount: Record<string, any>; // 用例树模块数量
|
||||||
recycleModulesCount: Record<string, any>; // 回收站模块数量
|
recycleModulesCount: Record<string, any>; // 回收站模块数量
|
||||||
operatingState: boolean; // 操作状态
|
operatingState: boolean; // 操作状态
|
||||||
|
tabSettingList: TabItemType[]; // 详情tab
|
||||||
} => ({
|
} => ({
|
||||||
moduleId: [],
|
moduleId: [],
|
||||||
allModuleId: [],
|
allModuleId: [],
|
||||||
|
@ -21,6 +22,7 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
modulesCount: {},
|
modulesCount: {},
|
||||||
recycleModulesCount: {},
|
recycleModulesCount: {},
|
||||||
operatingState: false,
|
operatingState: false,
|
||||||
|
tabSettingList: [],
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
// 设置选择moduleId
|
// 设置选择moduleId
|
||||||
|
@ -37,6 +39,7 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
// 获取模块数量
|
// 获取模块数量
|
||||||
async getCaseModulesCountCount(params: CaseModuleQueryParams) {
|
async getCaseModulesCountCount(params: CaseModuleQueryParams) {
|
||||||
try {
|
try {
|
||||||
|
this.modulesCount = {};
|
||||||
this.modulesCount = await getCaseModulesCounts(params);
|
this.modulesCount = await getCaseModulesCounts(params);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -45,6 +48,7 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
// 获取模块数量
|
// 获取模块数量
|
||||||
async getRecycleMModulesCountCount(params: CaseModuleQueryParams) {
|
async getRecycleMModulesCountCount(params: CaseModuleQueryParams) {
|
||||||
try {
|
try {
|
||||||
|
this.recycleModulesCount = {};
|
||||||
this.recycleModulesCount = await getRecycleModulesCounts(params);
|
this.recycleModulesCount = await getRecycleModulesCounts(params);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -54,6 +58,14 @@ const useFeatureCaseStore = defineStore('featureCase', {
|
||||||
setIsAlreadySuccess(state: boolean) {
|
setIsAlreadySuccess(state: boolean) {
|
||||||
this.operatingState = state;
|
this.operatingState = state;
|
||||||
},
|
},
|
||||||
|
// 设置菜单
|
||||||
|
setTab(list: TabItemType[]) {
|
||||||
|
this.tabSettingList = list;
|
||||||
|
},
|
||||||
|
// 获取显示的tab
|
||||||
|
getTab() {
|
||||||
|
return this.tabSettingList.filter((item) => item.enable);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="showModal"
|
||||||
|
title-align="start"
|
||||||
|
class="ms-modal-form ms-modal-medium"
|
||||||
|
:cancel-text="t('common.cancel')"
|
||||||
|
unmount-on-close
|
||||||
|
@close="handleCancel"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
{{ title }}
|
||||||
|
</template>
|
||||||
|
<div class="form">
|
||||||
|
<a-form ref="demandFormRef" :model="modelForm" size="large" layout="vertical">
|
||||||
|
<a-form-item :label="t('caseManagement.featureCase.tableColumnID')" asterisk-position="end" field="demandId">
|
||||||
|
<a-input
|
||||||
|
v-model="modelForm.demandId"
|
||||||
|
:max-length="20"
|
||||||
|
:placeholder="t('caseManagement.featureCase.pleaseEnterID')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('caseManagement.featureCase.requirementTitle')"
|
||||||
|
asterisk-position="end"
|
||||||
|
field="demandName"
|
||||||
|
:rules="[{ required: true, message: t('caseManagement.featureCase.pleaseEnterTitle') }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model="modelForm.demandName"
|
||||||
|
:max-length="255"
|
||||||
|
show-word-limit
|
||||||
|
:placeholder="t('caseManagement.featureCase.pleaseEnterTitle')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('caseManagement.featureCase.requirementUrl')" asterisk-position="end" field="demandUrl">
|
||||||
|
<a-input
|
||||||
|
v-model="modelForm.demandUrl"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('caseManagement.featureCase.pleaseEnterRequirementUrl')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button>
|
||||||
|
<a-button type="secondary" @click="handleOK(true)">{{ t('ms.dialog.saveContinue') }}</a-button>
|
||||||
|
<a-button class="ml-[12px]" type="primary" :loading="confirmLoading" @click="handleOK(false)">
|
||||||
|
{{ updateName ? t('common.update') : t('common.create') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import { addDemandRequest, updateDemand } from '@/api/modules/case-management/featureCase';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { CreateOrUpdateDemand, DemandFormList, DemandItem } from '@/models/caseManagement/featureCase';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps<{
|
||||||
|
caseId: string;
|
||||||
|
visible: boolean;
|
||||||
|
form: DemandItem;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', v: boolean): void;
|
||||||
|
(e: 'success'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const form = ref<CreateOrUpdateDemand>({
|
||||||
|
id: '',
|
||||||
|
caseId: props.caseId,
|
||||||
|
demandPlatform: 'LOCAL',
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateName = ref<string>('');
|
||||||
|
|
||||||
|
const showModal = ref<boolean>(false);
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const initModelForm: DemandFormList = {
|
||||||
|
demandId: '',
|
||||||
|
demandName: '',
|
||||||
|
demandUrl: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const modelForm = ref<DemandFormList>({
|
||||||
|
...initModelForm,
|
||||||
|
});
|
||||||
|
|
||||||
|
const demandFormRef = ref<FormInstance | null>(null);
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
modelForm.value = { ...initModelForm };
|
||||||
|
form.value.id = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
demandFormRef.value?.resetFields();
|
||||||
|
showModal.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOK(isContinue: boolean) {
|
||||||
|
demandFormRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
const { demandId, demandName, demandUrl } = modelForm.value;
|
||||||
|
confirmLoading.value = true;
|
||||||
|
const params: CreateOrUpdateDemand = {
|
||||||
|
...form.value,
|
||||||
|
demandList: [{ demandId, demandName, demandUrl }],
|
||||||
|
};
|
||||||
|
if (form.value.id) {
|
||||||
|
await updateDemand(params);
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
} else {
|
||||||
|
await addDemandRequest(params);
|
||||||
|
Message.success(t('common.addSuccess'));
|
||||||
|
}
|
||||||
|
if (!isContinue) {
|
||||||
|
handleCancel();
|
||||||
|
}
|
||||||
|
resetForm();
|
||||||
|
emit('success');
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
confirmLoading.value = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(val) => {
|
||||||
|
showModal.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showModal.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:visible', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const title = ref<string>('');
|
||||||
|
watchEffect(() => {
|
||||||
|
title.value = form.value.id
|
||||||
|
? t('caseManagement.featureCase.updateDemand', { name: props.form.demandName })
|
||||||
|
: t('caseManagement.featureCase.addDemand');
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.form,
|
||||||
|
(val) => {
|
||||||
|
modelForm.value = { ...val };
|
||||||
|
form.value.id = val.id;
|
||||||
|
updateName.value = val.demandName;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,295 @@
|
||||||
|
<template>
|
||||||
|
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
||||||
|
<template #index="{ rowIndex }">
|
||||||
|
<div class="circle text-xs font-medium"> {{ rowIndex + 1 }}</div>
|
||||||
|
</template>
|
||||||
|
<template #caseStep="{ record }">
|
||||||
|
<a-textarea
|
||||||
|
v-if="record.showStep"
|
||||||
|
v-model="record.step"
|
||||||
|
size="mini"
|
||||||
|
:auto-size="true"
|
||||||
|
class="w-max-[267px]"
|
||||||
|
:placeholder="t('system.orgTemplate.stepTip')"
|
||||||
|
@blur="blurHandler(record, 'step')"
|
||||||
|
/>
|
||||||
|
<div v-else-if="record.step && !record.showStep" class="w-full cursor-pointer" @click="edit(record, 'step')">{{
|
||||||
|
record.step
|
||||||
|
}}</div>
|
||||||
|
<div
|
||||||
|
v-else-if="!record.caseStep && !record.showStep"
|
||||||
|
class="placeholder w-full cursor-pointer text-[var(--color-text-brand)]"
|
||||||
|
@click="edit(record, 'step')"
|
||||||
|
>{{ t('system.orgTemplate.stepTip') }}</div
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #expectedResult="{ record }">
|
||||||
|
<a-textarea
|
||||||
|
v-if="record.showExpected"
|
||||||
|
v-model="record.expected"
|
||||||
|
size="mini"
|
||||||
|
:auto-size="true"
|
||||||
|
class="w-max-[267px]"
|
||||||
|
:placeholder="t('system.orgTemplate.expectationTip')"
|
||||||
|
@blur="blurHandler(record, 'expected')"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="record.expected && !record.showExpected"
|
||||||
|
class="w-full cursor-pointer"
|
||||||
|
@click="edit(record, 'expected')"
|
||||||
|
>{{ record.expected }}</div
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-else-if="!record.expected && !record.showExpected"
|
||||||
|
class="placeholder w-full cursor-pointer text-[var(--color-text-brand)]"
|
||||||
|
@click="edit(record, 'expected')"
|
||||||
|
>{{ t('system.orgTemplate.expectationTip') }}</div
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #operation="{ record }">
|
||||||
|
<MsTableMoreAction
|
||||||
|
v-if="!record.internal"
|
||||||
|
:list="moreActions"
|
||||||
|
@select="(item:ActionsItem) => handleMoreActionSelect(item,record)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</MsBaseTable>
|
||||||
|
<a-button class="mt-2 px-0" type="text" :disabled="!props.isDisabled" @click="addStep">
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus class="text-[14px]" />
|
||||||
|
</template>
|
||||||
|
{{ t('system.orgTemplate.addStep') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from '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 MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { getGenerateId } from '@/utils';
|
||||||
|
|
||||||
|
import type { StepList } from '@/models/caseManagement/featureCase';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
stepList: any;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
isDisabled: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:stepList']);
|
||||||
|
|
||||||
|
const templateFieldColumns = ref<MsTableColumn>([
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.numberIndex',
|
||||||
|
dataIndex: 'index',
|
||||||
|
slotName: 'index',
|
||||||
|
width: 100,
|
||||||
|
showDrag: false,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.useCaseStep',
|
||||||
|
slotName: 'caseStep',
|
||||||
|
dataIndex: 'caseStep',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.expectedResult',
|
||||||
|
dataIndex: 'expectedResult',
|
||||||
|
slotName: 'expectedResult',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const moreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'caseManagement.featureCase.copyStep',
|
||||||
|
eventTag: 'copyStep',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'caseManagement.featureCase.InsertStepsBefore',
|
||||||
|
eventTag: 'InsertStepsBefore',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'caseManagement.featureCase.afterInsertingSteps',
|
||||||
|
eventTag: 'afterInsertingSteps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isDivider: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'common.delete',
|
||||||
|
danger: true,
|
||||||
|
eventTag: 'delete',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { propsRes, propsEvent, setProps } = useTable(undefined, {
|
||||||
|
tableKey: TableKeyEnum.CASE_MANAGEMENT_DETAIL_TABLE,
|
||||||
|
columns: templateFieldColumns.value,
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
selectable: false,
|
||||||
|
noDisable: true,
|
||||||
|
size: 'default',
|
||||||
|
showSetting: false,
|
||||||
|
showPagination: false,
|
||||||
|
enableDrag: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 步骤描述
|
||||||
|
const stepData = ref<StepList[]>([
|
||||||
|
{
|
||||||
|
id: getGenerateId(),
|
||||||
|
step: '',
|
||||||
|
expected: '',
|
||||||
|
showStep: false,
|
||||||
|
showExpected: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 复制步骤
|
||||||
|
function copyStep(record: StepList) {
|
||||||
|
stepData.value.push({
|
||||||
|
...record,
|
||||||
|
id: getGenerateId(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除步骤
|
||||||
|
function deleteStep(record: StepList) {
|
||||||
|
stepData.value = stepData.value.filter((item: any) => item.id !== record.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤之前插入步骤
|
||||||
|
function insertStepsBefore(record: StepList) {
|
||||||
|
const index = stepData.value.map((item: any) => item.id).indexOf(record.id);
|
||||||
|
const insertItem = {
|
||||||
|
id: getGenerateId(),
|
||||||
|
step: '',
|
||||||
|
expected: '',
|
||||||
|
showStep: false,
|
||||||
|
showExpected: false,
|
||||||
|
};
|
||||||
|
stepData.value.splice(index, 0, insertItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤之后插入步骤
|
||||||
|
function afterInsertingSteps(record: StepList) {
|
||||||
|
const index = stepData.value.map((item: any) => item.id).indexOf(record.id);
|
||||||
|
const insertItem = {
|
||||||
|
id: getGenerateId(),
|
||||||
|
step: '',
|
||||||
|
expected: '',
|
||||||
|
showStep: false,
|
||||||
|
showExpected: false,
|
||||||
|
};
|
||||||
|
stepData.value.splice(index + 1, 0, insertItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更多操作
|
||||||
|
const handleMoreActionSelect = (item: ActionsItem, record: StepList) => {
|
||||||
|
switch (item.eventTag) {
|
||||||
|
case 'copyStep':
|
||||||
|
copyStep(record);
|
||||||
|
break;
|
||||||
|
case 'InsertStepsBefore':
|
||||||
|
insertStepsBefore(record);
|
||||||
|
break;
|
||||||
|
case 'afterInsertingSteps':
|
||||||
|
afterInsertingSteps(record);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
deleteStep(record);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加步骤
|
||||||
|
const addStep = () => {
|
||||||
|
stepData.value.push({
|
||||||
|
id: getGenerateId(),
|
||||||
|
step: '',
|
||||||
|
expected: '',
|
||||||
|
showStep: false,
|
||||||
|
showExpected: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑步骤
|
||||||
|
function edit(record: StepList, type: string) {
|
||||||
|
if (!props.isDisabled) return;
|
||||||
|
if (type === 'step') {
|
||||||
|
record.showStep = true;
|
||||||
|
} else {
|
||||||
|
record.showExpected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 失去焦点回调
|
||||||
|
function blurHandler(record: StepList, type: string) {
|
||||||
|
if (!props.isDisabled) return;
|
||||||
|
if (type === 'step') {
|
||||||
|
record.showStep = false;
|
||||||
|
} else {
|
||||||
|
record.showExpected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tableRef = ref<InstanceType<typeof MsBaseTable> | null>(null);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
stepData.value = props.stepList;
|
||||||
|
setProps({ data: stepData.value });
|
||||||
|
if (!props.isDisabled) {
|
||||||
|
tableRef.value?.initColumn(templateFieldColumns.value.slice(0, templateFieldColumns.value.length - 1));
|
||||||
|
} else {
|
||||||
|
tableRef.value?.initColumn(templateFieldColumns.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => stepData.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:stepList', val);
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setProps({ data: stepData.value });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.circle {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-text-4);
|
||||||
|
background: var(--color-text-n8);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
<ms-base-table ref="tableRef" v-bind="propsRes" v-on="propsEvent">
|
||||||
|
<template #demandId="{ record }">
|
||||||
|
<span class="ml-2"> {{ record.demandId }}</span>
|
||||||
|
</template>
|
||||||
|
<template #demandName="{ record }">
|
||||||
|
<span class="ml-1" :class="[props.highlightName ? 'text-[rgb(var(--primary-5))]' : '']">
|
||||||
|
{{ record.demandName }}
|
||||||
|
<span v-if="record.children && (record.children || []).length"
|
||||||
|
>({{ (record.children || []).length }})</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #operation="{ record }">
|
||||||
|
<MsButton v-if="record.demandPlatform === 'LOCAL'" @click="emit('update', record)">{{
|
||||||
|
t('caseManagement.featureCase.cancelAssociation')
|
||||||
|
}}</MsButton>
|
||||||
|
<MsButton v-if="record.children && (record.children || []).length" @click="emit('update', record)">{{
|
||||||
|
t('common.edit')
|
||||||
|
}}</MsButton>
|
||||||
|
</template>
|
||||||
|
</ms-base-table>
|
||||||
|
</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 { getDemandList } from '@/api/modules/case-management/featureCase';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { DemandItem } from '@/models/caseManagement/featureCase';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
funParams: Record<string, any>; // 列表查询参数
|
||||||
|
isShowOperation?: boolean; // 是否显示操作列
|
||||||
|
highlightName?: boolean; // 是否高亮名称
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
isShowOperation: true,
|
||||||
|
highlightName: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update', record: DemandItem): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const expandedKeys = ref<string[]>([]);
|
||||||
|
const columns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.tableColumnID',
|
||||||
|
slotName: 'demandId',
|
||||||
|
showInTable: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.tableColumnName',
|
||||||
|
slotName: 'demandName',
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.demandPlatform',
|
||||||
|
width: 300,
|
||||||
|
dataIndex: 'demandPlatform',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.tableColumnActions',
|
||||||
|
slotName: 'operation',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 300,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getDemandList, {
|
||||||
|
tableKey: TableKeyEnum.CASE_MANAGEMENT_DEMAND,
|
||||||
|
columns,
|
||||||
|
rowKey: 'id',
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
selectable: false,
|
||||||
|
showSetting: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const initData = async () => {
|
||||||
|
const { keyword, caseId } = props.funParams;
|
||||||
|
setLoadListParams({ keyword, caseId });
|
||||||
|
await loadList();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initData();
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
initData,
|
||||||
|
});
|
||||||
|
const tableRef = ref<InstanceType<typeof MsBaseTable> | null>(null);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.isShowOperation,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
tableRef.value?.initColumn(columns.slice(0, columns.length - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
|
@ -0,0 +1,439 @@
|
||||||
|
<template>
|
||||||
|
<MsDetailDrawer
|
||||||
|
ref="detailDrawerRef"
|
||||||
|
v-model:visible="showDrawerVisible"
|
||||||
|
:width="1200"
|
||||||
|
:footer="false"
|
||||||
|
:title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.id, name: detailInfo?.name })"
|
||||||
|
:detail-id="props.detailId"
|
||||||
|
:detail-index="props.detailIndex"
|
||||||
|
:get-detail-func="getCaseDetail"
|
||||||
|
:pagination="props.pagination"
|
||||||
|
:table-data="props.tableData"
|
||||||
|
:page-change="props.pageChange"
|
||||||
|
@loaded="loadedCase"
|
||||||
|
>
|
||||||
|
<template #titleRight="{ loading }">
|
||||||
|
<div class="rightButtons flex items-center">
|
||||||
|
<MsButton
|
||||||
|
type="icon"
|
||||||
|
status="secondary"
|
||||||
|
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||||
|
:disabled="loading"
|
||||||
|
:loading="editLoading"
|
||||||
|
@click="updateHandler('edit')"
|
||||||
|
>
|
||||||
|
<MsIcon type="icon-icon_edit_outlined" class="mr-1 font-[16px]" />
|
||||||
|
{{ t('common.edit') }}
|
||||||
|
</MsButton>
|
||||||
|
<MsButton
|
||||||
|
type="icon"
|
||||||
|
status="secondary"
|
||||||
|
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||||
|
:disabled="loading"
|
||||||
|
:loading="shareLoading"
|
||||||
|
@click="shareHandler"
|
||||||
|
>
|
||||||
|
<MsIcon type="icon-icon_share1" class="mr-1 font-[16px]" />
|
||||||
|
{{ t('caseManagement.featureCase.share') }}
|
||||||
|
</MsButton>
|
||||||
|
<MsButton
|
||||||
|
type="icon"
|
||||||
|
status="secondary"
|
||||||
|
class="mr-4 !rounded-[var(--border-radius-small)]"
|
||||||
|
:disabled="loading"
|
||||||
|
:loading="followLoading"
|
||||||
|
@click="followHandler"
|
||||||
|
>
|
||||||
|
<MsIcon
|
||||||
|
:type="detailInfo.followFlag ? 'icon-icon_collect_filled' : 'icon-icon_collection_outlined'"
|
||||||
|
class="mr-1 font-[16px]"
|
||||||
|
:class="[detailInfo.followFlag ? 'text-[rgb(var(--warning-6))]' : '']"
|
||||||
|
/>
|
||||||
|
{{ t('caseManagement.featureCase.follow') }}
|
||||||
|
</MsButton>
|
||||||
|
<MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]">
|
||||||
|
<a-dropdown position="br" :hide-on-select="false">
|
||||||
|
<div>
|
||||||
|
<icon-more class="mr-1" />
|
||||||
|
<span> {{ t('caseManagement.featureCase.more') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<a-doption>
|
||||||
|
<a-switch class="mr-1" size="small" />{{ t('caseManagement.featureCase.addToPublic') }}
|
||||||
|
</a-doption>
|
||||||
|
<a-doption @click="updateHandler('copy')">
|
||||||
|
<MsIcon type="icon-icon_copy_filled" class="font-[16px]" />{{ t('common.copy') }}</a-doption
|
||||||
|
>
|
||||||
|
<a-doption class="error-6 text-[rgb(var(--danger-6))]" @click="deleteHandler()">
|
||||||
|
<MsIcon type="icon-icon_delete-trash_outlined" class="font-[16px] text-[rgb(var(--danger-6))]" />
|
||||||
|
{{ t('common.delete') }}
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</MsButton>
|
||||||
|
<MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]" @click="toggle">
|
||||||
|
<MsIcon :type="isFullscreen ? 'icon-icon_off_screen' : 'icon-icon_full_screen_one'" class="mr-1" size="16" />
|
||||||
|
{{ t('caseManagement.featureCase.fullScreen') }}
|
||||||
|
</MsButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<div ref="wrapperRef" class="h-full bg-white">
|
||||||
|
<MsSplitBox ref="wrapperRef" expand-direction="right" :max="0.7" :min="0.7" :size="900">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #right>
|
||||||
|
<div class="rightWrapper p-[24px]">
|
||||||
|
<div class="mb-4 font-medium">{{ t('caseManagement.featureCase.basicInfo') }}</div>
|
||||||
|
<div class="baseItem">
|
||||||
|
<span class="label"> {{ t('caseManagement.featureCase.tableColumnModule') }}</span>
|
||||||
|
<span>{{ moduleName }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 自定义字段开始 -->
|
||||||
|
<MsFormCreate
|
||||||
|
v-if="formRules.length"
|
||||||
|
ref="formCreateRef"
|
||||||
|
class="w-full"
|
||||||
|
:option="options"
|
||||||
|
:form-rule="formRules"
|
||||||
|
:form-create-key="FormCreateKeyEnum.CASE_CUSTOM_ATTRS_DETAIL"
|
||||||
|
/>
|
||||||
|
<!-- 自定义字段结束 -->
|
||||||
|
<div class="baseItem">
|
||||||
|
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateUser') }}</span>
|
||||||
|
<span>{{ detailInfo?.createUser }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="baseItem">
|
||||||
|
<span class="label"> {{ t('caseManagement.featureCase.tableColumnCreateTime') }}</span>
|
||||||
|
<span>{{ dayjs(detailInfo?.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsSplitBox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsDetailDrawer>
|
||||||
|
<SettingDrawer v-model:visible="showSettingDrawer" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useFullscreen } from '@vueuse/core';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
||||||
|
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 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 { deleteCaseRequest, followerCaseRequest, getCaseDetail } from '@/api/modules/case-management/featureCase';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useModal from '@/hooks/useModal';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
|
import { characterLimit, findNodeByKey } from '@/utils';
|
||||||
|
|
||||||
|
import type { CaseManagementTable, CustomAttributes, TabItemType } from '@/models/caseManagement/featureCase';
|
||||||
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
|
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const detailDrawerRef = ref<InstanceType<typeof MsDetailDrawer>>();
|
||||||
|
const wrapperRef = ref();
|
||||||
|
const { isFullscreen, toggle } = useFullscreen(wrapperRef);
|
||||||
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openModal } = useModal();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
detailId: string; // 详情 id
|
||||||
|
detailIndex: number; // 详情 下标
|
||||||
|
tableData: any[]; // 表格数据
|
||||||
|
pagination: MsPaginationI; // 分页器对象
|
||||||
|
pageChange: (page: number) => Promise<void>; // 分页变更函数
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible']);
|
||||||
|
|
||||||
|
const userId = computed(() => userStore.userInfo.id);
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
|
|
||||||
|
const showDrawerVisible = ref<boolean>(false);
|
||||||
|
|
||||||
|
const showSettingDrawer = ref<boolean>(false);
|
||||||
|
function showMenuSetting() {
|
||||||
|
showSettingDrawer.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabSettingList = computed(() => {
|
||||||
|
return featureCaseStore.tabSettingList;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabSetting = ref<TabItemType[]>([...tabSettingList.value]);
|
||||||
|
const activeTab = ref<string | number>('detail');
|
||||||
|
function changeTabs(key: string | number) {
|
||||||
|
activeTab.value = key;
|
||||||
|
switch (activeTab.value) {
|
||||||
|
case 'setting':
|
||||||
|
showMenuSetting();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailInfo = ref<Record<string, any>>({});
|
||||||
|
const customFields = ref<CustomAttributes[]>([]);
|
||||||
|
|
||||||
|
function loadedCase(detail: CaseManagementTable) {
|
||||||
|
detailInfo.value = { ...detail };
|
||||||
|
customFields.value = detailInfo.value.customFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleName = computed(() => {
|
||||||
|
return findNodeByKey<Record<string, any>>(featureCaseStore.caseTree, detailInfo.value?.moduleId as string, 'id')
|
||||||
|
?.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
const editLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
function updateSuccess() {
|
||||||
|
detailDrawerRef.value?.initDetail();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHandler(type: string) {
|
||||||
|
router.push({
|
||||||
|
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
|
||||||
|
query: {
|
||||||
|
id: detailInfo.value.id,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
mode: type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
function shareHandler() {}
|
||||||
|
|
||||||
|
const followLoading = ref<boolean>(false);
|
||||||
|
// 关注
|
||||||
|
async function followHandler() {
|
||||||
|
followLoading.value = true;
|
||||||
|
try {
|
||||||
|
await followerCaseRequest({ userId: userId.value as string, functionalCaseId: detailInfo.value.id });
|
||||||
|
updateSuccess();
|
||||||
|
Message.success(
|
||||||
|
detailInfo.value.followFlag
|
||||||
|
? t('caseManagement.featureCase.cancelFollowSuccess')
|
||||||
|
: t('caseManagement.featureCase.followSuccess')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
followLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用例
|
||||||
|
function deleteHandler() {
|
||||||
|
const { id, name } = detailInfo.value;
|
||||||
|
openModal({
|
||||||
|
type: 'error',
|
||||||
|
title: t('caseManagement.featureCase.deleteCaseTitle', { name: characterLimit(name) }),
|
||||||
|
content: t('caseManagement.featureCase.beforeDeleteCase'),
|
||||||
|
okText: t('common.confirmDelete'),
|
||||||
|
cancelText: t('common.cancel'),
|
||||||
|
okButtonProps: {
|
||||||
|
status: 'danger',
|
||||||
|
},
|
||||||
|
onBeforeOk: async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
id,
|
||||||
|
deleteAll: false,
|
||||||
|
projectId: currentProjectId.value,
|
||||||
|
};
|
||||||
|
await deleteCaseRequest(params);
|
||||||
|
Message.success(t('common.deleteSuccess'));
|
||||||
|
updateSuccess();
|
||||||
|
detailDrawerRef.value?.openPrevDetail();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideCancel: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRules = ref<FormItem[]>([]);
|
||||||
|
|
||||||
|
const isDisabled = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 表单配置项
|
||||||
|
const options = {
|
||||||
|
resetBtn: false, // 不展示默认配置的重置和提交
|
||||||
|
submitBtn: false,
|
||||||
|
on: false, // 取消绑定on事件
|
||||||
|
form: {
|
||||||
|
layout: 'horizontal',
|
||||||
|
labelAlign: 'left',
|
||||||
|
labelColProps: {
|
||||||
|
span: 9,
|
||||||
|
},
|
||||||
|
wrapperColProps: {
|
||||||
|
span: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 暂时默认
|
||||||
|
row: {
|
||||||
|
gutter: 0,
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
'asterisk-position': 'end',
|
||||||
|
'validate-trigger': ['change'],
|
||||||
|
'hide-asterisk': true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化表单
|
||||||
|
function initForm() {
|
||||||
|
formRules.value = customFields.value.map((item: any) => {
|
||||||
|
return {
|
||||||
|
type: item.type,
|
||||||
|
name: item.fieldId,
|
||||||
|
label: item.fieldName,
|
||||||
|
value: JSON.parse(item.defaultValue),
|
||||||
|
required: item.required,
|
||||||
|
options: item.options || [],
|
||||||
|
props: {
|
||||||
|
modelValue: JSON.parse(item.defaultValue),
|
||||||
|
disabled: isDisabled.value,
|
||||||
|
options: item.options || [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}) as FormItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => customFields.value,
|
||||||
|
() => {
|
||||||
|
initForm();
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(val) => {
|
||||||
|
showDrawerVisible.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showDrawerVisible.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:visible', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => tabSettingList.value,
|
||||||
|
() => {
|
||||||
|
tabSetting.value = featureCaseStore.getTab();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.leftWrapper {
|
||||||
|
.header {
|
||||||
|
padding: 0 16px;
|
||||||
|
border-bottom: 1px solid var(--color-text-n8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.rightWrapper {
|
||||||
|
.baseItem {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
@apply flex;
|
||||||
|
.label {
|
||||||
|
width: 38%;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.arco-form-item-layout-horizontal) {
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
}
|
||||||
|
:deep(.arco-form-item-label-col > .arco-form-item-label) {
|
||||||
|
color: var(--color-text-3) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.rightButtons {
|
||||||
|
:deep(.ms-button--secondary):hover,
|
||||||
|
:hover > .arco-icon {
|
||||||
|
color: rgb(var(--primary-5)) !important;
|
||||||
|
background: var(--color-bg-3);
|
||||||
|
.arco-icon:hover {
|
||||||
|
color: rgb(var(--primary-5)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.error-6 {
|
||||||
|
color: rgb(var(--danger-6));
|
||||||
|
&:hover {
|
||||||
|
color: rgb(var(--danger-6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.active .arco-badge-number) {
|
||||||
|
background: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="page-header mb-4 h-[34px]">
|
<div class="page-header mb-4 h-[34px]">
|
||||||
<div class="text-[var(--color-text-1)]"
|
<div class="text-[var(--color-text-1)]"
|
||||||
>{{ moduleNamePath }}
|
>{{ moduleNamePath }}
|
||||||
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] }})</span></div
|
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span></div
|
||||||
>
|
>
|
||||||
<div class="flex w-[80%] items-center justify-end">
|
<div class="flex w-[80%] items-center justify-end">
|
||||||
<a-select class="w-[240px]" :placeholder="t('caseManagement.featureCase.versionPlaceholder')">
|
<a-select class="w-[240px]" :placeholder="t('caseManagement.featureCase.versionPlaceholder')">
|
||||||
|
@ -57,8 +57,8 @@
|
||||||
v-on="propsEvent"
|
v-on="propsEvent"
|
||||||
@batch-action="handleTableBatch"
|
@batch-action="handleTableBatch"
|
||||||
>
|
>
|
||||||
<template #name="{ record }">
|
<template #name="{ record, rowIndex }">
|
||||||
<a-button type="text" class="px-0" @click="showCaseDetail(record.id)">{{ record.name }}</a-button>
|
<a-button type="text" class="px-0" @click="showCaseDetail(record.id, rowIndex)">{{ record.name }}</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #reviewStatus="{ record }">
|
<template #reviewStatus="{ record }">
|
||||||
<MsIcon
|
<MsIcon
|
||||||
|
@ -83,8 +83,10 @@
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsButton @click="operateCase(record, 'edit')">{{ t('common.edit') }}</MsButton>
|
<MsButton @click="operateCase(record, 'edit')">{{ t('common.edit') }}</MsButton>
|
||||||
|
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||||
<MsButton @click="operateCase(record, 'copy')">{{ t('caseManagement.featureCase.copy') }}</MsButton>
|
<MsButton @click="operateCase(record, 'copy')">{{ t('caseManagement.featureCase.copy') }}</MsButton>
|
||||||
<MsButton class="!mr-0" @click="deleteCase(record)">{{ t('common.delete') }}</MsButton>
|
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||||
|
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
|
||||||
</template>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
<!-- 用例表结束 -->
|
<!-- 用例表结束 -->
|
||||||
|
@ -135,6 +137,14 @@
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<ExportExcelDrawer v-model:visible="showExportExcelVisible" />
|
<ExportExcelDrawer v-model:visible="showExportExcelVisible" />
|
||||||
<BatchEditModal v-model:visible="showEditModel" :batch-params="batchParams" @success="successHandler" />
|
<BatchEditModal v-model:visible="showEditModel" :batch-params="batchParams" @success="successHandler" />
|
||||||
|
<CaseDetailDrawer
|
||||||
|
v-model:visible="showDetailDrawer"
|
||||||
|
:detail-id="activeDetailId"
|
||||||
|
:detail-index="activeCaseIndex"
|
||||||
|
:table-data="propsRes.data"
|
||||||
|
:page-change="propsEvent.pageChange"
|
||||||
|
:pagination="propsRes.msPagination!"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -149,9 +159,12 @@
|
||||||
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 { 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 FilterPanel from '@/components/business/ms-filter-panel/searchForm.vue';
|
import FilterPanel from '@/components/business/ms-filter-panel/searchForm.vue';
|
||||||
import BatchEditModal from './batchEditModal.vue';
|
import BatchEditModal from './batchEditModal.vue';
|
||||||
|
import CaseDetailDrawer from './caseDetailDrawer.vue';
|
||||||
import FeatureCaseTree from './caseTree.vue';
|
import FeatureCaseTree from './caseTree.vue';
|
||||||
import ExportExcelDrawer from './exportExcelDrawer.vue';
|
import ExportExcelDrawer from './exportExcelDrawer.vue';
|
||||||
|
|
||||||
|
@ -428,6 +441,10 @@
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
moreAction: [
|
moreAction: [
|
||||||
|
{
|
||||||
|
label: 'featureTest.featureCase.addDemand',
|
||||||
|
eventTag: 'addDemand',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'caseManagement.featureCase.associatedDemand',
|
label: 'caseManagement.featureCase.associatedDemand',
|
||||||
eventTag: 'associatedDemand',
|
eventTag: 'associatedDemand',
|
||||||
|
@ -495,7 +512,7 @@
|
||||||
scroll: { x: 3200 },
|
scroll: { x: 3200 },
|
||||||
selectable: true,
|
selectable: true,
|
||||||
showSetting: true,
|
showSetting: true,
|
||||||
heightUsed: 340,
|
heightUsed: 374,
|
||||||
enableDrag: true,
|
enableDrag: true,
|
||||||
},
|
},
|
||||||
(record) => ({
|
(record) => ({
|
||||||
|
@ -594,6 +611,20 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const moreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'common.delete',
|
||||||
|
danger: true,
|
||||||
|
eventTag: 'delete',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function handleMoreActionSelect(item: ActionsItem, record: CaseManagementTable) {
|
||||||
|
if (item.eventTag === 'delete') {
|
||||||
|
deleteCase(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const showExportExcelVisible = ref<boolean>(false);
|
const showExportExcelVisible = ref<boolean>(false);
|
||||||
|
|
||||||
// 导出Excel
|
// 导出Excel
|
||||||
|
@ -714,20 +745,39 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加需求
|
||||||
|
function addDemand() {}
|
||||||
|
// 关联需求
|
||||||
|
function handleAssociatedDemand() {}
|
||||||
|
|
||||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||||
batchParams.value = params;
|
batchParams.value = params;
|
||||||
if (event.eventTag === 'exportExcel') {
|
switch (event.eventTag) {
|
||||||
handleShowExportExcel();
|
case 'exportExcel':
|
||||||
} else if (event.eventTag === 'batchEdit') {
|
handleShowExportExcel();
|
||||||
batchEdit();
|
break;
|
||||||
} else if (event.eventTag === 'delete') {
|
case 'batchEdit':
|
||||||
batchDelete();
|
batchEdit();
|
||||||
} else if (event.eventTag === 'batchMoveTo') {
|
break;
|
||||||
batchMoveOrCopy();
|
case 'delete':
|
||||||
isMove.value = true;
|
batchDelete();
|
||||||
} else if (event.eventTag === 'batchCopyTo') {
|
break;
|
||||||
batchMoveOrCopy();
|
case 'batchMoveTo':
|
||||||
isMove.value = false;
|
batchMoveOrCopy();
|
||||||
|
isMove.value = true;
|
||||||
|
break;
|
||||||
|
case 'batchCopyTo':
|
||||||
|
batchMoveOrCopy();
|
||||||
|
isMove.value = false;
|
||||||
|
break;
|
||||||
|
case 'addDemand':
|
||||||
|
addDemand();
|
||||||
|
break;
|
||||||
|
case 'associatedDemand':
|
||||||
|
handleAssociatedDemand();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,9 +791,15 @@
|
||||||
emitTableParams();
|
emitTableParams();
|
||||||
resetSelector();
|
resetSelector();
|
||||||
}
|
}
|
||||||
|
const showDetailDrawer = ref(false);
|
||||||
|
const activeDetailId = ref<string>('');
|
||||||
|
const activeCaseIndex = ref<number>(0);
|
||||||
// 详情
|
// 详情
|
||||||
function showCaseDetail(id: string) {}
|
function showCaseDetail(id: string, index: number) {
|
||||||
|
showDetailDrawer.value = true;
|
||||||
|
activeDetailId.value = id;
|
||||||
|
activeCaseIndex.value = index;
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => showType.value,
|
() => showType.value,
|
||||||
|
|
|
@ -45,70 +45,7 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- 步骤描述 -->
|
<!-- 步骤描述 -->
|
||||||
<div v-if="form.caseEditType === 'STEP'" class="w-full">
|
<div v-if="form.caseEditType === 'STEP'" class="w-full">
|
||||||
<MsBaseTable v-bind="propsRes" ref="stepTableRef" v-on="propsEvent">
|
<AddStep v-model:step-list="stepData" />
|
||||||
<template #index="{ rowIndex }">
|
|
||||||
<div class="circle text-xs font-medium"> {{ rowIndex + 1 }}</div>
|
|
||||||
</template>
|
|
||||||
<template #caseStep="{ record }">
|
|
||||||
<a-textarea
|
|
||||||
v-if="record.showStep"
|
|
||||||
v-model="record.step"
|
|
||||||
size="mini"
|
|
||||||
:auto-size="true"
|
|
||||||
class="w-max-[267px]"
|
|
||||||
:placeholder="t('system.orgTemplate.stepTip')"
|
|
||||||
@blur="blurHandler(record, 'step')"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-else-if="record.step && !record.showStep"
|
|
||||||
class="w-full cursor-pointer"
|
|
||||||
@click="edit(record, 'step')"
|
|
||||||
>{{ record.step }}</div
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-else-if="!record.caseStep && !record.showStep"
|
|
||||||
class="placeholder w-full cursor-pointer text-[var(--color-text-brand)]"
|
|
||||||
@click="edit(record, 'step')"
|
|
||||||
>{{ t('system.orgTemplate.stepTip') }}</div
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #expectedResult="{ record }">
|
|
||||||
<a-textarea
|
|
||||||
v-if="record.showExpected"
|
|
||||||
v-model="record.expected"
|
|
||||||
size="mini"
|
|
||||||
:auto-size="true"
|
|
||||||
class="w-max-[267px]"
|
|
||||||
:placeholder="t('system.orgTemplate.expectationTip')"
|
|
||||||
@blur="blurHandler(record, 'expected')"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-else-if="record.expected && !record.showExpected"
|
|
||||||
class="w-full cursor-pointer"
|
|
||||||
@click="edit(record, 'expected')"
|
|
||||||
>{{ record.expected }}</div
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-else-if="!record.expected && !record.showExpected"
|
|
||||||
class="placeholder w-full cursor-pointer text-[var(--color-text-brand)]"
|
|
||||||
@click="edit(record, 'expected')"
|
|
||||||
>{{ t('system.orgTemplate.expectationTip') }}</div
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<template #operation="{ record }">
|
|
||||||
<MsTableMoreAction
|
|
||||||
v-if="!record.internal"
|
|
||||||
:list="moreActions"
|
|
||||||
@select="(item:ActionsItem) => handleMoreActionSelect(item,record)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</MsBaseTable>
|
|
||||||
<a-button class="mt-2 px-0" type="text" @click="addStep">
|
|
||||||
<template #icon>
|
|
||||||
<icon-plus class="text-[14px]" />
|
|
||||||
</template>
|
|
||||||
{{ t('system.orgTemplate.addStep') }}
|
|
||||||
</a-button>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 文本描述 -->
|
<!-- 文本描述 -->
|
||||||
<MsRichText v-else v-model:modelValue="form.textDescription" />
|
<MsRichText v-else v-model:modelValue="form.textDescription" />
|
||||||
|
@ -233,7 +170,7 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- 自定义字段结束 -->
|
<!-- 自定义字段结束 -->
|
||||||
</div>
|
</div>
|
||||||
<div class=" ">
|
<div>
|
||||||
<MsUpload
|
<MsUpload
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
accept="none"
|
accept="none"
|
||||||
|
@ -260,14 +197,10 @@
|
||||||
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
||||||
import type { FormItem } from '@/components/pure/ms-form-create/types';
|
import type { FormItem } from '@/components/pure/ms-form-create/types';
|
||||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.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 MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
|
||||||
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
||||||
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
import MsUpload from '@/components/pure/ms-upload/index.vue';
|
||||||
import type { MsFileItem } from '@/components/pure/ms-upload/types';
|
import type { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
|
import AddStep from './addStep.vue';
|
||||||
import AssociatedFileDrawer from './associatedFileDrawer.vue';
|
import AssociatedFileDrawer from './associatedFileDrawer.vue';
|
||||||
|
|
||||||
import { getCaseDefaultFields, getCaseDetail } from '@/api/modules/case-management/featureCase';
|
import { getCaseDefaultFields, getCaseDetail } from '@/api/modules/case-management/featureCase';
|
||||||
|
@ -281,8 +214,8 @@
|
||||||
import type { AssociatedList, CreateCase, StepList } from '@/models/caseManagement/featureCase';
|
import type { AssociatedList, CreateCase, StepList } from '@/models/caseManagement/featureCase';
|
||||||
import type { CustomField, DefinedFieldItem } from '@/models/setting/template';
|
import type { CustomField, DefinedFieldItem } from '@/models/setting/template';
|
||||||
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
|
||||||
|
|
||||||
|
import { convertToFile } from './utils';
|
||||||
import {
|
import {
|
||||||
getCustomDetailFields,
|
getCustomDetailFields,
|
||||||
getTotalFieldOptionList,
|
getTotalFieldOptionList,
|
||||||
|
@ -300,75 +233,6 @@
|
||||||
|
|
||||||
const emit = defineEmits(['update:formModeValue', 'changeFile']);
|
const emit = defineEmits(['update:formModeValue', 'changeFile']);
|
||||||
const acceptType = ref('none'); // 模块-上传文件类型
|
const acceptType = ref('none'); // 模块-上传文件类型
|
||||||
|
|
||||||
const templateFieldColumns: MsTableColumn = [
|
|
||||||
{
|
|
||||||
title: 'system.orgTemplate.numberIndex',
|
|
||||||
dataIndex: 'index',
|
|
||||||
slotName: 'index',
|
|
||||||
width: 100,
|
|
||||||
showDrag: false,
|
|
||||||
showInTable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'system.orgTemplate.useCaseStep',
|
|
||||||
slotName: 'caseStep',
|
|
||||||
dataIndex: 'caseStep',
|
|
||||||
showDrag: true,
|
|
||||||
showInTable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'system.orgTemplate.expectedResult',
|
|
||||||
dataIndex: 'expectedResult',
|
|
||||||
slotName: 'expectedResult',
|
|
||||||
showDrag: true,
|
|
||||||
showInTable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'system.orgTemplate.operation',
|
|
||||||
slotName: 'operation',
|
|
||||||
fixed: 'right',
|
|
||||||
width: 200,
|
|
||||||
showInTable: true,
|
|
||||||
showDrag: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const { propsRes, propsEvent, setProps } = useTable(undefined, {
|
|
||||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_DETAIL_TABLE,
|
|
||||||
columns: templateFieldColumns,
|
|
||||||
scroll: { x: '800px' },
|
|
||||||
selectable: false,
|
|
||||||
noDisable: true,
|
|
||||||
size: 'default',
|
|
||||||
showSetting: false,
|
|
||||||
showPagination: false,
|
|
||||||
enableDrag: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const moreActions: ActionsItem[] = [
|
|
||||||
{
|
|
||||||
label: 'caseManagement.featureCase.copyStep',
|
|
||||||
eventTag: 'copyStep',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'caseManagement.featureCase.InsertStepsBefore',
|
|
||||||
eventTag: 'InsertStepsBefore',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'caseManagement.featureCase.afterInsertingSteps',
|
|
||||||
eventTag: 'afterInsertingSteps',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isDivider: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'common.delete',
|
|
||||||
danger: true,
|
|
||||||
eventTag: 'delete',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const caseFormRef = ref<FormInstance>();
|
const caseFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
@ -440,91 +304,6 @@
|
||||||
form.value.caseEditType = value as string;
|
form.value.caseEditType = value as string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加步骤
|
|
||||||
const addStep = () => {
|
|
||||||
stepData.value.push({
|
|
||||||
id: getGenerateId(),
|
|
||||||
step: '',
|
|
||||||
expected: '',
|
|
||||||
showStep: false,
|
|
||||||
showExpected: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 复制步骤
|
|
||||||
function copyStep(record: StepList) {
|
|
||||||
stepData.value.push({
|
|
||||||
...record,
|
|
||||||
id: getGenerateId(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除步骤
|
|
||||||
function deleteStep(record: StepList) {
|
|
||||||
stepData.value = stepData.value.filter((item: any) => item.id !== record.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 步骤之前插入步骤
|
|
||||||
function insertStepsBefore(record: StepList) {
|
|
||||||
const index = stepData.value.map((item: any) => item.id).indexOf(record.id);
|
|
||||||
const insertItem = {
|
|
||||||
id: getGenerateId(),
|
|
||||||
step: '',
|
|
||||||
expected: '',
|
|
||||||
showStep: false,
|
|
||||||
showExpected: false,
|
|
||||||
};
|
|
||||||
stepData.value.splice(index, 0, insertItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 步骤之后插入步骤
|
|
||||||
function afterInsertingSteps(record: StepList) {
|
|
||||||
const index = stepData.value.map((item: any) => item.id).indexOf(record.id);
|
|
||||||
const insertItem = {
|
|
||||||
id: getGenerateId(),
|
|
||||||
step: '',
|
|
||||||
expected: '',
|
|
||||||
showStep: false,
|
|
||||||
showExpected: false,
|
|
||||||
};
|
|
||||||
stepData.value.splice(index + 1, 0, insertItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑步骤
|
|
||||||
function edit(record: StepList, type: string) {
|
|
||||||
if (type === 'step') {
|
|
||||||
record.showStep = true;
|
|
||||||
} else {
|
|
||||||
record.showExpected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 失去焦点回调
|
|
||||||
function blurHandler(record: StepList, type: string) {
|
|
||||||
if (type === 'step') {
|
|
||||||
record.showStep = false;
|
|
||||||
} else {
|
|
||||||
record.showExpected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更多操作
|
|
||||||
const handleMoreActionSelect = (item: ActionsItem, record: StepList) => {
|
|
||||||
switch (item.eventTag) {
|
|
||||||
case 'copyStep':
|
|
||||||
copyStep(record);
|
|
||||||
break;
|
|
||||||
case 'InsertStepsBefore':
|
|
||||||
insertStepsBefore(record);
|
|
||||||
break;
|
|
||||||
case 'afterInsertingSteps':
|
|
||||||
afterInsertingSteps(record);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
deleteStep(record);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 总自定义字段
|
// 总自定义字段
|
||||||
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
||||||
|
|
||||||
|
@ -564,32 +343,12 @@
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将文件信息转换为文件格式
|
|
||||||
function convertToFile(fileInfo: AssociatedList): MsFileItem {
|
|
||||||
const fileName = fileInfo.fileType ? `${fileInfo.name}.${fileInfo.fileType || ''}` : `${fileInfo.name}`;
|
|
||||||
const type = fileName.split('.')[1];
|
|
||||||
const file = new File([new Blob()], `${fileName}`, {
|
|
||||||
type: `application/${type}`,
|
|
||||||
});
|
|
||||||
Object.defineProperty(file, 'size', { value: fileInfo.size });
|
|
||||||
return {
|
|
||||||
enable: fileInfo.enable || false,
|
|
||||||
file,
|
|
||||||
name: fileName,
|
|
||||||
percent: 0,
|
|
||||||
status: 'done',
|
|
||||||
uid: fileInfo.id,
|
|
||||||
url: `http://172.16.200.18:8081/${fileInfo.filePath || ''}`,
|
|
||||||
local: fileInfo.local,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 处理关联文件
|
// 处理关联文件
|
||||||
function saveSelectAssociatedFile(fileData: AssociatedList[]) {
|
function saveSelectAssociatedFile(fileData: AssociatedList[]) {
|
||||||
const fileResultList = fileData.map((fileInfo) => convertToFile(fileInfo));
|
const fileResultList = fileData.map((fileInfo) => convertToFile(fileInfo));
|
||||||
fileList.value.push(...fileResultList);
|
fileList.value.push(...fileResultList);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = ref('');
|
|
||||||
const isEditOrCopy = computed(() => !!route.query.id);
|
const isEditOrCopy = computed(() => !!route.query.id);
|
||||||
const attachmentsList = ref([]);
|
const attachmentsList = ref([]);
|
||||||
|
|
||||||
|
@ -669,8 +428,6 @@
|
||||||
.map((fileInfo: any) => {
|
.map((fileInfo: any) => {
|
||||||
return convertToFile(fileInfo);
|
return convertToFile(fileInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理删除本地文件id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理详情
|
// 处理详情
|
||||||
|
@ -750,14 +507,6 @@
|
||||||
showDrawer.value = true;
|
showDrawer.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => stepData.value,
|
|
||||||
(val) => {
|
|
||||||
setProps({ data: val });
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
||||||
fileList.value = _fileList.map((e) => {
|
fileList.value = _fileList.map((e) => {
|
||||||
return {
|
return {
|
||||||
|
@ -832,10 +581,6 @@
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setProps({ data: stepData.value });
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
formRules.value = [];
|
formRules.value = [];
|
||||||
formRuleField.value = [];
|
formRuleField.value = [];
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
allow-clear
|
allow-clear
|
||||||
class="mb-[16px]"
|
class="mb-[16px]"
|
||||||
></a-input-search>
|
></a-input-search>
|
||||||
<a-spin class="w-full" :style="{ height: `calc(100vh - 316px)` }" :loading="loading">
|
<a-spin class="w-full" :style="{ height: `calc(100vh - 346px)` }" :loading="loading">
|
||||||
<MsTree
|
<MsTree
|
||||||
v-model:focus-node-key="focusNodeKey"
|
v-model:focus-node-key="focusNodeKey"
|
||||||
:selected-keys="props.selectedKeys"
|
:selected-keys="props.selectedKeys"
|
||||||
|
@ -328,7 +328,7 @@
|
||||||
|
|
||||||
const virtualListProps = computed(() => {
|
const virtualListProps = computed(() => {
|
||||||
return {
|
return {
|
||||||
height: 'calc(100vh - 360px)',
|
height: 'calc(100vh - 366px)',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="mb-4 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<a-button type="primary" @click="associatedDemand">
|
||||||
|
{{ t('caseManagement.featureCase.associatedDemand') }}</a-button
|
||||||
|
>
|
||||||
|
<a-button class="mx-3" type="outline" @click="addDemand">
|
||||||
|
{{ t('caseManagement.featureCase.addDemand') }}</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('caseManagement.featureCase.searchByNameAndId')"
|
||||||
|
allow-clear
|
||||||
|
class="mx-[8px] w-[240px]"
|
||||||
|
@search="searchList"
|
||||||
|
@press-enter="searchList"
|
||||||
|
></a-input-search>
|
||||||
|
</div>
|
||||||
|
<AssociatedDemandTable
|
||||||
|
ref="demandRef"
|
||||||
|
:fun-params="{ caseId: props.caseId, keyword }"
|
||||||
|
@update="updateDemand"
|
||||||
|
></AssociatedDemandTable>
|
||||||
|
<AddDemandModal v-model:visible="showAddModel" :case-id="props.caseId" :form="modelForm" @success="searchList()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
|
import AddDemandModal from './addDemandModal.vue';
|
||||||
|
import AssociatedDemandTable from './associatedDemandTable.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { DemandItem } from '@/models/caseManagement/featureCase';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
caseId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const keyword = ref<string>('');
|
||||||
|
const demandRef = ref();
|
||||||
|
|
||||||
|
const searchList = debounce(() => {
|
||||||
|
demandRef.value.initData();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
const showAddModel = ref<boolean>(false);
|
||||||
|
|
||||||
|
function addDemand() {
|
||||||
|
showAddModel.value = true;
|
||||||
|
}
|
||||||
|
const modelForm = ref<DemandItem>({
|
||||||
|
id: '',
|
||||||
|
caseId: '', // 功能用例ID
|
||||||
|
demandId: '', // 需求ID
|
||||||
|
demandName: '', // 需求标题
|
||||||
|
demandUrl: '', // 需求地址
|
||||||
|
demandPlatform: '', // 需求所属平台
|
||||||
|
createTime: '',
|
||||||
|
updateTime: '',
|
||||||
|
createUser: '',
|
||||||
|
updateUser: '',
|
||||||
|
children: [], // 平台下对应的需求
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新需求
|
||||||
|
function updateDemand(record: DemandItem) {
|
||||||
|
showAddModel.value = true;
|
||||||
|
modelForm.value = { ...record };
|
||||||
|
}
|
||||||
|
// 关联需求(暂无接口)
|
||||||
|
function associatedDemand() {}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -14,7 +14,7 @@
|
||||||
<div class="flex items-center" :class="getActiveClass('all')" @click="setActiveFolder('all')">
|
<div class="flex items-center" :class="getActiveClass('all')" @click="setActiveFolder('all')">
|
||||||
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||||
<div class="folder-name mx-[4px]">{{ t('caseManagement.featureCase.allCase') }}</div>
|
<div class="folder-name mx-[4px]">{{ t('caseManagement.featureCase.allCase') }}</div>
|
||||||
<div class="folder-count">({{ recycleModulesCount.all }})</div></div
|
<div class="folder-count">({{ recycleModulesCount.all || 0 }})</div></div
|
||||||
>
|
>
|
||||||
<div class="ml-auto flex items-center">
|
<div class="ml-auto flex items-center">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-divider class="my-[8px]" />
|
<a-divider class="my-[8px]" />
|
||||||
<a-spin class="w-full" :loading="loading">
|
<a-spin class="h-[calc(100vh-274px)] w-full" :loading="loading">
|
||||||
<MsTree
|
<MsTree
|
||||||
v-model:focus-node-key="focusNodeKey"
|
v-model:focus-node-key="focusNodeKey"
|
||||||
:selected-keys="selectedKeys"
|
:selected-keys="selectedKeys"
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
<div class="page-header mb-4 h-[34px]">
|
<div class="page-header mb-4 h-[34px]">
|
||||||
<div class="text-[var(--color-text-1)]"
|
<div class="text-[var(--color-text-1)]"
|
||||||
>{{ currentModuleName }}
|
>{{ currentModuleName }}
|
||||||
<span class="text-[var(--color-text-4)]"> ({{ recycleModulesCount[activeFolder] }})</span></div
|
<span class="text-[var(--color-text-4)]"> ({{ recycleModulesCount[activeFolder] || 0 }})</span></div
|
||||||
>
|
>
|
||||||
<div class="flex w-[80%] items-center justify-end">
|
<div class="flex w-[80%] items-center justify-end">
|
||||||
<a-select class="w-[240px]" :placeholder="t('caseManagement.featureCase.versionPlaceholder')">
|
<a-select class="w-[240px]" :placeholder="t('caseManagement.featureCase.versionPlaceholder')">
|
||||||
|
@ -129,7 +129,6 @@
|
||||||
* @description 功能用例-回收站
|
* @description 功能用例-回收站
|
||||||
*/
|
*/
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
@ -408,7 +407,7 @@
|
||||||
|
|
||||||
const virtualListProps = computed(() => {
|
const virtualListProps = computed(() => {
|
||||||
return {
|
return {
|
||||||
height: 'calc(100vh - 316px)',
|
height: 'calc(100vh - 270px)',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -645,7 +644,7 @@
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.pageWrap {
|
.pageWrap {
|
||||||
min-width: 1000px;
|
min-width: 1000px;
|
||||||
height: calc(100vh - 136px);
|
height: calc(100vh - 126px);
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
@apply bg-white;
|
@apply bg-white;
|
||||||
.back {
|
.back {
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
<template>
|
||||||
|
<MsDrawer
|
||||||
|
v-model:visible="showSettingVisible"
|
||||||
|
:mask="false"
|
||||||
|
:title="t('caseManagement.featureCase.displaySetting')"
|
||||||
|
:width="480"
|
||||||
|
unmount-on-close
|
||||||
|
:footer="false"
|
||||||
|
>
|
||||||
|
<div class="header mb-1 flex h-[22px] items-center justify-between">
|
||||||
|
<div class="flex items-center text-[var(--color-text-4)]"
|
||||||
|
>{{ t('caseManagement.featureCase.displaySetting') }}
|
||||||
|
|
||||||
|
<a-tooltip>
|
||||||
|
<template #content>
|
||||||
|
<div>{{ t('caseManagement.featureCase.tabShowSetting') }} </div>
|
||||||
|
<div>{{ t('caseManagement.featureCase.closeModuleTab') }}</div>
|
||||||
|
<div>{{ t('caseManagement.featureCase.enableModuleTab') }}</div>
|
||||||
|
</template>
|
||||||
|
<span class="inline-block align-middle">
|
||||||
|
<icon-question-circle class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
|
||||||
|
/></span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="cursor-pointer text-[rgb(var(--primary-5))]" @click="setDefault"
|
||||||
|
>{{ t('caseManagement.featureCase.recoverDefault') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="itemTab">
|
||||||
|
<span>{{ t('caseManagement.featureCase.detail') }}</span>
|
||||||
|
<a-switch v-model="detailEnable" size="small" :disabled="true" />
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="center" class="non-sort"
|
||||||
|
><span class="one-line-text text-xs text-[var(--color-text-4)]">{{
|
||||||
|
t('caseManagement.featureCase.nonClosableTab')
|
||||||
|
}}</span></a-divider
|
||||||
|
>
|
||||||
|
<div v-for="item of tabSettingList" :key="item.key" class="itemTab">
|
||||||
|
<span>{{ t(item.title) }}</span>
|
||||||
|
<a-switch v-model="item.enable" size="small" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MsDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
|
|
||||||
|
import type { TabItemType } from '@/models/caseManagement/featureCase';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', val: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const showSettingVisible = ref<boolean>(false);
|
||||||
|
const detailEnable = ref<boolean>(true);
|
||||||
|
|
||||||
|
const tabDefaultSettingList = ref<TabItemType[]>([
|
||||||
|
{
|
||||||
|
key: 'case',
|
||||||
|
title: 'caseManagement.featureCase.case',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'requirement',
|
||||||
|
title: 'caseManagement.featureCase.requirement',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'bug',
|
||||||
|
title: 'caseManagement.featureCase.bug',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dependency',
|
||||||
|
title: 'caseManagement.featureCase.dependency',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'caseReview',
|
||||||
|
title: 'caseManagement.featureCase.caseReview',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'testPlan',
|
||||||
|
title: 'caseManagement.featureCase.testPlan',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'comments',
|
||||||
|
title: 'caseManagement.featureCase.comments',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'changeHistory',
|
||||||
|
title: 'caseManagement.featureCase.changeHistory',
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tabList = computed(() => {
|
||||||
|
return featureCaseStore.tabSettingList;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabSettingList = ref([...tabList.value]);
|
||||||
|
|
||||||
|
function setDefault() {
|
||||||
|
tabSettingList.value = tabSettingList.value.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
enable: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(val) => {
|
||||||
|
showSettingVisible.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showSettingVisible.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:visible', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => tabSettingList.value,
|
||||||
|
(val) => {
|
||||||
|
featureCaseStore.setTab(val as TabItemType[]);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (tabList.value.length < 1) {
|
||||||
|
featureCaseStore.setTab(tabDefaultSettingList.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.itemTab {
|
||||||
|
height: 38px;
|
||||||
|
@apply flex items-center justify-between p-3;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,446 @@
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
|
<template>
|
||||||
|
<div class="caseDetailWrapper ml-1">
|
||||||
|
<a-form ref="caseFormRef" class="rounded-[4px]" :model="detailForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
class="relative"
|
||||||
|
field="precondition"
|
||||||
|
:label="t('system.orgTemplate.precondition')"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<span class="absolute right-[6px] top-0">
|
||||||
|
<a-button v-if="props.allowEdit" type="text" class="px-0" @click="prepositionEdit">
|
||||||
|
<MsIcon type="icon-icon_edit_outlined" class="mr-1 font-[16px] text-[rgb(var(--primary-5))]" />{{
|
||||||
|
t('caseManagement.featureCase.contentEdit')
|
||||||
|
}}</a-button
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<MsRichText v-if="isEditPreposition" v-model:model-value="detailForm.prerequisite" class="mt-2" />
|
||||||
|
<div v-else class="text-[var(--color-text-3)]" v-html="detailForm?.prerequisite || '-'"></div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
field="step"
|
||||||
|
:label="
|
||||||
|
detailForm.caseEditType === 'STEP'
|
||||||
|
? t('system.orgTemplate.stepDescription')
|
||||||
|
: t('system.orgTemplate.textDescription')
|
||||||
|
"
|
||||||
|
class="relative"
|
||||||
|
>
|
||||||
|
<div class="absolute left-16 top-0 font-normal">
|
||||||
|
<a-divider direction="vertical" />
|
||||||
|
<a-dropdown :popup-max-height="false" @select="handleSelectType">
|
||||||
|
<span class="changeType text-[var(--color-text-3)]"
|
||||||
|
>{{ t('system.orgTemplate.changeType') }} <icon-down
|
||||||
|
/></span>
|
||||||
|
<template #content>
|
||||||
|
<a-doption value="STEP" :class="getSelectTypeClass('STEP')">
|
||||||
|
{{ t('system.orgTemplate.stepDescription') }}</a-doption
|
||||||
|
>
|
||||||
|
<a-doption value="TEXT" :class="getSelectTypeClass('TEXT')">{{
|
||||||
|
t('system.orgTemplate.textDescription')
|
||||||
|
}}</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
<!-- 步骤描述 -->
|
||||||
|
<div v-if="detailForm.caseEditType === 'STEP'" class="w-full">
|
||||||
|
<AddStep v-model:step-list="stepData" :is-disabled="isEditPreposition" />
|
||||||
|
</div>
|
||||||
|
<!-- 文本描述 -->
|
||||||
|
<MsRichText
|
||||||
|
v-if="detailForm.caseEditType === 'TEXT' && isEditPreposition"
|
||||||
|
v-model:modelValue="detailForm.textDescription"
|
||||||
|
/>
|
||||||
|
<div v-if="detailForm.caseEditType === 'TEXT' && !isEditPreposition">{{
|
||||||
|
detailForm.textDescription || '-'
|
||||||
|
}}</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="detailForm.caseEditType === 'TEXT'"
|
||||||
|
field="remark"
|
||||||
|
:label="t('caseManagement.featureCase.expectedResult')"
|
||||||
|
>
|
||||||
|
<MsRichText
|
||||||
|
v-if="detailForm.caseEditType === 'TEXT' && isEditPreposition"
|
||||||
|
v-model:modelValue="detailForm.expectedResult"
|
||||||
|
/>
|
||||||
|
<div v-else class="text-[var(--color-text-3)]" v-html="detailForm.description || '-'"></div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="remark" :label="t('caseManagement.featureCase.remark')">
|
||||||
|
<MsRichText v-if="isEditPreposition" v-model:modelValue="detailForm.description" />
|
||||||
|
<div v-else class="text-[var(--color-text-3)]" v-html="detailForm.description || '-'"></div>
|
||||||
|
</a-form-item>
|
||||||
|
<div v-if="isEditPreposition" class="flex justify-end">
|
||||||
|
<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></div
|
||||||
|
>
|
||||||
|
<a-form-item field="attachment" :label="t('caseManagement.featureCase.attachment')">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="mb-1">
|
||||||
|
<a-dropdown position="tr" trigger="hover">
|
||||||
|
<a-button type="outline">
|
||||||
|
<template #icon> <icon-plus class="text-[14px]" /> </template
|
||||||
|
>{{ t('system.orgTemplate.addAttachment') }}</a-button
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<a-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
: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)]">
|
||||||
|
<icon-upload />{{ t('caseManagement.featureCase.uploadFile') }}</a-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-upload>
|
||||||
|
<a-button type="text" class="!text-[var(--color-text-1)]" @click="associatedFile">
|
||||||
|
<MsIcon type="icon-icon_link-copy_outlined" size="16" />{{
|
||||||
|
t('caseManagement.featureCase.associatedFile')
|
||||||
|
}}</a-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="!hover:bg-[rgb(var(--primary-1))] !text-[var(--color-text-4)]">{{
|
||||||
|
t('system.orgTemplate.addAttachmentTip')
|
||||||
|
}}</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<!-- 文件列表开始 -->
|
||||||
|
<div class="w-[90%]">
|
||||||
|
<MsFileList ref="fileListRef" v-model:file-list="fileList" mode="static">
|
||||||
|
<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)">
|
||||||
|
{{ t('caseManagement.featureCase.storage') }}
|
||||||
|
</MsButton>
|
||||||
|
<MsButton 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)">
|
||||||
|
{{ t('caseManagement.featureCase.download') }}
|
||||||
|
</MsButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</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">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
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 { updateCaseRequest } 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 { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
|
import type { StepList } from '@/models/caseManagement/featureCase';
|
||||||
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
|
|
||||||
|
import { convertToFile } from './utils';
|
||||||
|
import debounce from 'lodash-es/debounce';
|
||||||
|
|
||||||
|
const formCreateStore = useFormCreateStore();
|
||||||
|
|
||||||
|
const caseFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
form: Record<string, any>;
|
||||||
|
allowEdit?: boolean; // 是否允许编辑
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
allowEdit: true, // 是否允许编辑
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'updateSuccess'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const detailForm = ref<Record<string, any>>({
|
||||||
|
projectId: currentProjectId.value,
|
||||||
|
templateId: '',
|
||||||
|
name: '',
|
||||||
|
prerequisite: '',
|
||||||
|
caseEditType: 'STEP',
|
||||||
|
steps: '',
|
||||||
|
textDescription: '',
|
||||||
|
expectedResult: '',
|
||||||
|
description: '',
|
||||||
|
publicCase: false,
|
||||||
|
moduleId: '',
|
||||||
|
versionId: '',
|
||||||
|
tags: [],
|
||||||
|
customFields: [],
|
||||||
|
relateFileMetaIds: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 步骤描述
|
||||||
|
const stepData = ref<StepList[]>([
|
||||||
|
{
|
||||||
|
id: getGenerateId(),
|
||||||
|
step: '',
|
||||||
|
expected: '',
|
||||||
|
showStep: false,
|
||||||
|
showExpected: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const isEditPreposition = ref<boolean>(false); // 非编辑状态
|
||||||
|
|
||||||
|
// 更改类型
|
||||||
|
const handleSelectType = (value: string | number | Record<string, any> | undefined) => {
|
||||||
|
detailForm.value.caseEditType = value as string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取类型样式
|
||||||
|
function getSelectTypeClass(type: string) {
|
||||||
|
return detailForm.value.caseEditType === type
|
||||||
|
? ['bg-[rgb(var(--primary-1))]', '!text-[rgb(var(--primary-5))]']
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑前置条件
|
||||||
|
function prepositionEdit() {
|
||||||
|
isEditPreposition.value = !isEditPreposition.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
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([]);
|
||||||
|
|
||||||
|
// 后台传过来的local文件的item列表
|
||||||
|
const oldLocalFileList = computed(() => {
|
||||||
|
return attachmentsList.value.filter((item: any) => item.local);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 后台已保存本地文件
|
||||||
|
const currentOldLocalFileList = computed(() => {
|
||||||
|
return fileList.value.filter((item) => item.local && item.status !== 'init').map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 已经关联过的id列表
|
||||||
|
const associateFileIds = computed(() => {
|
||||||
|
return attachmentsList.value.filter((item: any) => !item.local).map((item: any) => item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前新增传过来的关联list
|
||||||
|
const currentAlreadyAssociateFileList = computed(() => {
|
||||||
|
return fileList.value
|
||||||
|
.filter((item) => !item.local && !associateFileIds.value.includes(item.uid))
|
||||||
|
.map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增关联文件ID列表
|
||||||
|
const newAssociateFileListIds = computed(() => {
|
||||||
|
return fileList.value
|
||||||
|
.filter((item: any) => !item.local && !associateFileIds.value.includes(item.uid))
|
||||||
|
.map((item: any) => item.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除本地上传的文件id
|
||||||
|
const deleteFileMetaIds = computed(() => {
|
||||||
|
return oldLocalFileList.value
|
||||||
|
.filter((item: any) => !currentOldLocalFileList.value.includes(item.id))
|
||||||
|
.map((item: any) => item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取消关联文件id
|
||||||
|
const unLinkFilesIds = computed(() => {
|
||||||
|
return associateFileIds.value.filter((id: string) => !currentAlreadyAssociateFileList.value.includes(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRuleList = computed(() =>
|
||||||
|
formCreateStore.formCreateRuleMap.get(FormCreateKeyEnum.CASE_CUSTOM_ATTRS_DETAIL)
|
||||||
|
);
|
||||||
|
|
||||||
|
function getParams() {
|
||||||
|
const steps = stepData.value.map((item, index) => {
|
||||||
|
return {
|
||||||
|
num: index,
|
||||||
|
desc: item.step,
|
||||||
|
result: item.expected,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const customFieldsMaps: Record<string, any> = {};
|
||||||
|
formRuleList.value?.forEach((item: any) => {
|
||||||
|
customFieldsMaps[item.field as string] = item.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
request: {
|
||||||
|
...detailForm.value,
|
||||||
|
steps: JSON.stringify(steps),
|
||||||
|
deleteFileMetaIds: deleteFileMetaIds.value,
|
||||||
|
unLinkFilesIds: unLinkFilesIds.value,
|
||||||
|
newAssociateFileListIds: newAssociateFileListIds.value,
|
||||||
|
tags: JSON.parse(detailForm.value.tags),
|
||||||
|
customFields: customFieldsMaps,
|
||||||
|
},
|
||||||
|
fileList: fileList.value.filter((item: any) => item.status === 'init'), // 总文件列表
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
function handleOK() {
|
||||||
|
caseFormRef.value?.validate().then(async (res: any) => {
|
||||||
|
if (!res) {
|
||||||
|
try {
|
||||||
|
confirmLoading.value = true;
|
||||||
|
await updateCaseRequest(getParams());
|
||||||
|
Message.success(t('caseManagement.featureCase.editSuccess'));
|
||||||
|
isEditPreposition.value = false;
|
||||||
|
emit('updateSuccess');
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
confirmLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
isEditPreposition.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDetails() {
|
||||||
|
const { steps, attachments } = detailForm.value;
|
||||||
|
if (steps) {
|
||||||
|
stepData.value = JSON.parse(steps).map((item: any) => {
|
||||||
|
return {
|
||||||
|
step: item.desc,
|
||||||
|
expected: item.result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
attachmentsList.value = attachments;
|
||||||
|
// 处理文件列表
|
||||||
|
fileList.value = attachments
|
||||||
|
.map((fileInfo: any) => {
|
||||||
|
return {
|
||||||
|
...fileInfo,
|
||||||
|
name: fileInfo.fileName,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.map((fileInfo: any) => {
|
||||||
|
return convertToFile(fileInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.form,
|
||||||
|
() => {
|
||||||
|
detailForm.value = { ...props.form };
|
||||||
|
getDetails();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 单独更新字段
|
||||||
|
const updateCustomFields = debounce(() => {
|
||||||
|
const customFieldsMaps: Record<string, any> = {};
|
||||||
|
formRuleList.value?.forEach((item: any) => {
|
||||||
|
customFieldsMaps[item.field as string] = item.value;
|
||||||
|
});
|
||||||
|
detailForm.value.customFields = customFieldsMaps as Record<string, any>;
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
// 监视收集自定义字段参数
|
||||||
|
watch(
|
||||||
|
() => formRuleList.value,
|
||||||
|
() => {
|
||||||
|
const customFieldsValues = props.form.customFields.map((item: any) => JSON.parse(item.defaultValue));
|
||||||
|
// 如果和起始值不一致更新某个字段
|
||||||
|
const isChange = formRuleList.value?.every((item: any) => customFieldsValues.includes(item.value));
|
||||||
|
if (!isChange) {
|
||||||
|
updateCustomFields();
|
||||||
|
handleOK();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.arco-form-item-label) {
|
||||||
|
font-weight: bold !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,8 @@
|
||||||
|
import type { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { AssociatedList } from '@/models/caseManagement/featureCase';
|
||||||
import { StatusType } from '@/enums/caseEnum';
|
import { StatusType } from '@/enums/caseEnum';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -89,5 +92,29 @@ export function getReviewStatusClass(status: keyof typeof StatusType) {
|
||||||
return 'text-[rgb(var(--link-6))]';
|
return 'text-[rgb(var(--link-6))]';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** *
|
||||||
|
*
|
||||||
|
* @description 将文件信息转换为文件格式
|
||||||
|
* @param {stafileInfotus} 文件file
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function convertToFile(fileInfo: AssociatedList): MsFileItem {
|
||||||
|
const fileName = fileInfo.fileType ? `${fileInfo.name}.${fileInfo.fileType || ''}` : `${fileInfo.name}`;
|
||||||
|
const type = fileName.split('.')[1];
|
||||||
|
const file = new File([new Blob()], `${fileName}`, {
|
||||||
|
type: `application/${type}`,
|
||||||
|
});
|
||||||
|
Object.defineProperty(file, 'size', { value: fileInfo.size });
|
||||||
|
return {
|
||||||
|
enable: fileInfo.enable || false,
|
||||||
|
file,
|
||||||
|
name: fileName,
|
||||||
|
percent: 0,
|
||||||
|
status: 'done',
|
||||||
|
uid: fileInfo.id,
|
||||||
|
url: `http://172.16.200.18:8081/${fileInfo.filePath || ''}`,
|
||||||
|
local: fileInfo.local,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -1,91 +1,94 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-[16px]">
|
<div class="rounded-2xl bg-white">
|
||||||
<a-button type="primary" @click="caseDetail">
|
<div class="p-[24px] pb-[16px]">
|
||||||
{{ t('caseManagement.featureCase.creatingCase') }}
|
<a-button type="primary" @click="caseDetail">
|
||||||
</a-button>
|
{{ t('caseManagement.featureCase.creatingCase') }}
|
||||||
<a-button class="mx-3" type="outline"> {{ t('caseManagement.featureCase.importExcel') }} </a-button>
|
</a-button>
|
||||||
<a-button type="outline"> {{ t('caseManagement.featureCase.importXmind') }} </a-button>
|
<a-button class="mx-3" type="outline"> {{ t('caseManagement.featureCase.importExcel') }} </a-button>
|
||||||
</div>
|
<a-button type="outline"> {{ t('caseManagement.featureCase.importXmind') }} </a-button>
|
||||||
<div class="pageWrap">
|
</div>
|
||||||
<MsSplitBox>
|
<a-divider class="!my-0" />
|
||||||
<template #left>
|
<div class="pageWrap">
|
||||||
<div class="p-[24px] pb-0">
|
<MsSplitBox>
|
||||||
<div class="feature-case h-[100%]">
|
<template #left>
|
||||||
<div class="case h-[38px]">
|
<div class="p-[24px] pb-0">
|
||||||
<div class="flex items-center" :class="getActiveClass('all')" @click="setActiveFolder('all')">
|
<div class="feature-case h-[100%]">
|
||||||
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
|
||||||
<div class="folder-name mx-[4px]">{{ t('caseManagement.featureCase.allCase') }}</div>
|
|
||||||
<div class="folder-count">({{ modulesCount.all }})</div></div
|
|
||||||
>
|
|
||||||
<div class="ml-auto flex items-center">
|
|
||||||
<a-tooltip
|
|
||||||
:content="
|
|
||||||
isExpandAll ? t('project.fileManagement.collapseAll') : t('project.fileManagement.expandAll')
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<MsButton type="icon" status="secondary" class="!mr-0 p-[4px]" @click="expandHandler">
|
|
||||||
<MsIcon :type="isExpandAll ? 'icon-icon_folder_collapse1' : 'icon-icon_folder_expansion1'" />
|
|
||||||
</MsButton>
|
|
||||||
</a-tooltip>
|
|
||||||
<MsPopConfirm
|
|
||||||
ref="confirmRef"
|
|
||||||
v-model:visible="addSubVisible"
|
|
||||||
:is-delete="false"
|
|
||||||
:title="t('caseManagement.featureCase.addSubModule')"
|
|
||||||
:all-names="rootModulesName"
|
|
||||||
:loading="confirmLoading"
|
|
||||||
:ok-text="t('common.confirm')"
|
|
||||||
:field-config="{
|
|
||||||
placeholder: t('caseManagement.featureCase.addGroupTip'),
|
|
||||||
}"
|
|
||||||
@confirm="confirmHandler"
|
|
||||||
>
|
|
||||||
<MsButton type="icon" class="!mr-0 p-[2px]">
|
|
||||||
<MsIcon
|
|
||||||
type="icon-icon_create_planarity"
|
|
||||||
size="18"
|
|
||||||
class="text-[rgb(var(--primary-5))] hover:text-[rgb(var(--primary-4))]"
|
|
||||||
/>
|
|
||||||
</MsButton>
|
|
||||||
</MsPopConfirm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a-divider class="my-[8px]" />
|
|
||||||
<FeatureCaseTree
|
|
||||||
ref="caseTreeRef"
|
|
||||||
v-model:selected-keys="selectedKeys"
|
|
||||||
:all-names="rootModulesName"
|
|
||||||
:active-folder="activeFolder"
|
|
||||||
:is-expand-all="isExpandAll"
|
|
||||||
:modules-count="modulesCount"
|
|
||||||
@case-node-select="caseNodeSelect"
|
|
||||||
@init="setRootModules"
|
|
||||||
></FeatureCaseTree>
|
|
||||||
<div class="b-0 absolute w-[88%]">
|
|
||||||
<a-divider class="!my-0 !mb-2" />
|
|
||||||
<div class="case h-[38px]">
|
<div class="case h-[38px]">
|
||||||
<div class="flex items-center" :class="getActiveClass('recycle')" @click="setActiveFolder('recycle')">
|
<div class="flex items-center" :class="getActiveClass('all')" @click="setActiveFolder('all')">
|
||||||
<MsIcon type="icon-icon_delete-trash_outlined" class="folder-icon" />
|
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||||
<div class="folder-name mx-[4px]">{{ t('caseManagement.featureCase.recycle') }}</div>
|
<div class="folder-name mx-[4px]">{{ t('caseManagement.featureCase.allCase') }}</div>
|
||||||
<div class="folder-count">({{ recycleModulesCount.all }})</div></div
|
<div class="folder-count">({{ modulesCount.all || 0 }})</div></div
|
||||||
>
|
>
|
||||||
|
<div class="ml-auto flex items-center">
|
||||||
|
<a-tooltip
|
||||||
|
:content="
|
||||||
|
isExpandAll ? t('project.fileManagement.collapseAll') : t('project.fileManagement.expandAll')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<MsButton type="icon" status="secondary" class="!mr-0 p-[4px]" @click="expandHandler">
|
||||||
|
<MsIcon :type="isExpandAll ? 'icon-icon_folder_collapse1' : 'icon-icon_folder_expansion1'" />
|
||||||
|
</MsButton>
|
||||||
|
</a-tooltip>
|
||||||
|
<MsPopConfirm
|
||||||
|
ref="confirmRef"
|
||||||
|
v-model:visible="addSubVisible"
|
||||||
|
:is-delete="false"
|
||||||
|
:title="t('caseManagement.featureCase.addSubModule')"
|
||||||
|
:all-names="rootModulesName"
|
||||||
|
:loading="confirmLoading"
|
||||||
|
:ok-text="t('common.confirm')"
|
||||||
|
:field-config="{
|
||||||
|
placeholder: t('caseManagement.featureCase.addGroupTip'),
|
||||||
|
}"
|
||||||
|
@confirm="confirmHandler"
|
||||||
|
>
|
||||||
|
<MsButton type="icon" class="!mr-0 p-[2px]">
|
||||||
|
<MsIcon
|
||||||
|
type="icon-icon_create_planarity"
|
||||||
|
size="18"
|
||||||
|
class="text-[rgb(var(--primary-5))] hover:text-[rgb(var(--primary-4))]"
|
||||||
|
/>
|
||||||
|
</MsButton>
|
||||||
|
</MsPopConfirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider class="my-[8px]" />
|
||||||
|
<FeatureCaseTree
|
||||||
|
ref="caseTreeRef"
|
||||||
|
v-model:selected-keys="selectedKeys"
|
||||||
|
:all-names="rootModulesName"
|
||||||
|
:active-folder="activeFolder"
|
||||||
|
:is-expand-all="isExpandAll"
|
||||||
|
:modules-count="modulesCount"
|
||||||
|
@case-node-select="caseNodeSelect"
|
||||||
|
@init="setRootModules"
|
||||||
|
></FeatureCaseTree>
|
||||||
|
<div class="b-0 absolute w-[88%]">
|
||||||
|
<a-divider class="!my-0 !mb-2" />
|
||||||
|
<div class="case h-[38px]">
|
||||||
|
<div class="flex items-center" :class="getActiveClass('recycle')" @click="setActiveFolder('recycle')">
|
||||||
|
<MsIcon type="icon-icon_delete-trash_outlined" class="folder-icon" />
|
||||||
|
<div class="folder-name mx-[4px]">{{ t('caseManagement.featureCase.recycle') }}</div>
|
||||||
|
<div class="folder-count">({{ recycleModulesCount.all || 0 }})</div></div
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
<template #right>
|
||||||
<template #right>
|
<div class="p-[24px]">
|
||||||
<div class="p-[24px]">
|
<CaseTable
|
||||||
<CaseTable
|
:active-folder="activeFolder"
|
||||||
:active-folder="activeFolder"
|
:offspring-ids="offspringIds"
|
||||||
:offspring-ids="offspringIds"
|
:active-folder-type="activeCaseType"
|
||||||
:active-folder-type="activeCaseType"
|
:modules-count="modulesCount"
|
||||||
:modules-count="modulesCount"
|
@init="initModulesCount"
|
||||||
@init="initModulesCount"
|
></CaseTable>
|
||||||
></CaseTable>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</MsSplitBox>
|
||||||
</MsSplitBox>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -234,12 +237,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function test() {
|
|
||||||
router.push({
|
|
||||||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_CREATE_SUCCESS,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置默认选中状态
|
// 设置默认选中状态
|
||||||
router.beforeEach((to: any, from: any, next) => {
|
router.beforeEach((to: any, from: any, next) => {
|
||||||
const routeEnumValues = Object.values(CaseManagementRouteEnum);
|
const routeEnumValues = Object.values(CaseManagementRouteEnum);
|
||||||
|
@ -260,7 +257,7 @@
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.pageWrap {
|
.pageWrap {
|
||||||
min-width: 1000px;
|
min-width: 1000px;
|
||||||
height: calc(100vh - 136px);
|
height: calc(100vh - 166px);
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
@apply bg-white;
|
@apply bg-white;
|
||||||
.case {
|
.case {
|
||||||
|
|
|
@ -119,4 +119,40 @@ export default {
|
||||||
'caseManagement.featureCase.mightWantTo': 'You might want to',
|
'caseManagement.featureCase.mightWantTo': 'You might want to',
|
||||||
'caseManagement.featureCase.createTestPlan': 'Create a test plan',
|
'caseManagement.featureCase.createTestPlan': 'Create a test plan',
|
||||||
'caseManagement.featureCase.createCaseReview': 'Create use case reviews',
|
'caseManagement.featureCase.createCaseReview': 'Create use case reviews',
|
||||||
|
'caseManagement.featureCase.detailDisplaySetting': 'Display setting',
|
||||||
|
'caseManagement.featureCase.addDemand': 'Add Demand',
|
||||||
|
'caseManagement.featureCase.updateDemand': 'Update Demand ({name})',
|
||||||
|
'caseManagement.featureCase.updateUser': 'processor',
|
||||||
|
'caseManagement.featureCase.displaySetting': 'displaySetting',
|
||||||
|
'caseManagement.featureCase.tabShowSetting': 'tab display setting',
|
||||||
|
'caseManagement.featureCase.closeModuleTab': 'Close: in the drawer not show related modules',
|
||||||
|
'caseManagement.featureCase.enableModuleTab': 'Open: Display the relevant modules in the drawer',
|
||||||
|
'caseManagement.featureCase.recoverDefault': 'Restore default',
|
||||||
|
'caseManagement.featureCase.detail': 'details',
|
||||||
|
'caseManagement.featureCase.nonClosableTab': 'These attributes cannot be turned off',
|
||||||
|
'caseManagement.featureCase.case': 'case',
|
||||||
|
'caseManagement.featureCase.requirement': 'demand',
|
||||||
|
'caseManagement.featureCase.bug': 'bug',
|
||||||
|
'caseManagement.featureCase.dependency': 'dependencies',
|
||||||
|
'caseManagement.featureCase.caseReview': 'case review',
|
||||||
|
'caseManagement.featureCase.testPlan': 'Test plan',
|
||||||
|
'caseManagement.featureCase.comments': 'comments',
|
||||||
|
'caseManagement.featureCase.changeHistory': 'Change history',
|
||||||
|
'caseManagement.featureCase.demandPlatform': 'Platform',
|
||||||
|
'caseManagement.featureCase.pleaseEnterID': 'Please enter ID',
|
||||||
|
'caseManagement.featureCase.requirementTitle': 'Requirement title',
|
||||||
|
'caseManagement.featureCase.pleaseEnterTitle': 'Please enter a requirement title',
|
||||||
|
'caseManagement.featureCase.requirementUrl': 'Demand url',
|
||||||
|
'caseManagement.featureCase.pleaseEnterRequirementUrl': 'Please input requirements url',
|
||||||
|
'caseManagement.featureCase.cancelAssociation': 'Cancel Association',
|
||||||
|
'caseManagement.featureCase.caseDetailTitle': '【{id}】{name}',
|
||||||
|
'caseManagement.featureCase.share': 'share',
|
||||||
|
'caseManagement.featureCase.follow': 'follow',
|
||||||
|
'caseManagement.featureCase.fullScreen': 'Full screen',
|
||||||
|
'caseManagement.featureCase.more': 'More',
|
||||||
|
'caseManagement.featureCase.basicInfo': 'Basic Info',
|
||||||
|
'caseManagement.featureCase.attachment': 'attachment',
|
||||||
|
'caseManagement.featureCase.contentEdit': 'Content Edit',
|
||||||
|
'caseManagement.featureCase.followSuccess': 'Followed Success',
|
||||||
|
'caseManagement.featureCase.cancelFollowSuccess': 'Cancel success',
|
||||||
};
|
};
|
||||||
|
|
|
@ -117,4 +117,40 @@ export default {
|
||||||
'caseManagement.featureCase.mightWantTo': '你可能还想',
|
'caseManagement.featureCase.mightWantTo': '你可能还想',
|
||||||
'caseManagement.featureCase.createTestPlan': '创建测试计划',
|
'caseManagement.featureCase.createTestPlan': '创建测试计划',
|
||||||
'caseManagement.featureCase.createCaseReview': '创建用例评审',
|
'caseManagement.featureCase.createCaseReview': '创建用例评审',
|
||||||
|
'caseManagement.featureCase.detailDisplaySetting': '显示设置',
|
||||||
|
'caseManagement.featureCase.addDemand': '添加需求',
|
||||||
|
'caseManagement.featureCase.updateDemand': '更新需求 ({name})',
|
||||||
|
'caseManagement.featureCase.updateUser': '处理人',
|
||||||
|
'caseManagement.featureCase.showSetting': '显示设置',
|
||||||
|
'caseManagement.featureCase.tabShowSetting': 'tab 显示设置',
|
||||||
|
'caseManagement.featureCase.closeModuleTab': '关闭: 在抽屉内不展示相关模块',
|
||||||
|
'caseManagement.featureCase.enableModuleTab': '开启: 在抽屉内展示相关模块',
|
||||||
|
'caseManagement.featureCase.recoverDefault': '恢复默认',
|
||||||
|
'caseManagement.featureCase.detail': '详情',
|
||||||
|
'caseManagement.featureCase.nonClosableTab': '以上属性不可关闭',
|
||||||
|
'caseManagement.featureCase.case': '用例',
|
||||||
|
'caseManagement.featureCase.requirement': '需求',
|
||||||
|
'caseManagement.featureCase.bug': '缺陷',
|
||||||
|
'caseManagement.featureCase.dependency': '依赖关系',
|
||||||
|
'caseManagement.featureCase.caseReview': '用例评审',
|
||||||
|
'caseManagement.featureCase.testPlan': '测试计划',
|
||||||
|
'caseManagement.featureCase.comments': '评论',
|
||||||
|
'caseManagement.featureCase.changeHistory': '变更历史',
|
||||||
|
'caseManagement.featureCase.demandPlatform': '平台',
|
||||||
|
'caseManagement.featureCase.pleaseEnterID': '请输入ID',
|
||||||
|
'caseManagement.featureCase.requirementTitle': '需求标题',
|
||||||
|
'caseManagement.featureCase.pleaseEnterTitle': '请输入需求标题',
|
||||||
|
'caseManagement.featureCase.requirementUrl': '需求地址',
|
||||||
|
'caseManagement.featureCase.pleaseEnterRequirementUrl': '请输入需求地址',
|
||||||
|
'caseManagement.featureCase.cancelAssociation': '取消关联',
|
||||||
|
'caseManagement.featureCase.caseDetailTitle': '【{id}】{name}',
|
||||||
|
'caseManagement.featureCase.share': '分享',
|
||||||
|
'caseManagement.featureCase.follow': '关注',
|
||||||
|
'caseManagement.featureCase.fullScreen': '全屏',
|
||||||
|
'caseManagement.featureCase.more': '更多',
|
||||||
|
'caseManagement.featureCase.basicInfo': '基本信息',
|
||||||
|
'caseManagement.featureCase.attachment': '附件',
|
||||||
|
'caseManagement.featureCase.contentEdit': '内容编辑',
|
||||||
|
'caseManagement.featureCase.followSuccess': '关注成功',
|
||||||
|
'caseManagement.featureCase.cancelFollowSuccess': '取消成功',
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,10 +94,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-empty class="mt-20">
|
<a-empty v-if="filterList.length < 1" class="mt-20"> </a-empty>
|
||||||
暂无数据
|
|
||||||
<span class="cursor-pointer text-[rgb(var(--primary-5))]" @click="goPluginManagement">跳转至插件管理</span>
|
|
||||||
</a-empty>
|
|
||||||
</a-scrollbar>
|
</a-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue