fix(缺陷管理): 缺陷创建编辑复制状态流转问题

--bug=1036120 --user=宋昌昌 【缺陷管理】缺陷状态只有新建 https://www.tapd.cn/55049933/s/1464084
This commit is contained in:
song-cc-rock 2024-02-22 18:34:13 +08:00 committed by 刘瑞斌
parent f2e4816c64
commit eed6f82bcc
7 changed files with 156 additions and 118 deletions

View File

@ -47,5 +47,12 @@ public class BugDetailDTO {
private Boolean followFlag; private Boolean followFlag;
@Schema(description = "附件集合") @Schema(description = "附件集合")
List<BugFileDTO> attachments; private List<BugFileDTO> attachments;
@Schema(description = "第三方平台缺陷ID")
private String platformBugId;
@Schema(description = "缺陷状态")
private String status;
} }

View File

@ -254,6 +254,8 @@ public class BugService {
detail.setProjectId(bug.getProjectId()); detail.setProjectId(bug.getProjectId());
detail.setTemplateId(template.getId()); detail.setTemplateId(template.getId());
detail.setPlatformDefault(template.getPlatformDefault()); detail.setPlatformDefault(template.getPlatformDefault());
detail.setStatus(bug.getStatus());
detail.setPlatformBugId(bug.getPlatformBugId());
if (!detail.getPlatformDefault()) { if (!detail.getPlatformDefault()) {
// 非平台默认模板 {标题, 内容, 标签, 自定义字段: 处理人, 状态} // 非平台默认模板 {标题, 内容, 标签, 自定义字段: 处理人, 状态}
detail.setTitle(bug.getTitle()); detail.setTitle(bug.getTitle());

View File

@ -20,6 +20,7 @@ import io.metersphere.system.mapper.StatusDefinitionMapper;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -296,11 +297,13 @@ public class BaseStatusFlowSettingService {
} else { } else {
//修改时, 获取当前状态的流转选项值即可 //修改时, 获取当前状态的流转选项值即可
List<StatusFlow> nextStatusFlows = baseStatusFlowService.getNextStatusFlows(targetStatusId); List<StatusFlow> nextStatusFlows = baseStatusFlowService.getNextStatusFlows(targetStatusId);
List<String> toIds = new ArrayList<>();
if (CollectionUtils.isEmpty(nextStatusFlows)) { if (CollectionUtils.isEmpty(nextStatusFlows)) {
// 当前状态选项值没有下一步流转选项值, 返回空集合 // 当前状态选项值没有下一步流转选项值, 返回当前状态即可
return List.of(); toIds = List.of(targetStatusId);
} else {
toIds = ListUtils.union(nextStatusFlows.stream().map(StatusFlow::getToId).collect(Collectors.toList()), List.of(targetStatusId));
} }
List<String> toIds = nextStatusFlows.stream().map(StatusFlow::getToId).toList();
List<StatusItem> statusItems = baseStatusItemService.getToStatusItemByScopeIdAndScene(scopeId, scene, toIds); List<StatusItem> statusItems = baseStatusItemService.getToStatusItemByScopeIdAndScene(scopeId, scene, toIds);
statusItems = baseStatusItemService.translateInternalStatusItem(statusItems); statusItems = baseStatusItemService.translateInternalStatusItem(statusItems);
return statusItems.stream().map(item -> new SelectOption(item.getName(), item.getId())).toList(); return statusItems.stream().map(item -> new SelectOption(item.getName(), item.getId())).toList();

View File

@ -1,6 +1,6 @@
import { FormItemType } from '@/components/pure/ms-form-create/types'; import {FormItemType} from '@/components/pure/ms-form-create/types';
import { BatchApiParams } from './common'; import {BatchApiParams} from './common';
export interface BugListItem { export interface BugListItem {
id: string; // 缺陷id id: string; // 缺陷id
@ -86,3 +86,8 @@ export interface OperationFile {
associated: boolean; // 是否是本地 还是关联 associated: boolean; // 是否是本地 还是关联
moduleId?: string; // 文件转存模块id moduleId?: string; // 文件转存模块id
} }
export interface BugTemplateRequest {
fromStatusId: string; // 缺陷当前状态
platformBugKey: string; // 缺陷第三方平台Key
}

View File

@ -155,43 +155,43 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineModel, ref } from 'vue'; import {defineModel, ref} from 'vue';
import { useRoute, useRouter } from 'vue-router'; import {useRoute, useRouter} from 'vue-router';
import { Message } from '@arco-design/web-vue'; import {Message} from '@arco-design/web-vue';
import { debounce } from 'lodash-es'; import {debounce} from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue'; import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types'; import type {FormItem, FormRuleItem} from '@/components/pure/ms-form-create/types';
import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue'; import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import type { MsPaginationI } from '@/components/pure/ms-table/type'; import type {MsPaginationI} from '@/components/pure/ms-table/type';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue'; import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import { CommentInput } from '@/components/business/ms-comment'; import {CommentInput} from '@/components/business/ms-comment';
import { CommentParams } from '@/components/business/ms-comment/types'; import {CommentParams} from '@/components/business/ms-comment/types';
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue'; import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
import BugCaseTab from './bugCaseTab.vue'; import BugCaseTab from './bugCaseTab.vue';
import BugDetailTab from './bugDetailTab.vue'; import BugDetailTab from './bugDetailTab.vue';
import BugHistoryTab from './bugHistoryTab.vue'; import BugHistoryTab from './bugHistoryTab.vue';
import CommentTab from './commentTab.vue'; import CommentTab from './commentTab.vue';
import { import {
createOrUpdateComment, createOrUpdateComment,
deleteSingleBug, deleteSingleBug,
followBug, followBug,
getBugDetail, getBugDetail,
getTemplateById, getTemplateById,
} from '@/api/modules/bug-management/index'; } from '@/api/modules/bug-management/index';
import { useI18n } from '@/hooks/useI18n'; import {useI18n} from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
import { characterLimit } from '@/utils'; import {characterLimit} from '@/utils';
import { BugEditCustomField, BugEditFormObject } from '@/models/bug-management'; import {BugEditCustomField, BugEditFormObject, BugTemplateRequest} from '@/models/bug-management';
import { SelectValue } from '@/models/projectManagement/menuManagement'; import {SelectValue} from '@/models/projectManagement/menuManagement';
import { RouteEnum } from '@/enums/routeEnum'; import {RouteEnum} from '@/enums/routeEnum';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const detailDrawerRef = ref<InstanceType<typeof MsDetailDrawer>>(); const detailDrawerRef = ref<InstanceType<typeof MsDetailDrawer>>();
const wrapperRef = ref(); const wrapperRef = ref();
@ -245,10 +245,10 @@
} }
}; };
const templateChange = async (v: SelectValue, valueObj: BugEditFormObject) => { const templateChange = async (v: SelectValue, valueObj: BugEditFormObject, request: BugTemplateRequest) => {
if (v) { if (v) {
try { try {
const res = await getTemplateById({ projectId: appStore.currentProjectId, id: v }); const res = await getTemplateById({ projectId: appStore.currentProjectId, id: v, fromStatusId: request.fromStatusId, platformBugKey: request.platformBugKey});
getFormRules(res.customFields, valueObj); getFormRules(res.customFields, valueObj);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -268,7 +268,7 @@
}); });
} }
// //
await templateChange(templateId, tmpObj); await templateChange(templateId, tmpObj, {platformBugKey: detail.platformBugId, fromStatusId: detail.status});
} }
const editLoading = ref<boolean>(false); const editLoading = ref<boolean>(false);

