fix(缺陷管理): 修复缺陷更新刷新方式
--bug=1038739 --user=宋昌昌 【缺陷管理】优化更新缺陷刷新方式 https://www.tapd.cn/55049933/s/1491966 --bug=1038537 --user=宋昌昌 【项目管理】添加文件提示和逻辑优化 https://www.tapd.cn/55049933/s/1493052 --bug=1039006 --user=宋昌昌 【项目管理】项目与权限-应用设置-用例管理-关联需求输入错误的key提示优化 https://www.tapd.cn/55049933/s/1493115
This commit is contained in:
parent
0987e1fc82
commit
08826e6236
|
@ -432,6 +432,8 @@ message.domain.bug_updateTime=更新时间
|
|||
message.domain.bug_deleteUser=删除人
|
||||
message.domain.bug_deleteTime=删除时间
|
||||
message.domain.bug_handleUser=处理人
|
||||
message.domain.bug_sync_platform=同步平台
|
||||
message.domain.bug_sync_total_count=同步数量
|
||||
#UI
|
||||
message.domain.ui_name=场景名称
|
||||
message.domain.ui_level=用例等级
|
||||
|
|
|
@ -468,6 +468,8 @@ message.domain.bug_updateTime=Update time
|
|||
message.domain.bug_deleteUser=Delete user
|
||||
message.domain.bug_deleteTime=Delete time
|
||||
message.domain.bug_handleUser=Processor
|
||||
message.domain.bug_sync_platform=Platform
|
||||
message.domain.bug_sync_total_count=Total
|
||||
#UI
|
||||
message.domain.ui_name=Scenario name
|
||||
message.domain.ui_level=Case level
|
||||
|
|
|
@ -467,6 +467,8 @@ message.domain.bug_updateTime=更新时间
|
|||
message.domain.bug_deleteUser=删除人
|
||||
message.domain.bug_deleteTime=删除时间
|
||||
message.domain.bug_handleUser=处理人
|
||||
message.domain.bug_sync_platform=同步平台
|
||||
message.domain.bug_sync_total_count=同步数量
|
||||
#UI
|
||||
message.domain.ui_name=场景名称
|
||||
message.domain.ui_level=用例等级
|
||||
|
|
|
@ -468,6 +468,8 @@ message.domain.bug_updateTime=更新時間
|
|||
message.domain.bug_deleteUser=刪除人
|
||||
message.domain.bug_deleteTime=刪除時間
|
||||
message.domain.bug_handleUser=處理人
|
||||
message.domain.bug_sync_platform=同步平臺
|
||||
message.domain.bug_sync_total_count=同步數量
|
||||
#UI
|
||||
message.domain.ui_name=場景名稱
|
||||
message.domain.ui_level=用例等級
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.bug.service;
|
||||
|
||||
import io.metersphere.project.service.ProjectApplicationService;
|
||||
import io.metersphere.system.domain.User;
|
||||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.notice.NoticeModel;
|
||||
|
@ -23,19 +24,24 @@ public class BugSyncNoticeService {
|
|||
@Resource
|
||||
private NoticeSendService noticeSendService;
|
||||
|
||||
@Resource
|
||||
private ProjectApplicationService projectApplicationService;
|
||||
|
||||
public void sendNotice(int total, String currentUser, String language, String projectId) {
|
||||
String platformName = projectApplicationService.getPlatformName(projectId);
|
||||
User user = userMapper.selectByPrimaryKey(currentUser);
|
||||
Map<String, String> defaultTemplateMap = MessageTemplateUtils.getDefaultTemplateMap();
|
||||
String template = defaultTemplateMap.get(NoticeConstants.TemplateText.BUG_SYNC_TASK_EXECUTE_COMPLETED);
|
||||
Map<String, String> defaultSubjectMap = MessageTemplateUtils.getDefaultTemplateSubjectMap();
|
||||
String subject = defaultSubjectMap.get(NoticeConstants.TemplateText.BUG_SYNC_TASK_EXECUTE_COMPLETED);
|
||||
// ${OPERATOR}同步了${total}条缺陷
|
||||
Map<String, Object> paramMap = new HashMap<>(3);
|
||||
Map<String, Object> paramMap = new HashMap<>(4);
|
||||
paramMap.put(NoticeConstants.RelatedUser.OPERATOR, user.getName());
|
||||
paramMap.put("total", total);
|
||||
paramMap.put("projectId", projectId);
|
||||
paramMap.put("Language", language);
|
||||
NoticeModel noticeModel = NoticeModel.builder().operator(currentUser)
|
||||
paramMap.put("platform", platformName);
|
||||
NoticeModel noticeModel = NoticeModel.builder().operator(currentUser).excludeSelf(false)
|
||||
.context(template).subject(subject).paramMap(paramMap).event(NoticeConstants.Event.EXECUTE_COMPLETED).build();
|
||||
noticeSendService.send(NoticeConstants.TaskType.BUG_SYNC_TASK, noticeModel);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,10 @@ import io.metersphere.sdk.util.Translator;
|
|||
import io.metersphere.system.domain.CustomField;
|
||||
import io.metersphere.system.domain.CustomFieldExample;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import io.metersphere.system.dto.BugNoticeDTO;
|
||||
import io.metersphere.system.dto.BugMessageDTO;
|
||||
import io.metersphere.system.dto.BugSyncNoticeDTO;
|
||||
import io.metersphere.system.dto.request.DefaultBugCustomField;
|
||||
import io.metersphere.system.dto.request.DefaultFunctionalCustomField;
|
||||
import io.metersphere.system.dto.sdk.ApiDefinitionCaseDTO;
|
||||
import io.metersphere.system.dto.sdk.ApiScenarioMessageDTO;
|
||||
import io.metersphere.system.dto.sdk.FunctionalCaseMessageDTO;
|
||||
|
@ -71,11 +74,16 @@ public class NoticeTemplateService {
|
|||
//TODO:获取报告
|
||||
}
|
||||
case NoticeConstants.TaskType.BUG_TASK -> {
|
||||
Field[] allFields = FieldUtils.getAllFields(BugNoticeDTO.class);
|
||||
Field[] allFields = FieldUtils.getAllFields(BugMessageDTO.class);
|
||||
addOptionDto(messageTemplateFieldDTOList, allFields, null);
|
||||
addCustomFiled(messageTemplateFieldDTOList, projectId, TemplateScene.BUG.toString());
|
||||
//TODO:获取报告
|
||||
}
|
||||
case NoticeConstants.TaskType.BUG_SYNC_TASK -> {
|
||||
Field[] allFields = FieldUtils.getAllFields(BugSyncNoticeDTO.class);
|
||||
addOptionDto(messageTemplateFieldDTOList, allFields, null);
|
||||
//TODO:获取报告
|
||||
}
|
||||
case NoticeConstants.TaskType.UI_SCENARIO_TASK -> {
|
||||
Field[] allFields = FieldUtils.getAllFields(UiScenario.class);
|
||||
addOptionDto(messageTemplateFieldDTOList, allFields, "ui_");
|
||||
|
@ -120,7 +128,13 @@ public class NoticeTemplateService {
|
|||
for (CustomField customField : customFields) {
|
||||
MessageTemplateFieldDTO messageTemplateFieldDTO = new MessageTemplateFieldDTO();
|
||||
messageTemplateFieldDTO.setId(customField.getName());
|
||||
messageTemplateFieldDTO.setName(StringUtils.isBlank(customField.getRemark()) ? "-" : customField.getRemark());
|
||||
if (StringUtils.equalsAnyIgnoreCase(customField.getName(),
|
||||
DefaultBugCustomField.DEGREE.getName(), DefaultFunctionalCustomField.PRIORITY.getName())) {
|
||||
// 缺陷严重程度, 用例等级 作为系统内置的自定义字段需要国际化后在模板展示
|
||||
messageTemplateFieldDTO.setName(Translator.get("custom_field." + customField.getName()));
|
||||
} else {
|
||||
messageTemplateFieldDTO.setName(customField.getName());
|
||||
}
|
||||
messageTemplateFieldDTO.setFieldSource(NoticeConstants.FieldSource.CUSTOM_FIELD);
|
||||
messageTemplateFieldDTOS.add(messageTemplateFieldDTO);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package io.metersphere.system.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BugMessageDTO {
|
||||
|
||||
@Schema(description ="message.domain.bug_num")
|
||||
private String id;
|
||||
|
||||
@Schema(description ="message.domain.bug_title")
|
||||
private String title;
|
||||
|
||||
@Schema(description ="message.domain.bug_handleUser")
|
||||
private String handleUser;
|
||||
|
||||
@Schema(description ="message.domain.bug_status")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "message.domain.bug_createUser")
|
||||
private String createUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_updateUser")
|
||||
private String updateUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_deleteUser")
|
||||
private String deleteUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_createTime")
|
||||
private Long createTime;
|
||||
|
||||
@Schema(description = "message.domain.bug_updateTime")
|
||||
private Long updateTime;
|
||||
|
||||
@Schema(description = "message.domain.bug_deleteTime")
|
||||
private Long deleteTime;
|
||||
}
|
|
@ -3,47 +3,15 @@ package io.metersphere.system.dto;
|
|||
import io.metersphere.system.dto.sdk.OptionDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BugNoticeDTO {
|
||||
|
||||
@Schema(description ="message.domain.bug_num")
|
||||
private String id;
|
||||
|
||||
@Schema(description ="message.domain.bug_title")
|
||||
private String title;
|
||||
|
||||
@Schema(description ="message.domain.bug_handleUser")
|
||||
private String handleUser;
|
||||
|
||||
@Schema(description ="message.domain.bug_status")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "message.domain.bug_createUser")
|
||||
private String createUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_updateUser")
|
||||
private String updateUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_deleteUser")
|
||||
private String deleteUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_createTime")
|
||||
private Long createTime;
|
||||
|
||||
@Schema(description = "message.domain.bug_updateTime")
|
||||
private Long updateTime;
|
||||
|
||||
@Schema(description = "message.domain.bug_deleteTime")
|
||||
private Long deleteTime;
|
||||
public class BugNoticeDTO extends BugMessageDTO{
|
||||
|
||||
@Schema(description = "自定义字段内容")
|
||||
private List<OptionDTO> customFields;
|
||||
|
|
|
@ -1,47 +1,20 @@
|
|||
package io.metersphere.system.dto;
|
||||
|
||||
import io.metersphere.system.dto.sdk.OptionDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BugSyncNoticeDTO {
|
||||
|
||||
@Schema(description ="message.domain.bug_title")
|
||||
private String title;
|
||||
@Schema(description ="message.domain.bug_sync_platform")
|
||||
private String platform;
|
||||
|
||||
@Schema(description ="message.domain.bug_handleUser")
|
||||
private String handleUser;
|
||||
|
||||
@Schema(description ="message.domain.bug_status")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "message.domain.bug_createUser")
|
||||
private String createUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_updateUser")
|
||||
private String updateUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_deleteUser")
|
||||
private String deleteUser;
|
||||
|
||||
@Schema(description = "message.domain.bug_createTime")
|
||||
private Long createTime;
|
||||
|
||||
@Schema(description = "message.domain.bug_updateTime")
|
||||
private Long updateTime;
|
||||
|
||||
@Schema(description = "message.domain.bug_deleteTime")
|
||||
private Long deleteTime;
|
||||
|
||||
@Schema(description = "自定义字段内容")
|
||||
private List<OptionDTO> customFields;
|
||||
@Schema(description ="message.domain.bug_sync_total_count")
|
||||
private Integer total;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import io.metersphere.load.domain.LoadTest;
|
|||
import io.metersphere.plan.domain.TestPlan;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import io.metersphere.system.dto.BugNoticeDTO;
|
||||
import io.metersphere.system.dto.BugMessageDTO;
|
||||
import io.metersphere.system.dto.sdk.ApiDefinitionCaseDTO;
|
||||
import io.metersphere.system.dto.sdk.FunctionalCaseMessageDTO;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
|
@ -153,7 +153,7 @@ public class MessageTemplateUtils {
|
|||
allFields = FieldUtils.getAllFields(FunctionalCaseMessageDTO.class);
|
||||
}
|
||||
case NoticeConstants.TaskType.BUG_TASK -> {
|
||||
allFields = FieldUtils.getAllFields(BugNoticeDTO.class);
|
||||
allFields = FieldUtils.getAllFields(BugMessageDTO.class);
|
||||
}
|
||||
case NoticeConstants.TaskType.UI_SCENARIO_TASK -> {
|
||||
allFields = FieldUtils.getAllFields(UiScenario.class);
|
||||
|
|
|
@ -4,8 +4,8 @@ import io.metersphere.project.domain.*;
|
|||
import io.metersphere.project.mapper.MessageTaskBlobMapper;
|
||||
import io.metersphere.project.mapper.MessageTaskMapper;
|
||||
import io.metersphere.project.mapper.ProjectRobotMapper;
|
||||
import io.metersphere.system.notice.MessageDetail;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
import io.metersphere.system.notice.MessageDetail;
|
||||
import io.metersphere.system.notice.utils.MessageTemplateUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
@ -13,7 +13,10 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,5 +62,4 @@ declare global {
|
|||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
:detail-id="props.detailId"
|
||||
:detail-index="props.detailIndex"
|
||||
:table-data="props.tableData"
|
||||
@loading-detail="setDetailLoading"
|
||||
@loaded="handleDetailLoaded"
|
||||
/>
|
||||
<div class="ml-auto flex items-center">
|
||||
|
@ -59,7 +60,7 @@
|
|||
getDetailFunc: (id: string) => Promise<any>; // 获取详情的请求函数
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'loaded']);
|
||||
const emit = defineEmits(['update:visible', 'loaded', 'loadingDetail']);
|
||||
|
||||
const prevNextButtonRef = ref<InstanceType<typeof MsPrevNextButton>>();
|
||||
|
||||
|
@ -99,6 +100,10 @@
|
|||
emit('loaded', val);
|
||||
}
|
||||
|
||||
function setDetailLoading() {
|
||||
emit('loadingDetail');
|
||||
}
|
||||
|
||||
watch(
|
||||
() => innerVisible.value,
|
||||
(val) => {
|
||||
|
@ -128,4 +133,4 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<style lang="less" scoped></style>
|
|
@ -52,7 +52,7 @@
|
|||
getDetailFunc: (id: string) => Promise<any>; // 获取详情的请求函数
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:loading', 'loaded']);
|
||||
const emit = defineEmits(['update:loading', 'loaded', 'loadingDetail']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -94,6 +94,7 @@
|
|||
async function initDetail() {
|
||||
try {
|
||||
innerLoading.value = true;
|
||||
emit('loadingDetail');
|
||||
const res = await props.getDetailFunc(activeDetailId.value);
|
||||
emit('loaded', res);
|
||||
} catch (error) {
|
||||
|
@ -171,4 +172,4 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<style lang="less" scoped></style>
|
|
@ -225,6 +225,18 @@
|
|||
tooltip: item.tooltip,
|
||||
},
|
||||
};
|
||||
if (ruleItem.type === 'input') {
|
||||
// input 需要单独emit监听事件 emit:['change', 'blur'],
|
||||
ruleItem.on = {
|
||||
blur: () => {
|
||||
// 失去焦点后value值改变
|
||||
if (item.value !== fApi.value.getValue(item.name)) {
|
||||
fApi.value.validateField(item.name);
|
||||
emit('change', fApi.value.getValue(item.name), ruleItem, fApi.value);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
// 如果存在placeholder, 替换掉默认的placeholder
|
||||
if (item.platformPlaceHolder) {
|
||||
ruleItem.props.placeholder = item.platformPlaceHolder;
|
||||
|
@ -256,6 +268,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
function changeHandler(value: any, defaultValue: any, formRuleItem: FormRuleItem, api: any) {
|
||||
if (formRuleItem.type === 'input') {
|
||||
// 输入框失去焦点后再保存
|
||||
return;
|
||||
}
|
||||
fApi.value.validateField(value);
|
||||
emit('change', defaultValue, formRuleItem, api);
|
||||
}
|
||||
|
||||
function getControlFormItems() {
|
||||
const convertedData = formItems.value.map((item: FormItem) => convertItem(item));
|
||||
formRuleList.value = convertedData;
|
||||
|
@ -270,7 +291,6 @@
|
|||
}
|
||||
function setValue() {
|
||||
nextTick(() => {
|
||||
console.log(props.formRule);
|
||||
const tempObj: Record<string, any> = {};
|
||||
props.formRule.forEach((item) => {
|
||||
tempObj[item.name] = item.value;
|
||||
|
@ -306,11 +326,6 @@
|
|||
},
|
||||
};
|
||||
|
||||
function changeHandler(value: any, defaultValue: any, formRuleItem: FormRuleItem, api: any) {
|
||||
fApi.value.validateField(value);
|
||||
emit('change', defaultValue, formRuleItem, api);
|
||||
}
|
||||
|
||||
function handleMounted() {
|
||||
// setValue();
|
||||
emit('mounted');
|
||||
|
@ -339,4 +354,4 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped></style>
|
|
@ -56,7 +56,7 @@
|
|||
v-if="item.status === UploadStatus.init"
|
||||
class="text-[12px] leading-[16px] text-[var(--color-text-4)]"
|
||||
>
|
||||
{{ t('ms.upload.waiting_save') }}
|
||||
{{ initFileSaveTips ? initFileSaveTips : t('ms.upload.waiting') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="item.status === UploadStatus.done"
|
||||
|
@ -76,8 +76,8 @@
|
|||
}}
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<div v-if="showUploadSuccess(item)" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_colorful" />
|
||||
<div v-if="showUploadSuccess(item)" class="ml-4 flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_colorful" class="mr-2" />
|
||||
{{ t('ms.upload.uploadSuccess') }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -185,6 +185,7 @@
|
|||
showDelete?: boolean; // 是否展示删除按钮
|
||||
handleView?: (item: MsFileItem) => void; // 是否自定义预览
|
||||
showUploadTypeDesc?: boolean; // 自定义上传类型关联于&上传于
|
||||
initFileSaveTips?: string; // 上传初始文件时的提示
|
||||
}>(),
|
||||
{
|
||||
mode: 'remote',
|
||||
|
@ -211,6 +212,7 @@
|
|||
watch(
|
||||
() => props.fileList,
|
||||
(val) => {
|
||||
console.log(props.initFileSaveTips);
|
||||
innerFileList.value = val.sort((a, b) => {
|
||||
if (a.status === UploadStatus.init && b.status !== UploadStatus.init) {
|
||||
return -1; // "init" 排在前面
|
||||
|
@ -381,4 +383,4 @@
|
|||
justify-content: space-between;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -15,6 +15,7 @@
|
|||
show-full-screen
|
||||
unmount-on-close
|
||||
:mask="false"
|
||||
@loading-detail="setDetailLoading"
|
||||
@loaded="loadedBug"
|
||||
>
|
||||
<template #titleLeft>
|
||||
|
@ -90,113 +91,115 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #default="{ loading }">
|
||||
<div ref="wrapperRef" class="h-full bg-white">
|
||||
<MsSplitBox
|
||||
ref="wrapperRef"
|
||||
expand-direction="right"
|
||||
:max="0.7"
|
||||
:min="0.7"
|
||||
:size="900"
|
||||
:class="{ 'left-bug-detail': activeTab === 'comment' }"
|
||||
>
|
||||
<template #first>
|
||||
<div class="leftWrapper h-full">
|
||||
<div class="header h-[50px]">
|
||||
<MsTab
|
||||
v-model:active-key="activeTab"
|
||||
:content-tab-list="contentTabList"
|
||||
:get-text-func="getTabBadge"
|
||||
class="no-content relative mb-[8px]"
|
||||
/>
|
||||
<div class="tab-pane-container">
|
||||
<BugDetailTab
|
||||
v-if="activeTab === 'detail'"
|
||||
ref="bugDetailTabRef"
|
||||
:form-item="formItem"
|
||||
:allow-edit="hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
|
||||
:detail-info="detailInfo"
|
||||
:is-platform-default-template="isPlatformDefaultTemplate"
|
||||
:platform-system-fields="platformSystemFields"
|
||||
:current-platform="props.currentPlatform"
|
||||
@update-success="updateSuccess"
|
||||
<a-spin :loading="detailLoading" class="w-full">
|
||||
<div ref="wrapperRef" class="h-full bg-white">
|
||||
<MsSplitBox
|
||||
ref="wrapperRef"
|
||||
expand-direction="right"
|
||||
:max="0.7"
|
||||
:min="0.7"
|
||||
:size="900"
|
||||
:class="{ 'left-bug-detail': activeTab === 'comment' }"
|
||||
>
|
||||
<template #first>
|
||||
<div class="leftWrapper h-full">
|
||||
<div class="header h-[50px]">
|
||||
<MsTab
|
||||
v-model:active-key="activeTab"
|
||||
:content-tab-list="contentTabList"
|
||||
:get-text-func="getTabBadge"
|
||||
class="no-content relative mb-[8px]"
|
||||
/>
|
||||
<div class="tab-pane-container">
|
||||
<BugDetailTab
|
||||
v-if="activeTab === 'detail'"
|
||||
ref="bugDetailTabRef"
|
||||
:form-item="formItem"
|
||||
:allow-edit="hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
|
||||
:detail-info="detailInfo"
|
||||
:is-platform-default-template="isPlatformDefaultTemplate"
|
||||
:platform-system-fields="platformSystemFields"
|
||||
:current-platform="props.currentPlatform"
|
||||
@update-success="updateSuccess"
|
||||
/>
|
||||
|
||||
<BugCaseTab
|
||||
v-else-if="activeTab === 'case'"
|
||||
:bug-id="detailInfo.id"
|
||||
@update-case-success="updateSuccess"
|
||||
/>
|
||||
<BugCaseTab
|
||||
v-else-if="activeTab === 'case'"
|
||||
:bug-id="detailInfo.id"
|
||||
@update-case-success="updateSuccess"
|
||||
/>
|
||||
|
||||
<CommentTab v-else-if="activeTab === 'comment'" ref="commentRef" :bug-id="detailInfo.id" />
|
||||
<CommentTab v-else-if="activeTab === 'comment'" ref="commentRef" :bug-id="detailInfo.id" />
|
||||
|
||||
<BugHistoryTab v-else-if="activeTab === 'history'" :bug-id="detailInfo.id" />
|
||||
<BugHistoryTab v-else-if="activeTab === 'history'" :bug-id="detailInfo.id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<a-spin :loading="rightLoading" class="w-full">
|
||||
<!-- 所属平台一致, 详情展示 -->
|
||||
<div v-if="props.currentPlatform === detailInfo.platform" class="rightWrapper p-[24px]">
|
||||
<!-- 自定义字段开始 -->
|
||||
<div class="inline-block w-full break-words">
|
||||
<a-skeleton v-if="loading" class="w-full" :loading="loading" :animation="true">
|
||||
<a-space direction="vertical" class="w-[100%]" size="large">
|
||||
<a-skeleton-line :rows="14" :line-height="30" :line-spacing="30" />
|
||||
</a-space>
|
||||
</a-skeleton>
|
||||
<div v-if="!loading" class="mb-4 font-medium">
|
||||
<strong>
|
||||
{{ t('bugManagement.detail.basicInfo') }}
|
||||
</strong>
|
||||
</div>
|
||||
<MsFormCreate
|
||||
v-if="!loading"
|
||||
ref="formCreateRef"
|
||||
v-model:form-item="formItem"
|
||||
v-model:api="fApi"
|
||||
:form-rule="formRules"
|
||||
class="w-full"
|
||||
:option="options"
|
||||
@change="handelFormCreateChange"
|
||||
/>
|
||||
<!-- 自定义字段结束 -->
|
||||
<div
|
||||
v-if="!isPlatformDefaultTemplate && hasAnyPermission(['PROJECT_BUG:READ+UPDATE']) && !loading"
|
||||
class="baseItem"
|
||||
>
|
||||
<a-form
|
||||
:model="{}"
|
||||
:label-col-props="{
|
||||
span: 9,
|
||||
}"
|
||||
:wrapper-col-props="{
|
||||
span: 15,
|
||||
}"
|
||||
label-align="left"
|
||||
content-class="tags-class"
|
||||
</template>
|
||||
<template #second>
|
||||
<a-spin :loading="rightLoading" class="w-full">
|
||||
<!-- 所属平台一致, 详情展示 -->
|
||||
<div v-if="props.currentPlatform === detailInfo.platform" class="rightWrapper p-[24px]">
|
||||
<!-- 自定义字段开始 -->
|
||||
<div class="inline-block w-full break-words">
|
||||
<a-skeleton v-if="loading" class="w-full" :loading="loading" :animation="true">
|
||||
<a-space direction="vertical" class="w-[100%]" size="large">
|
||||
<a-skeleton-line :rows="14" :line-height="30" :line-spacing="30" />
|
||||
</a-space>
|
||||
</a-skeleton>
|
||||
<div v-if="!loading" class="mb-4 font-medium">
|
||||
<strong>
|
||||
{{ t('bugManagement.detail.basicInfo') }}
|
||||
</strong>
|
||||
</div>
|
||||
<MsFormCreate
|
||||
v-if="!loading"
|
||||
ref="formCreateRef"
|
||||
v-model:form-item="formItem"
|
||||
v-model:api="fApi"
|
||||
:form-rule="formRules"
|
||||
class="w-full"
|
||||
:option="options"
|
||||
@change="handelFormCreateChange"
|
||||
/>
|
||||
<!-- 自定义字段结束 -->
|
||||
<div
|
||||
v-if="!isPlatformDefaultTemplate && hasAnyPermission(['PROJECT_BUG:READ+UPDATE']) && !loading"
|
||||
class="baseItem"
|
||||
>
|
||||
<a-form-item field="tags" :label="t('system.orgTemplate.tags')">
|
||||
<MsTagsInput
|
||||
v-model:model-value="tags"
|
||||
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
|
||||
@blur="changeTag"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-form
|
||||
:model="{}"
|
||||
:label-col-props="{
|
||||
span: 9,
|
||||
}"
|
||||
:wrapper-col-props="{
|
||||
span: 15,
|
||||
}"
|
||||
label-align="left"
|
||||
content-class="tags-class"
|
||||
>
|
||||
<a-form-item field="tags" :label="t('system.orgTemplate.tags')">
|
||||
<MsTagsInput
|
||||
v-model:model-value="tags"
|
||||
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+UPDATE'])"
|
||||
@blur="changeTag"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内置基础信息结束 -->
|
||||
</div>
|
||||
<!-- 所属平台不一致, 详情不展示, 展示空面板 -->
|
||||
<div v-else>
|
||||
<a-empty> {{ $t('messageBox.noContent') }} </a-empty>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
</div>
|
||||
<!-- 内置基础信息结束 -->
|
||||
</div>
|
||||
<!-- 所属平台不一致, 详情不展示, 展示空面板 -->
|
||||
<div v-else>
|
||||
<a-empty> {{ $t('messageBox.noContent') }} </a-empty>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
</div>
|
||||
</a-spin>
|
||||
<CommentInput
|
||||
v-if="activeTab === 'comment' && hasAnyPermission(['PROJECT_BUG:READ+COMMENT'])"
|
||||
:content="commentContent"
|
||||
|
@ -241,7 +244,7 @@
|
|||
followBug,
|
||||
getBugDetail,
|
||||
getTemplateById,
|
||||
} from '@/api/modules/bug-management/index';
|
||||
} from '@/api/modules/bug-management';
|
||||
import { EditorPreviewFileUrl } from '@/api/requrls/bug-management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
|
@ -251,8 +254,7 @@
|
|||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import type { CustomFieldItem } from '@/models/bug-management';
|
||||
import { BugEditCustomField, BugEditFormObject, BugTemplateRequest } from '@/models/bug-management';
|
||||
import { SelectValue } from '@/models/projectManagement/menuManagement';
|
||||
import { BugEditCustomField, BugEditFormObject } from '@/models/bug-management';
|
||||
import { RouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -290,7 +292,7 @@
|
|||
const bugDetailTabRef = ref();
|
||||
const isPlatformDefaultTemplate = ref(false);
|
||||
const rightLoading = ref(false);
|
||||
const rowLength = ref<number>(0);
|
||||
const detailLoading = ref(false);
|
||||
const activeTab = ref<string>('detail');
|
||||
|
||||
const detailInfo = ref<Record<string, any>>({ match: [] }); // 存储当前详情信息,通过loadBug 获取
|
||||
|
@ -332,53 +334,33 @@
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
const currentCustomFields = ref<CustomFieldItem[]>([]);
|
||||
|
||||
const templateChange = async (v: SelectValue, valueObj: BugEditFormObject, request: BugTemplateRequest) => {
|
||||
if (v) {
|
||||
try {
|
||||
const res = await getTemplateById({
|
||||
projectId: appStore.currentProjectId,
|
||||
id: v,
|
||||
fromStatusId: request.fromStatusId,
|
||||
platformBugKey: request.platformBugKey,
|
||||
});
|
||||
platformSystemFields.value = res.customFields.filter((field) => field.platformSystemField);
|
||||
platformSystemFields.value.forEach((item) => {
|
||||
item.defaultValue = valueObj[item.fieldId];
|
||||
});
|
||||
getFormRules(
|
||||
res.customFields.filter((field) => !field.platformSystemField),
|
||||
valueObj
|
||||
);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const getOptionFromTemplate = (field: CustomFieldItem | undefined) => {
|
||||
if (field) {
|
||||
return field.options ? field.options : JSON.parse(field.platformOptionJson);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
async function loadedBug(detail: BugEditFormObject) {
|
||||
detailInfo.value = { ...detail };
|
||||
const { templateId } = detailInfo.value;
|
||||
// 是否平台默认模板
|
||||
isPlatformDefaultTemplate.value = detail.platformDefault;
|
||||
// TAG 赋值
|
||||
tags.value = detail.tags || [];
|
||||
caseCount.value = detailInfo.value.linkCaseCount;
|
||||
const tmpObj = { status: detailInfo.value.status };
|
||||
// 初始化自定义字段
|
||||
// 关闭loading
|
||||
detailLoading.value = false;
|
||||
const customFieldsRes = await getTemplateById({
|
||||
projectId: appStore.currentProjectId,
|
||||
id: templateId,
|
||||
id: detail.templateId,
|
||||
fromStatusId: detail.status,
|
||||
platformBugKey: detail.platformBugId,
|
||||
});
|
||||
// 详情信息, TAG赋值
|
||||
detailInfo.value = { ...detail };
|
||||
tags.value = detail.tags || [];
|
||||
caseCount.value = detailInfo.value.linkCaseCount;
|
||||
const tmpObj = { status: detailInfo.value.status };
|
||||
platformSystemFields.value = customFieldsRes.customFields.filter((field) => field.platformSystemField);
|
||||
currentCustomFields.value = customFieldsRes.customFields || [];
|
||||
if (detailInfo.value.customFields && Array.isArray(detailInfo.value.customFields)) {
|
||||
const MULTIPLE_TYPE = ['MULTIPLE_SELECT', 'MULTIPLE_INPUT', 'CHECKBOX', 'MULTIPLE_MEMBER'];
|
||||
|
@ -392,8 +374,7 @@
|
|||
const optionsIds = (multipleOptions || []).map((e: any) => e.value);
|
||||
if (item.value) {
|
||||
if (item.type !== 'MULTIPLE_INPUT') {
|
||||
const currentDefaultValue = optionsIds.filter((e: any) => JSON.parse(item.value).includes(e));
|
||||
tmpObj[item.id] = currentDefaultValue;
|
||||
tmpObj[item.id] = optionsIds.filter((e: any) => JSON.parse(item.value).includes(e));
|
||||
} else {
|
||||
tmpObj[item.id] = JSON.parse(item.value);
|
||||
}
|
||||
|
@ -411,16 +392,29 @@
|
|||
);
|
||||
// 如果该值在选项中已经被删除掉
|
||||
const optionsIds = (multipleOptions || []).map((e: any) => e.value);
|
||||
const currentDefaultValue = optionsIds.find((e: any) => item.value === e) || '';
|
||||
tmpObj[item.id] = currentDefaultValue;
|
||||
tmpObj[item.id] = optionsIds.find((e: any) => item.value === e) || '';
|
||||
} else {
|
||||
tmpObj[item.id] = item.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 初始化自定义字段
|
||||
await templateChange(templateId, tmpObj, { platformBugKey: detail.platformBugId, fromStatusId: detail.status });
|
||||
platformSystemFields.value.forEach((item) => {
|
||||
item.defaultValue = tmpObj[item.fieldId];
|
||||
});
|
||||
getFormRules(
|
||||
customFieldsRes.customFields.filter((field) => !field.platformSystemField),
|
||||
tmpObj
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情加载中
|
||||
*/
|
||||
function setDetailLoading() {
|
||||
detailLoading.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 tab 的参数数量徽标
|
||||
*/
|
||||
|
@ -443,7 +437,6 @@
|
|||
|
||||
function updateSuccess() {
|
||||
rightLoading.value = false;
|
||||
detailDrawerRef.value?.initDetail();
|
||||
emit('submit');
|
||||
}
|
||||
|
||||
|
@ -774,4 +767,4 @@
|
|||
// width: 100%;
|
||||
// word-wrap: break-word;
|
||||
//}
|
||||
</style>
|
||||
</style>
|
|
@ -84,6 +84,7 @@
|
|||
:upload-func="uploadOrAssociationFile"
|
||||
:handle-delete="deleteFileHandler"
|
||||
:show-delete="props.allowEdit"
|
||||
:init-file-save-tips="t('ms.upload.waiting_save')"
|
||||
@finish="uploadFileOver"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
|
@ -570,4 +571,4 @@
|
|||
:deep(.arco-form-item-label) {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -76,7 +76,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<MsFileList ref="fileListRef" v-model:file-list="fileList" mode="static">
|
||||
<MsFileList
|
||||
ref="fileListRef"
|
||||
v-model:file-list="fileList"
|
||||
:init-file-save-tips="t('ms.upload.waiting_save')"
|
||||
mode="static"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
<!-- 本地文件 -->
|
||||
<div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
|
||||
|
@ -826,4 +831,4 @@
|
|||
font-size: 14px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -35,6 +35,7 @@ export function convertToFileByBug(fileInfo: AssociatedList): MsFileItem {
|
|||
isCopyFlag,
|
||||
associateId: refId,
|
||||
createUserName,
|
||||
createTime,
|
||||
uploadedTime: createTime,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -84,7 +84,13 @@
|
|||
</a-form>
|
||||
<!-- 文件列表开始 -->
|
||||
<div class="w-[90%]">
|
||||
<MsFileList ref="fileListRef" v-model:file-list="fileList" mode="static" :show-upload-type-desc="true">
|
||||
<MsFileList
|
||||
ref="fileListRef"
|
||||
v-model:file-list="fileList"
|
||||
mode="static"
|
||||
:init-file-save-tips="t('ms.upload.waiting_save')"
|
||||
:show-upload-type-desc="true"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
<!-- 本地文件 -->
|
||||
<div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
|
||||
|
@ -283,8 +289,6 @@
|
|||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import type {
|
||||
AssociatedList,
|
||||
|
@ -842,4 +846,4 @@
|
|||
color: var(--color-text-4);
|
||||
background: var(--color-text-n8);
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -54,8 +54,8 @@
|
|||
<a-tooltip position="tl" :content-style="{ maxWidth: '500px' }">
|
||||
<template #content>
|
||||
<div class="flex flex-col">
|
||||
<div>{{ t('project.menu.defect.enableTip') }}</div>
|
||||
<div class="flex flex-nowrap">{{ t('project.menu.defect.closeTip') }}</div>
|
||||
<div>{{ t('project.menu.demand.enableTip') }}</div>
|
||||
<div class="flex flex-nowrap">{{ t('project.menu.demand.closeTip') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
|
@ -216,4 +216,4 @@
|
|||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</script>
|
|
@ -74,5 +74,7 @@ export default {
|
|||
'Turn on: The defects created by the platform are synced to the third-party project management platform',
|
||||
'project.menu.defect.closeTip':
|
||||
'Turn off: The defects created by the platform cannot be synced to the third-party project management platform',
|
||||
'project.menu.demand.enableTip': 'On: functional cases can relate to third-party demands',
|
||||
'project.menu.demand.closeTip': 'Off: functional cases cannot relate to third party demands',
|
||||
'project.menu.defect.customLabel': 'Custom Frequency',
|
||||
};
|
||||
|
|
|
@ -67,6 +67,8 @@ export default {
|
|||
// 同步缺陷
|
||||
'project.menu.defect.enableTip': '开启:平台创建的缺陷同步至第三方项目管理平台',
|
||||
'project.menu.defect.closeTip': '关闭:平台创建的缺陷则无法同步至第三方项目管理平台',
|
||||
'project.menu.demand.enableTip': '开启:平台创建的用例可关联第三方需求',
|
||||
'project.menu.demand.closeTip': '关闭:平台创建的用例无法关联第三方的需求',
|
||||
'project.menu.defect.customLabel': '自定义频率',
|
||||
'project.menu.defect.enableAfterConfig': '配置第三方信息后可开启',
|
||||
// 误报规则
|
||||
|
|
Loading…
Reference in New Issue