refactor(缺陷管理): 缺陷管理创建缺陷创建组件抽离&重构

This commit is contained in:
xinxin.wu 2024-08-22 11:37:34 +08:00 committed by Craftsman
parent 00f4cf449c
commit 01a912b399
5 changed files with 441 additions and 363 deletions

View File

@ -32,7 +32,7 @@ const BugManagement: AppRouteRecordRaw = {
{ {
path: 'detail/:mode?', path: 'detail/:mode?',
name: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL, name: BugManagementRouteEnum.BUG_MANAGEMENT_DETAIL,
component: () => import('@/views/bug-management/edit.vue'), component: () => import('@/views/bug-management/createAndEditBug.vue'),
meta: { meta: {
locale: 'bugManagement.editBug', locale: 'bugManagement.editBug',
roles: ['PROJECT_BUG:READ+ADD', 'PROJECT_BUG:READ+UPDATE'], roles: ['PROJECT_BUG:READ+ADD', 'PROJECT_BUG:READ+UPDATE'],

View File

@ -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>

View File

@ -1,185 +1,161 @@
<template> <template>
<MsCard <a-form ref="formRef" :model="form" layout="vertical">
has-breadcrumb <div class="flex flex-row">
:title="title" <div class="left mt-[16px] w-[calc(100%-428px)] grow">
:loading="loading" <!-- 平台默认模板不展示缺陷名称, 描述 -->
:is-edit="isEdit" <a-form-item
@save="saveHandler(false)" v-if="!isPlatformDefaultTemplate"
@save-and-continue="saveHandler(true)" field="title"
> :label="t('bugManagement.bugName')"
<template v-if="!isEdit" #headerRight> :rules="[{ required: true, message: t('bugManagement.edit.nameIsRequired') }]"
<a-select >
v-model="form.templateId" <a-input v-model="form.title" :placeholder="t('bugManagement.edit.pleaseInputBugName')" :max-length="255" />
class="w-[240px]" </a-form-item>
:options="templateOption" <a-form-item v-if="!isPlatformDefaultTemplate" field="description" :label="t('bugManagement.edit.content')">
allow-search <MsRichText
:placeholder="t('bugManagement.edit.defaultSystemTemplate')" v-model:raw="form.description"
@change="templateChange" v-model:filed-ids="descriptionFileIds"
/> :upload-image="handleUploadImage"
</template> :preview-url="`${EditorPreviewFileUrl}/${appStore.currentProjectId}`"
<a-form ref="formRef" :model="form" layout="vertical"> />
<div class="flex flex-row"> </a-form-item>
<div class="left mt-[16px] w-[calc(100%-428px)] grow"> <!-- 平台默认模板展示字段, 暂时支持输入框, 富文本类型 -->
<!-- 平台默认模板不展示缺陷名称, 描述 --> <div v-if="isPlatformDefaultTemplate">
<a-form-item <a-form-item
v-if="!isPlatformDefaultTemplate" v-for="(value, key) in form.platformSystemFields"
field="title" :key="key"
:label="t('bugManagement.bugName')" :field="'platformSystemFields.' + key"
:rules="[{ required: true, message: t('bugManagement.edit.nameIsRequired') }]" :label="platformSystemFieldMap[key].fieldName"
:rules="[
{
required: platformSystemFieldMap[key].required,
message: `${platformSystemFieldMap[key].fieldName}` + t('bugManagement.edit.cannotBeNull'),
},
]"
> >
<a-input v-model="form.title" :placeholder="t('bugManagement.edit.pleaseInputBugName')" :max-length="255" /> <a-input
</a-form-item> v-if="platformSystemFieldMap[key].type === 'INPUT'"
<a-form-item v-if="!isPlatformDefaultTemplate" field="description" :label="t('bugManagement.edit.content')"> v-model="form.platformSystemFields[key]"
:max-length="255"
/>
<MsRichText <MsRichText
v-model:raw="form.description" v-if="platformSystemFieldMap[key].type === 'RICH_TEXT'"
v-model:filed-ids="descriptionFileIds" v-model:raw="form.platformSystemFields[key]"
v-model:filed-ids="descriptionFileIdMap[key]"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
:preview-url="`${EditorPreviewFileUrl}/${appStore.currentProjectId}`" :preview-url="`${EditorPreviewFileUrl}/${appStore.currentProjectId}`"
/> />
</a-form-item> </a-form-item>
<!-- 平台默认模板展示字段, 暂时支持输入框, 富文本类型 --> </div>
<div v-if="isPlatformDefaultTemplate"> <a-form-item field="attachment">
<a-form-item <div class="flex flex-col">
v-for="(value, key) in form.platformSystemFields" <div class="mb-1">
:key="key" <AddAttachment v-model:file-list="fileList" @change="handleChange" @link-file="associatedFile" />
:field="'platformSystemFields.' + key" </div>
:label="platformSystemFieldMap[key].fieldName" </div>
:rules="[ </a-form-item>
{ <MsFileList
required: platformSystemFieldMap[key].required, ref="fileListRef"
message: `${platformSystemFieldMap[key].fieldName}` + t('bugManagement.edit.cannotBeNull'), v-model:file-list="fileList"
}, :init-file-save-tips="t('ms.upload.waiting_save')"
]" mode="static"
> >
<a-input <template #actions="{ item }">
v-if="platformSystemFieldMap[key].type === 'INPUT'" <!-- 本地文件 -->
v-model="form.platformSystemFields[key]" <div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
:max-length="255" <MsButton
v-if="item.status !== 'init' && item.file.type.includes('image')"
type="button"
status="primary"
class="!mr-[4px]"
@click="handlePreview(item)"
>
{{ t('ms.upload.preview') }}
</MsButton>
<MsButton
v-if="item.status !== 'init'"
type="button"
status="primary"
class="!mr-[4px]"
@click="transferFile(item)"
>
{{ t('caseManagement.featureCase.storage') }}
</MsButton>
<SaveAsFilePopover
v-model:visible="transferVisible"
:saving-file="activeTransferFileParams"
:file-save-as-source-id="(form.id as string)"
:file-save-as-api="transferFileRequest"
:file-module-options-api="getTransferFileTree"
source-id-key="bugId"
@finish="getDetailInfo()"
/> />
<MsRichText <MsButton
v-if="platformSystemFieldMap[key].type === 'RICH_TEXT'" v-if="item.status !== 'init'"
v-model:raw="form.platformSystemFields[key]" type="button"
v-model:filed-ids="descriptionFileIdMap[key]" status="primary"
:upload-image="handleUploadImage" class="!mr-[4px]"
:preview-url="`${EditorPreviewFileUrl}/${appStore.currentProjectId}`" @click="downloadFile(item)"
>
{{ t('common.download') }}
</MsButton>
</div>
<!-- 关联文件 -->
<div v-else class="flex flex-nowrap">
<MsButton
v-if="item.status !== 'init' && item.file.type.includes('image')"
type="button"
status="primary"
class="!mr-[4px]"
@click="handlePreview(item)"
>
{{ t('ms.upload.preview') }}
</MsButton>
<MsButton v-if="bugId" type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
{{ t('common.download') }}
</MsButton>
<MsButton
v-if="bugId && item.isUpdateFlag"
type="button"
status="primary"
@click="handleUpdateFile(item)"
>
{{ t('common.update') }}
</MsButton>
</div>
</template>
<template #title="{ item }">
<span v-if="item.isUpdateFlag" class="ml-4 flex items-center font-normal text-[rgb(var(--warning-6))]"
><icon-exclamation-circle-fill /> <span>{{ t('caseManagement.featureCase.fileIsUpdated') }}</span>
</span>
</template>
</MsFileList>
</div>
<a-divider class="ml-[16px]" direction="vertical" />
<div class="right mt-[16px] w-[428px] grow pr-[24px]">
<div class="min-w-[250px] overflow-auto">
<a-skeleton v-if="isLoading" :loading="isLoading" :animation="true">
<a-space direction="vertical" class="w-full" size="large">
<a-skeleton-line :rows="12" :line-height="30" :line-spacing="30" />
</a-space>
</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" />
</div>
<a-form-item v-if="!isPlatformDefaultTemplate" field="tag" :label="t('bugManagement.tag')">
<MsTagsInput
v-model:model-value="form.tags"
:placeholder="t('bugManagement.edit.tagPlaceholder')"
allow-clear
/> />
</a-form-item> </a-form-item>
</div> </a-form>
<a-form-item field="attachment">
<div class="flex flex-col">
<div class="mb-1">
<AddAttachment v-model:file-list="fileList" @change="handleChange" @link-file="associatedFile" />
</div>
</div>
</a-form-item>
<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">
<MsButton
v-if="item.status !== 'init' && item.file.type.includes('image')"
type="button"
status="primary"
class="!mr-[4px]"
@click="handlePreview(item)"
>
{{ t('ms.upload.preview') }}
</MsButton>
<MsButton
v-if="item.status !== 'init'"
type="button"
status="primary"
class="!mr-[4px]"
@click="transferFile(item)"
>
{{ t('caseManagement.featureCase.storage') }}
</MsButton>
<SaveAsFilePopover
v-model:visible="transferVisible"
:saving-file="activeTransferFileParams"
:file-save-as-source-id="(form.id as string)"
:file-save-as-api="transferFileRequest"
:file-module-options-api="getTransferFileTree"
source-id-key="bugId"
@finish="getDetailInfo()"
/>
<MsButton
v-if="item.status !== 'init'"
type="button"
status="primary"
class="!mr-[4px]"
@click="downloadFile(item)"
>
{{ t('common.download') }}
</MsButton>
</div>
<!-- 关联文件 -->
<div v-else class="flex flex-nowrap">
<MsButton
v-if="item.status !== 'init' && item.file.type.includes('image')"
type="button"
status="primary"
class="!mr-[4px]"
@click="handlePreview(item)"
>
{{ t('ms.upload.preview') }}
</MsButton>
<MsButton v-if="bugId" type="button" status="primary" class="!mr-[4px]" @click="downloadFile(item)">
{{ t('common.download') }}
</MsButton>
<MsButton
v-if="bugId && item.isUpdateFlag"
type="button"
status="primary"
@click="handleUpdateFile(item)"
>
{{ t('common.update') }}
</MsButton>
</div>
</template>
<template #title="{ item }">
<span v-if="item.isUpdateFlag" class="ml-4 flex items-center font-normal text-[rgb(var(--warning-6))]"
><icon-exclamation-circle-fill /> <span>{{ t('caseManagement.featureCase.fileIsUpdated') }}</span>
</span>
</template>
</MsFileList>
</div>
<a-divider class="ml-[16px]" direction="vertical" />
<div class="right mt-[16px] w-[428px] grow pr-[24px]">
<div class="min-w-[250px] overflow-auto">
<a-skeleton v-if="isLoading" :loading="isLoading" :animation="true">
<a-space direction="vertical" class="w-full" size="large">
<a-skeleton-line :rows="12" :line-height="30" :line-spacing="30" />
</a-space>
</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"
/>
</div>
<a-form-item v-if="!isPlatformDefaultTemplate" field="tag" :label="t('bugManagement.tag')">
<MsTagsInput
v-model:model-value="form.tags"
:placeholder="t('bugManagement.edit.tagPlaceholder')"
allow-clear
/>
</a-form-item>
</a-form>
</div>
</div> </div>
</div> </div>
</a-form> </div>
</MsCard> </a-form>
<div> <div>
<MsUpload <MsUpload
v-model:file-list="fileList" v-model:file-list="fileList"
@ -211,7 +187,6 @@
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 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';
@ -225,13 +200,11 @@
import { import {
checkFileIsUpdateRequest, checkFileIsUpdateRequest,
createOrUpdateBug,
downloadFileRequest, downloadFileRequest,
editorUploadFile, editorUploadFile,
getAssociatedFileList, getAssociatedFileList,
getBugDetail, getBugDetail,
getTemplateById, getTemplateById,
getTemplateOption,
previewFile, previewFile,
transferFileRequest, transferFileRequest,
updateFile, updateFile,
@ -240,9 +213,6 @@
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement'; import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
import { EditorPreviewFileUrl } from '@/api/requrls/bug-management'; import { EditorPreviewFileUrl } from '@/api/requrls/bug-management';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
import useVisit from '@/hooks/useVisit';
import router from '@/router';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import { downloadByteFile } from '@/utils'; import { downloadByteFile } from '@/utils';
@ -260,24 +230,29 @@
import { TableQueryParams } from '@/models/common'; import { TableQueryParams } from '@/models/common';
import { SelectValue } from '@/models/projectManagement/menuManagement'; import { SelectValue } from '@/models/projectManagement/menuManagement';
import type { CustomField } from '@/models/setting/template'; import type { CustomField } from '@/models/setting/template';
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';
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' }); defineOptions({ name: 'BugEditPage' });
const { setIsSave } = useLeaveUnSaveTip();
setIsSave(false);
const { t } = useI18n(); const { t } = useI18n();
interface TemplateOption {
label: string;
value: string;
}
const appStore = useAppStore(); const appStore = useAppStore();
const route = useRoute(); const route = useRoute();
const templateOption = ref<TemplateOption[]>([]);
const form = ref<BugEditFormObject>({ const form = ref<BugEditFormObject>({
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
title: '', title: '',
@ -314,7 +289,7 @@
const acceptType = ref('none'); // - const acceptType = ref('none'); // -
const userStore = useUserStore(); const userStore = useUserStore();
const isEdit = computed(() => !!route.query.id && route.params.mode === 'edit'); 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 isEditOrCopy = computed(() => !!bugId.value);
const isCopy = computed(() => route.params.mode === 'copy'); const isCopy = computed(() => route.params.mode === 'copy');
const isPlatformDefaultTemplate = ref(false); const isPlatformDefaultTemplate = ref(false);
@ -324,15 +299,7 @@
const descriptionFileIds = ref<string[]>([]); const descriptionFileIds = ref<string[]>([]);
// -/ID // -/ID
const descriptionFileIdMap = ref<Record<string, string[]>>({}); 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 isLoading = ref<boolean>(true);
const rowLength = ref<number>(0); 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) { async function handlePreview(item: MsFileItem) {
try { try {
@ -568,120 +512,94 @@
return fileIds; return fileIds;
} }
function makeParams() {
//
const customFields: BugEditCustomFieldItem[] = [];
if (formItem.value && formItem.value.length) {
formItem.value.forEach((item: FormRuleItem) => {
if (item.sourceType === 'CASCADER') {
item.value = findParents(item.options as Option[], item.value as string, []) || '';
}
customFields.push({
id: item.field as string,
name: item.title as string,
type: item.sourceType as string,
value: Array.isArray(item.value) ? JSON.stringify(item.value) : (item.value as string),
});
});
}
if (isPlatformDefaultTemplate.value && form.value.platformSystemFields) {
Object.keys(form.value.platformSystemFields).forEach((key) => {
customFields.push({
id: platformSystemFieldMap[key].fieldId,
name: platformSystemFieldMap[key].fieldName,
type: platformSystemFieldMap[key].type,
value: form.value.platformSystemFields[key],
});
});
// delete form.value.platformSystemFields;
// , ,
delete form.value.title;
delete form.value.description;
delete form.value.tags;
}
//
const copyFileList = fileList.value.filter((item) => item.isCopyFlag);
let copyFiles: { refId: string; fileId: string; local: boolean }[] = [];
if (copyFileList.length > 0) {
copyFiles = copyFileList.map((file) => {
return {
refId: file.associateId,
fileId: file.uid,
local: file.local,
bugId: bugId.value as string,
fileName: file.name,
};
});
}
const tmpObj: BugEditFormObject = {
...form.value,
customFields,
copyFiles,
richTextTmpFileIds: isPlatformDefaultTemplate.value ? getDescriptionFileId() : descriptionFileIds.value,
};
if (isCopy.value) {
delete tmpObj.id;
delete tmpObj.richTextTmpFileIds;
}
//
const localFiles = fileList.value.filter((item) => item.local && item.status === 'init');
return {
request: tmpObj,
fileList: localFiles as unknown as File[],
};
}
async function resetForm() {
//
const { templateId } = form.value;
//
form.value = {
projectId: appStore.currentProjectId, // id
title: '',
description: '',
templateId,
tags: [],
platformSystemFields: {},
};
await templateChange(templateId);
//
fileList.value = [];
}
// //
const saveHandler = async (isContinue = false) => { const saveHandler = async (isContinue = false) => {
formRef.value.validate((error: any) => { formRef.value.validate((error: any) => {
if (!error) { if (!error) {
fApi.value.validate(async (valid: any) => { fApi.value.validate(async (valid: any) => {
if (valid === true) { if (valid === true) {
try { emit('saveParams', isContinue, makeParams());
loading.value = true;
const customFields: BugEditCustomFieldItem[] = [];
if (formItem.value && formItem.value.length) {
formItem.value.forEach((item: FormRuleItem) => {
if (item.sourceType === 'CASCADER') {
item.value = findParents(item.options as Option[], item.value as string, []) || '';
}
customFields.push({
id: item.field as string,
name: item.title as string,
type: item.sourceType as string,
value: Array.isArray(item.value) ? JSON.stringify(item.value) : (item.value as string),
});
});
}
if (isPlatformDefaultTemplate.value && form.value.platformSystemFields) {
Object.keys(form.value.platformSystemFields).forEach((key) => {
customFields.push({
id: platformSystemFieldMap[key].fieldId,
name: platformSystemFieldMap[key].fieldName,
type: platformSystemFieldMap[key].type,
value: form.value.platformSystemFields[key],
});
});
// delete form.value.platformSystemFields;
// , ,
delete form.value.title;
delete form.value.description;
delete form.value.tags;
}
//
const copyFileList = fileList.value.filter((item) => item.isCopyFlag);
let copyFiles: { refId: string; fileId: string; local: boolean }[] = [];
if (copyFileList.length > 0) {
copyFiles = copyFileList.map((file) => {
return {
refId: file.associateId,
fileId: file.uid,
local: file.local,
bugId: bugId.value as string,
fileName: file.name,
};
});
}
const tmpObj: BugEditFormObject = {
...form.value,
customFields,
copyFiles,
richTextTmpFileIds: isPlatformDefaultTemplate.value ? getDescriptionFileId() : descriptionFileIds.value,
};
if (isCopy.value) {
delete tmpObj.id;
delete tmpObj.richTextTmpFileIds;
}
//
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);
//
const { templateId } = form.value;
//
form.value = {
projectId: appStore.currentProjectId, // id
title: '',
description: '',
templateId,
tags: [],
platformSystemFields: {},
};
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;
}
} }
}); });
} }
@ -812,10 +730,6 @@
loading.value = false; loading.value = false;
}; };
const initDefaultFields = async () => {
await getTemplateOptions();
};
// formCreate // formCreate
watch( watch(
() => formRules.value, () => formRules.value,
@ -846,12 +760,29 @@
return data; return data;
} }
onMounted(async () => { watch(
await initDefaultFields(); () => innerTemplateId.value,
if (isEditOrCopy.value) { (val) => {
// if (val) {
await getDetailInfo(); form.value.templateId = val;
templateChange(val);
}
},
{
immediate: true,
} }
);
onMounted(() => {
if (isEditOrCopy.value) {
//
getDetailInfo();
}
});
defineExpose({
saveHandler,
resetForm,
}); });
</script> </script>

View File

@ -18,7 +18,8 @@
@loaded="loadedCase" @loaded="loadedCase"
> >
<template #titleName> <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 <a-input
v-if="isEditTitle" v-if="isEditTitle"
v-model="titleName" v-model="titleName"
@ -30,7 +31,7 @@
@keydown.enter="handleEditName" @keydown.enter="handleEditName"
/> />
<div v-else class="flex items-center"> <div v-else class="flex items-center">
<div> {{ detailInfo?.num }} </div> <div> [ {{ detailInfo?.num }} ] </div>
<div <div
:class="`${ :class="`${
hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) ? 'hover-title-name' : '' hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) ? 'hover-title-name' : ''
@ -41,9 +42,6 @@
</div> </div>
</div> </div>
</template> </template>
<template #titleLeft>
<div v-if="!isEditTitle" class="flex items-center"><caseLevel :case-level="caseLevels" /></div>
</template>
<template #titleRight="{ loading }"> <template #titleRight="{ loading }">
<div class="rightButtons flex items-center"> <div class="rightButtons flex items-center">
<MsButton <MsButton

View File

@ -1,7 +1,23 @@
<template> <template>
<div> <div>
<div class="flex items-center justify-between"> <div class="mb-[16px] flex items-center justify-between">
<div v-if="showType === 'link'" class="flex"> <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"> <a-tooltip v-if="!total">
<template #content> <template #content>
{{ t('caseManagement.featureCase.noAssociatedDefect') }} {{ t('caseManagement.featureCase.noAssociatedDefect') }}
@ -32,29 +48,6 @@
>{{ t('testPlan.featureCase.noBugDataNewBug') }} >{{ t('testPlan.featureCase.noBugDataNewBug') }}
</a-button> </a-button>
</div> </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> </div>
<BugList <BugList
v-if="showType === 'link'" v-if="showType === 'link'"