View File

@ -159,41 +159,41 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
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';
import { FormRuleItem } from '@/components/pure/ms-form-create/types'; import {FormRuleItem} from '@/components/pure/ms-form-create/types';
import MsIconfont from '@/components/pure/ms-icon-font/index.vue'; import MsIconfont from '@/components/pure/ms-icon-font/index.vue';
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue'; import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
import MsFileList from '@/components/pure/ms-upload/fileList.vue'; import MsFileList from '@/components/pure/ms-upload/fileList.vue';
import { MsFileItem } from '@/components/pure/ms-upload/types'; import {MsFileItem} from '@/components/pure/ms-upload/types';
import RelateFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue'; import RelateFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
import TransferModal from '@/views/case-management/caseManagementFeature/components/tabContent/transferModal.vue'; import TransferModal from '@/views/case-management/caseManagementFeature/components/tabContent/transferModal.vue';
import { import {
checkFileIsUpdateRequest, checkFileIsUpdateRequest,
createOrUpdateBug, createOrUpdateBug,
deleteFileOrCancelAssociation, deleteFileOrCancelAssociation,
downloadFileRequest, downloadFileRequest,
editorUploadFile, editorUploadFile,
getAssociatedFileList, getAssociatedFileList,
previewFile, previewFile,
transferFileRequest, transferFileRequest,
updateFile, updateFile,
uploadOrAssociationFile, uploadOrAssociationFile,
} from '@/api/modules/bug-management'; } from '@/api/modules/bug-management';
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement'; import {getModules, getModulesCount} from '@/api/modules/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n'; import {useI18n} from '@/hooks/useI18n';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
import { downloadByteFile, sleep } from '@/utils'; import {downloadByteFile, sleep} from '@/utils';
import { BugEditCustomFieldItem, BugEditFormObject } from '@/models/bug-management'; import {BugEditCustomFieldItem, BugEditFormObject} from '@/models/bug-management';
import { AssociatedList, AttachFileInfo } from '@/models/caseManagement/featureCase'; import {AssociatedList, AttachFileInfo} from '@/models/caseManagement/featureCase';
import { TableQueryParams } from '@/models/common'; import {TableQueryParams} from '@/models/common';
import { convertToFileByBug, convertToFileByDetail } from '@/views/bug-management/utils'; import {convertToFileByBug, convertToFileByDetail} from '@/views/bug-management/utils';
defineOptions({ defineOptions({
name: 'BugDetailTab', name: 'BugDetailTab',
}); });
@ -231,6 +231,7 @@
}, },
}); });
const form = ref({ const form = ref({
title: '',
description: '', description: '',
deleteLocalFileIds: [] as string[], deleteLocalFileIds: [] as string[],
unLinkRefIds: [] as string[], unLinkRefIds: [] as string[],
@ -261,7 +262,8 @@
const confirmLoading = ref<boolean>(false); const confirmLoading = ref<boolean>(false);
const initCurrentDetail = async (detail: BugEditFormObject) => { const initCurrentDetail = async (detail: BugEditFormObject) => {
const { attachments, description } = detail; const { attachments, title, description } = detail;
form.value.title = title;
form.value.description = description; form.value.description = description;
handleFileFunc(attachments); handleFileFunc(attachments);
}; };
@ -430,6 +432,7 @@
templateId: props.detailInfo.templateId, templateId: props.detailInfo.templateId,
customFields, customFields,
}; };
console.log(tmpObj)
// //
const res = await createOrUpdateBug({ request: tmpObj, fileList: fileList.value as unknown as File[] }); const res = await createOrUpdateBug({ request: tmpObj, fileList: fileList.value as unknown as File[] });
if (res) { if (res) {

View File

@ -188,52 +188,56 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRoute } from 'vue-router'; import {useRoute} 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';
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue'; import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
import { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types'; import {FormItem, FormRuleItem} 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 MsTagsInput from '@/components/pure/ms-tags-input/index.vue'; import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import FileList 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 {MsFileItem} from '@/components/pure/ms-upload/types';
import { MsFileItem } from '@/components/pure/ms-upload/types'; import RelateFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
import RelateFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue'; import TransferModal from '@/views/case-management/caseManagementFeature/components/tabContent/transferModal.vue';
import TransferModal from '@/views/case-management/caseManagementFeature/components/tabContent/transferModal.vue';
import { import {
checkFileIsUpdateRequest, checkFileIsUpdateRequest,
createOrUpdateBug, createOrUpdateBug,
downloadFileRequest, downloadFileRequest,
editorUploadFile, editorUploadFile,
getAssociatedFileList, getAssociatedFileList,
getBugDetail, getBugDetail,
getTemplateById, getTemplateById,
getTemplateOption, getTemplateOption,
previewFile, previewFile,
transferFileRequest, transferFileRequest,
updateFile, updateFile,
} from '@/api/modules/bug-management'; } from '@/api/modules/bug-management';
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement'; import {getModules, getModulesCount} from '@/api/modules/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n'; import {useI18n} from '@/hooks/useI18n';
import useVisit from '@/hooks/useVisit'; import useVisit from '@/hooks/useVisit';
import router from '@/router'; import router from '@/router';
import { useAppStore } from '@/store'; import {useAppStore} from '@/store';
import { downloadByteFile } from '@/utils'; import {downloadByteFile} from '@/utils';
import { scrollIntoView } from '@/utils/dom'; import {scrollIntoView} from '@/utils/dom';
import { BugEditCustomField, BugEditCustomFieldItem, BugEditFormObject } from '@/models/bug-management'; import {
import { AssociatedList, AttachFileInfo } from '@/models/caseManagement/featureCase'; BugEditCustomField,
import { TableQueryParams } from '@/models/common'; BugEditCustomFieldItem,
import { SelectValue } from '@/models/projectManagement/menuManagement'; BugEditFormObject,
import { BugManagementRouteEnum } from '@/enums/routeEnum'; BugTemplateRequest
} from '@/models/bug-management';
import {AssociatedList, AttachFileInfo} from '@/models/caseManagement/featureCase';
import {TableQueryParams} from '@/models/common';
import {SelectValue} from '@/models/projectManagement/menuManagement';
import {BugManagementRouteEnum} from '@/enums/routeEnum';
import { convertToFile } from '../case-management/caseManagementFeature/components/utils'; import {convertToFile} from '../case-management/caseManagementFeature/components/utils';
import { convertToFileByBug } from './utils'; import {convertToFileByBug} from './utils';
defineOptions({ name: 'BugEditPage' }); defineOptions({ name: 'BugEditPage' });
const { t } = useI18n(); const { t } = useI18n();
@ -348,10 +352,14 @@
} }
}; };
const templateChange = async (v: SelectValue) => { const templateChange = async (v: SelectValue, request?: BugTemplateRequest) => {
if (v) { if (v) {
try { try {
const res = await getTemplateById({ projectId: appStore.currentProjectId, id: v }); let param = {projectId: appStore.currentProjectId, id: v}
if (request) {
param = {...param, ...request}
}
const res = await getTemplateById(param);
getFormRules(res.customFields); getFormRules(res.customFields);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -542,7 +550,12 @@
const res = await getBugDetail(id); const res = await getBugDetail(id);
const { customFields, templateId, attachments } = res; const { customFields, templateId, attachments } = res;
// ID // ID
await templateChange(templateId); if (isCopy.value) {
// ,
await templateChange(templateId);
} else {
await templateChange(templateId, {fromStatusId: res.status, platformBugKey: res.platformBugId})
}
if (attachments && attachments.length) { if (attachments && attachments.length) {
attachmentsList.value = attachments; attachmentsList.value = attachments;
// //
@ -564,7 +577,12 @@
const tmpObj = {}; const tmpObj = {};
if (customFields && Array.isArray(customFields)) { if (customFields && Array.isArray(customFields)) {
customFields.forEach((item) => { customFields.forEach((item) => {
tmpObj[item.id] = item.value; if (item.id === 'status' && isCopy.value) {
// ,
tmpObj[item.id] = '';
} else {
tmpObj[item.id] = item.value;
}
}); });
} }
// //