refactor(缺陷管理): 缺陷管理创建缺陷创建组件抽离&重构
This commit is contained in:
parent
00f4cf449c
commit
01a912b399
|
@ -32,7 +32,7 @@ const BugManagement: AppRouteRecordRaw = {
|
|||
{
|
||||
path: 'detail/:mode?',
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
|
||||
component: () => import('@/views/bug-management/edit.vue'),
|
||||
component: () => import('@/views/bug-management/createAndEditBug.vue'),
|
||||
meta: {
|
||||
locale: 'bugManagement.editBug',
|
||||
roles: ['PROJECT_BUG:READ+ADD', 'PROJECT_BUG:READ+UPDATE'],
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
<template>
|
||||
<MsCard
|
||||
has-breadcrumb
|
||||
:title="title"
|
||||
:loading="loading"
|
||||
:is-edit="isEdit"
|
||||
@save="saveHandler(false)"
|
||||
@save-and-continue="saveHandler(true)"
|
||||
>
|
||||
<template v-if="!isEdit" #headerRight>
|
||||
<a-select
|
||||
v-model="bugTemplateId"
|
||||
class="w-[240px]"
|
||||
:options="templateOption"
|
||||
allow-search
|
||||
:placeholder="t('bugManagement.edit.defaultSystemTemplate')"
|
||||
/>
|
||||
</template>
|
||||
<BugDetail ref="bugDetailRef" v-model:template-id="bugTemplateId" :bug-id="bugId" @save-params="saveParams" />
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import BugDetail from './edit.vue';
|
||||
|
||||
import { createOrUpdateBug, getTemplateOption } from '@/api/modules/bug-management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import router from '@/router';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import { BugEditFormObject } from '@/models/bug-management';
|
||||
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
defineOptions({ name: 'BugEditPage' });
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
setIsSave(false);
|
||||
|
||||
const { t } = useI18n();
|
||||
interface TemplateOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const appStore = useAppStore();
|
||||
const route = useRoute();
|
||||
const templateOption = ref<TemplateOption[]>([]);
|
||||
const bugTemplateId = ref<string>('');
|
||||
const loading = ref(false);
|
||||
const isEdit = computed(() => !!route.query.id && route.params.mode === 'edit');
|
||||
const bugId = computed(() => route.query.id as string | undefined);
|
||||
|
||||
const isCopy = computed(() => route.params.mode === 'copy');
|
||||
|
||||
const visitedKey = 'doNotNextTipCreateBug';
|
||||
|
||||
const { getIsVisited } = useVisit(visitedKey);
|
||||
|
||||
const title = computed(() => {
|
||||
if (isCopy.value) {
|
||||
return t('bugManagement.copyBug');
|
||||
}
|
||||
return isEdit.value ? t('bugManagement.editBug') : t('bugManagement.createBug');
|
||||
});
|
||||
|
||||
const getTemplateOptions = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getTemplateOption(appStore.currentProjectId);
|
||||
templateOption.value = res.map((item) => {
|
||||
if (item.enableDefault && !isEdit.value) {
|
||||
// 选中默认模板
|
||||
bugTemplateId.value = item.id;
|
||||
}
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const bugDetailRef = ref<InstanceType<typeof BugDetail>>();
|
||||
|
||||
async function saveParams(isContinue: boolean, params: { request: BugEditFormObject; fileList: File[] }) {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await createOrUpdateBug(params);
|
||||
if (isEdit.value) {
|
||||
setIsSave(true);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
});
|
||||
} else {
|
||||
Message.success(t('common.createSuccess'));
|
||||
if (isContinue) {
|
||||
setIsSave(false);
|
||||
bugDetailRef.value?.resetForm();
|
||||
} else {
|
||||
setIsSave(true);
|
||||
// 跳转到成功页
|
||||
if (getIsVisited()) {
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
|
||||
query: {
|
||||
...route.query,
|
||||
id: res.data.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const saveHandler = async (isContinue = false) => {
|
||||
bugDetailRef.value?.saveHandler(isContinue);
|
||||
};
|
||||
|
||||
const initDefaultFields = async () => {
|
||||
await getTemplateOptions();
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
initDefaultFields();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-form-item-extra) {
|
||||
font-size: 14px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
:deep(.arco-form-item-content) {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
|
@ -1,22 +1,4 @@
|
|||
<template>
|
||||
<MsCard
|
||||
has-breadcrumb
|
||||
:title="title"
|
||||
:loading="loading"
|
||||
:is-edit="isEdit"
|
||||
@save="saveHandler(false)"
|
||||
@save-and-continue="saveHandler(true)"
|
||||
>
|
||||
<template v-if="!isEdit" #headerRight>
|
||||
<a-select
|
||||
v-model="form.templateId"
|
||||
class="w-[240px]"
|
||||
:options="templateOption"
|
||||
allow-search
|
||||
:placeholder="t('bugManagement.edit.defaultSystemTemplate')"
|
||||
@change="templateChange"
|
||||
/>
|
||||
</template>
|
||||
<a-form ref="formRef" :model="form" layout="vertical">
|
||||
<div class="flex flex-row">
|
||||
<div class="left mt-[16px] w-[calc(100%-428px)] grow">
|
||||
|
@ -159,12 +141,7 @@
|
|||
</a-skeleton>
|
||||
<a-form v-else :model="form" layout="vertical">
|
||||
<div style="display: inline-block; width: 100%; word-wrap: break-word">
|
||||
<MsFormCreate
|
||||
ref="formCreateRef"
|
||||
v-model:formItem="formItem"
|
||||
v-model:api="fApi"
|
||||
:form-rule="formRules"
|
||||
/>
|
||||
<MsFormCreate ref="formCreateRef" v-model:formItem="formItem" v-model:api="fApi" :form-rule="formRules" />
|
||||
</div>
|
||||
|
||||
<a-form-item v-if="!isPlatformDefaultTemplate" field="tag" :label="t('bugManagement.tag')">
|
||||
|
@ -179,7 +156,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</MsCard>
|
||||
<div>
|
||||
<MsUpload
|
||||
v-model:file-list="fileList"
|
||||
|
@ -211,7 +187,6 @@
|
|||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
|
||||
import { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
|
@ -225,13 +200,11 @@
|
|||
|
||||
import {
|
||||
checkFileIsUpdateRequest,
|
||||
createOrUpdateBug,
|
||||
downloadFileRequest,
|
||||
editorUploadFile,
|
||||
getAssociatedFileList,
|
||||
getBugDetail,
|
||||
getTemplateById,
|
||||
getTemplateOption,
|
||||
previewFile,
|
||||
transferFileRequest,
|
||||
updateFile,
|
||||
|
@ -240,9 +213,6 @@
|
|||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||
import { EditorPreviewFileUrl } from '@/api/requrls/bug-management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import router from '@/router';
|
||||
import { useAppStore } from '@/store';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { downloadByteFile } from '@/utils';
|
||||
|
@ -260,24 +230,29 @@
|
|||
import { TableQueryParams } from '@/models/common';
|
||||
import { SelectValue } from '@/models/projectManagement/menuManagement';
|
||||
import type { CustomField } from '@/models/setting/template';
|
||||
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { convertToFile } from '../case-management/caseManagementFeature/components/utils';
|
||||
import { convertToFileByBug } from './utils';
|
||||
|
||||
const props = defineProps<{
|
||||
bugId?: string;
|
||||
templateId: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'saveParams', isContinue: boolean, params: { request: BugEditFormObject; fileList: File[] }): void;
|
||||
}>();
|
||||
|
||||
const innerTemplateId = defineModel<string>('templateId', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
defineOptions({ name: 'BugEditPage' });
|
||||
const { setIsSave } = useLeaveUnSaveTip();
|
||||
setIsSave(false);
|
||||
|
||||
const { t } = useI18n();
|
||||
interface TemplateOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const appStore = useAppStore();
|
||||
const route = useRoute();
|
||||
const templateOption = ref<TemplateOption[]>([]);
|
||||
const form = ref<BugEditFormObject>({
|
||||
projectId: appStore.currentProjectId,
|
||||
title: '',
|
||||
|
@ -314,7 +289,7 @@
|
|||
const acceptType = ref('none'); // 模块-上传文件类型
|
||||
const userStore = useUserStore();
|
||||
const isEdit = computed(() => !!route.query.id && route.params.mode === 'edit');
|
||||
const bugId = computed(() => route.query.id || '');
|
||||
const bugId = ref<string | undefined>(props.bugId);
|
||||
const isEditOrCopy = computed(() => !!bugId.value);
|
||||
const isCopy = computed(() => route.params.mode === 'copy');
|
||||
const isPlatformDefaultTemplate = ref(false);
|
||||
|
@ -324,15 +299,7 @@
|
|||
const descriptionFileIds = ref<string[]>([]);
|
||||
// 描述-环境/富文本临时附件ID
|
||||
const descriptionFileIdMap = ref<Record<string, string[]>>({});
|
||||
const visitedKey = 'doNotNextTipCreateBug';
|
||||
const { getIsVisited } = useVisit(visitedKey);
|
||||
|
||||
const title = computed(() => {
|
||||
if (isCopy.value) {
|
||||
return t('bugManagement.copyBug');
|
||||
}
|
||||
return isEdit.value ? t('bugManagement.editBug') : t('bugManagement.createBug');
|
||||
});
|
||||
const isLoading = ref<boolean>(true);
|
||||
const rowLength = ref<number>(0);
|
||||
|
||||
|
@ -462,29 +429,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
const getTemplateOptions = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getTemplateOption(appStore.currentProjectId);
|
||||
templateOption.value = res.map((item) => {
|
||||
if (item.enableDefault && !isEdit.value) {
|
||||
// 当创建时 选中默认模板
|
||||
form.value.templateId = item.id;
|
||||
templateChange(item.id);
|
||||
}
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 预览图片
|
||||
async function handlePreview(item: MsFileItem) {
|
||||
try {
|
||||
|
@ -568,14 +512,8 @@
|
|||
return fileIds;
|
||||
}
|
||||
|
||||
// 保存
|
||||
const saveHandler = async (isContinue = false) => {
|
||||
formRef.value.validate((error: any) => {
|
||||
if (!error) {
|
||||
fApi.value.validate(async (valid: any) => {
|
||||
if (valid === true) {
|
||||
try {
|
||||
loading.value = true;
|
||||
function makeParams() {
|
||||
// 自定义字段参数
|
||||
const customFields: BugEditCustomFieldItem[] = [];
|
||||
if (formItem.value && formItem.value.length) {
|
||||
formItem.value.forEach((item: FormRuleItem) => {
|
||||
|
@ -619,7 +557,6 @@
|
|||
};
|
||||
});
|
||||
}
|
||||
|
||||
const tmpObj: BugEditFormObject = {
|
||||
...form.value,
|
||||
customFields,
|
||||
|
@ -632,18 +569,14 @@
|
|||
}
|
||||
// 过滤出本地保存的文件
|
||||
const localFiles = fileList.value.filter((item) => item.local && item.status === 'init');
|
||||
// 执行保存操作
|
||||
const res = await createOrUpdateBug({ request: tmpObj, fileList: localFiles as unknown as File[] });
|
||||
if (isEdit.value) {
|
||||
setIsSave(true);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
});
|
||||
} else {
|
||||
Message.success(t('common.createSuccess'));
|
||||
if (isContinue) {
|
||||
setIsSave(false);
|
||||
|
||||
return {
|
||||
request: tmpObj,
|
||||
fileList: localFiles as unknown as File[],
|
||||
};
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
// 如果是保存并继续创建
|
||||
const { templateId } = form.value;
|
||||
// 用当前模板初始化自定义字段
|
||||
|
@ -658,30 +591,15 @@
|
|||
await templateChange(templateId);
|
||||
// 清空文件列表
|
||||
fileList.value = [];
|
||||
} else {
|
||||
setIsSave(true);
|
||||
// 否则跳转到成功页
|
||||
if (getIsVisited()) {
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
|
||||
query: {
|
||||
...route.query,
|
||||
id: res.data.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 保存
|
||||
const saveHandler = async (isContinue = false) => {
|
||||
formRef.value.validate((error: any) => {
|
||||
if (!error) {
|
||||
fApi.value.validate(async (valid: any) => {
|
||||
if (valid === true) {
|
||||
emit('saveParams', isContinue, makeParams());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -812,10 +730,6 @@
|
|||
loading.value = false;
|
||||
};
|
||||
|
||||
const initDefaultFields = async () => {
|
||||
await getTemplateOptions();
|
||||
};
|
||||
|
||||
// 监视自定义字段改变处理formCreate
|
||||
watch(
|
||||
() => formRules.value,
|
||||
|
@ -846,12 +760,29 @@
|
|||
return data;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await initDefaultFields();
|
||||
if (isEditOrCopy.value) {
|
||||
// 详情
|
||||
await getDetailInfo();
|
||||
watch(
|
||||
() => innerTemplateId.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
form.value.templateId = val;
|
||||
templateChange(val);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (isEditOrCopy.value) {
|
||||
// 获取详情
|
||||
getDetailInfo();
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
saveHandler,
|
||||
resetForm,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
@loaded="loadedCase"
|
||||
>
|
||||
<template #titleName>
|
||||
<div :class="`case-title flex items-center ${isEditTitle ? 'w-full' : ''}`">
|
||||
<div :class="`case-title flex items-center gap-[8px] ${isEditTitle ? 'w-full' : ''}`">
|
||||
<div v-if="!isEditTitle" class="flex items-center"><caseLevel :case-level="caseLevels" /></div>
|
||||
<a-input
|
||||
v-if="isEditTitle"
|
||||
v-model="titleName"
|
||||
|
@ -30,7 +31,7 @@
|
|||
@keydown.enter="handleEditName"
|
||||
/>
|
||||
<div v-else class="flex items-center">
|
||||
<div> 【{{ detailInfo?.num }}】 </div>
|
||||
<div> [ {{ detailInfo?.num }} ] </div>
|
||||
<div
|
||||
:class="`${
|
||||
hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) ? 'hover-title-name' : ''
|
||||
|
@ -41,9 +42,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #titleLeft>
|
||||
<div v-if="!isEditTitle" class="flex items-center"><caseLevel :case-level="caseLevels" /></div>
|
||||
</template>
|
||||
<template #titleRight="{ loading }">
|
||||
<div class="rightButtons flex items-center">
|
||||
<MsButton
|
||||
|
|
|
@ -1,7 +1,23 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div v-if="showType === 'link'" class="flex">
|
||||
<div class="mb-[16px] flex items-center justify-between">
|
||||
<div>
|
||||
<a-radio-group v-model:model-value="showType" type="button" size="medium">
|
||||
<a-radio value="link">{{ t('caseManagement.featureCase.directLink') }}</a-radio>
|
||||
<a-radio value="testPlan">{{ t('caseManagement.featureCase.testPlan') }}</a-radio>
|
||||
</a-radio-group>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByName')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
@search="getFetch"
|
||||
@press-enter="getFetch"
|
||||
@clear="resetFetch"
|
||||
@input="changeHandler"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<div v-if="showType === 'link'" class="flex items-center">
|
||||
<a-tooltip v-if="!total">
|
||||
<template #content>
|
||||
{{ t('caseManagement.featureCase.noAssociatedDefect') }}
|
||||
|
@ -32,29 +48,6 @@
|
|||
>{{ t('testPlan.featureCase.noBugDataNewBug') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-else v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="font-medium">{{
|
||||
t('caseManagement.featureCase.testPlanLinkList')
|
||||
}}</div>
|
||||
<div class="mb-4">
|
||||
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type ml-[4px]">
|
||||
<a-radio value="link" class="show-type-icon p-[2px]">{{
|
||||
t('caseManagement.featureCase.directLink')
|
||||
}}</a-radio>
|
||||
<a-radio value="testPlan" class="show-type-icon p-[2px]">{{
|
||||
t('caseManagement.featureCase.testPlan')
|
||||
}}</a-radio>
|
||||
</a-radio-group>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('caseManagement.featureCase.searchByName')"
|
||||
allow-clear
|
||||
class="mx-[8px] w-[240px]"
|
||||
@search="getFetch"
|
||||
@press-enter="getFetch"
|
||||
@clear="resetFetch"
|
||||
@input="changeHandler"
|
||||
></a-input-search>
|
||||
</div>
|
||||
</div>
|
||||
<BugList
|
||||
v-if="showType === 'link'"
|
||||
|
|
Loading…
Reference in New Issue