feat(缺陷管理): 详情保存

This commit is contained in:
RubyLiu 2024-02-06 14:49:32 +08:00 committed by Craftsman
parent 1d71ba51d2
commit e3730b3bba
3 changed files with 87 additions and 81 deletions

View File

@ -88,7 +88,13 @@
<template #title> <template #title>
{{ t('bugManagement.detail.detail') }} {{ t('bugManagement.detail.detail') }}
</template> </template>
<BugDetailTab :allow-edit="true" :detail-info="detailInfo" @update-success="updateSuccess" /> <BugDetailTab
ref="bugDetailTabRef"
:form-item="formItem"
:allow-edit="true"
:detail-info="detailInfo"
@update-success="updateSuccess"
/>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="case"> <a-tab-pane key="case">
<template #title> <template #title>
@ -118,7 +124,7 @@
:form-rule="formRules" :form-rule="formRules"
class="w-full" class="w-full"
:option="options" :option="options"
@change="handleOK" @change="handelFormCreateChange"
/> />
<!-- 自定义字段结束 --> <!-- 自定义字段结束 -->
<div class="baseItem"> <div class="baseItem">
@ -154,6 +160,7 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
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';
@ -175,7 +182,6 @@
followBug, followBug,
getBugDetail, getBugDetail,
getTemplateById, getTemplateById,
updateBug,
} from '@/api/modules/bug-management/index'; } from '@/api/modules/bug-management/index';
import useFullScreen from '@/hooks/useFullScreen'; import useFullScreen from '@/hooks/useFullScreen';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
@ -212,6 +218,7 @@
const formItem = ref<FormRuleItem[]>([]); // const formItem = ref<FormRuleItem[]>([]); //
const currentProjectId = computed(() => appStore.currentProjectId); const currentProjectId = computed(() => appStore.currentProjectId);
const showDrawerVisible = defineModel<boolean>('visible', { default: false }); const showDrawerVisible = defineModel<boolean>('visible', { default: false });
const bugDetailTabRef = ref();
const activeTab = ref<string | number>('detail'); const activeTab = ref<string | number>('detail');
@ -219,7 +226,7 @@
const tags = ref([]); const tags = ref([]);
// //
const getFormRules = (arr: BugEditCustomField[]) => { const getFormRules = (arr: BugEditCustomField[], valueObj: BugEditFormObject) => {
formRules.value = []; formRules.value = [];
if (Array.isArray(arr) && arr.length) { if (Array.isArray(arr) && arr.length) {
formRules.value = arr.map((item) => { formRules.value = arr.map((item) => {
@ -227,11 +234,11 @@
type: item.type, type: item.type,
name: item.fieldId, name: item.fieldId,
label: item.fieldName, label: item.fieldName,
value: item.value, value: valueObj[item.fieldId],
options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : item.options, options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : item.options,
required: item.required as boolean, required: item.required as boolean,
props: { props: {
modelValue: item.value, modelValue: valueObj[item.fieldId],
options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : item.options, options: item.platformOptionJson ? JSON.parse(item.platformOptionJson) : item.options,
}, },
}; };
@ -239,11 +246,11 @@
} }
}; };
const templateChange = async (v: SelectValue) => { const templateChange = async (v: SelectValue, valueObj: BugEditFormObject) => {
if (v) { if (v) {
try { try {
const res = await getTemplateById({ projectId: appStore.currentProjectId, id: v }); const res = await getTemplateById({ projectId: appStore.currentProjectId, id: v });
getFormRules(res.customFields); getFormRules(res.customFields, valueObj);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -255,16 +262,14 @@
const { templateId } = detail; const { templateId } = detail;
// tag // tag
tags.value = detail.tags || []; tags.value = detail.tags || [];
//
await templateChange(templateId);
const tmpObj = {}; const tmpObj = {};
if (detail.customFields && Array.isArray(detail.customFields)) { if (detail.customFields && Array.isArray(detail.customFields)) {
detail.customFields.forEach((item) => { detail.customFields.forEach((item) => {
tmpObj[item.id] = item.value; tmpObj[item.id] = item.value;
}); });
} }
// //
fApi.value.setValue(tmpObj); await templateChange(templateId, tmpObj);
} }
const editLoading = ref<boolean>(false); const editLoading = ref<boolean>(false);
@ -333,24 +338,9 @@
}); });
} }
const handleOK = async () => { const handelFormCreateChange = debounce(() => {
const values = await fApi.value.validate(); bugDetailTabRef.value?.handleSave();
if (values) { }, 300);
const params = {
id: detailInfo.value.id,
projectId: currentProjectId.value,
...values,
};
try {
await updateBug(params);
Message.success(t('common.editSuccess'));
detailDrawerRef.value?.initDetail();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
};
// //
const options = { const options = {

View File

@ -1,36 +1,36 @@
<template> <template>
<div class="p-[16px]"> <div class="relative p-[16px] pb-[16px]">
<div class="header"> <div class="header">
<div class="header-title">{{ t('bugManagement.edit.content') }}</div> <div class="header-title">{{ t('bugManagement.edit.content') }}</div>
<div v-if="!contentEditAble" v-permission="['PROJECT_BUG:READ+UPDATE']" class="header-action"> <div v-permission="['PROJECT_BUG:READ+UPDATE']" class="header-action">
<a-button type="text" @click="contentEditAble = true"> <a-button type="text" @click="contentEditAble = !contentEditAble">
<template #icon> <MsIconfont type="icon-icon_edit_outlined" /> </template> <template #icon> <MsIconfont type="icon-icon_edit_outlined" /> </template>
{{ t('bugManagement.edit.contentEdit') }} {{ t('bugManagement.edit.contentEdit') }}
</a-button> </a-button>
</div> </div>
</div> </div>
<div class="mt-[8]" :class="{ 'max-h-[260px]': contentEditAble }"> <div class="mt-[16px]" :class="{ 'max-h-[260px]': contentEditAble }">
<MsRichText <MsRichText
v-if="contentEditAble" v-if="contentEditAble"
v-model:raw="form.content" v-model:raw="form.description"
v-model:filed-ids="fileIds" v-model:filed-ids="fileIds"
:disabled="!contentEditAble" :disabled="!contentEditAble"
:placeholder="t('bugManagement.edit.contentPlaceholder')" :placeholder="t('bugManagement.edit.contentPlaceholder')"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
/> />
<div v-else v-dompurify-html="form?.content || '-'" class="text-[var(--color-text-3)]"></div> <div v-else v-dompurify-html="form?.description || '-'" class="text-[var(--color-text-3)]"></div>
</div> </div>
<div v-if="contentEditAble" class="flex justify-end"> <div v-if="contentEditAble" class="mt-[8px] flex justify-end">
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button> <a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button>
<a-button class="ml-[12px]" type="primary" :loading="confirmLoading"> <a-button class="ml-[12px]" type="primary" :loading="confirmLoading" @click="handleSave">
{{ t('common.save') }} {{ t('common.save') }}
</a-button></div </a-button></div
> >
<div v-if="props.allowEdit"> <div v-if="props.allowEdit">
<div class="font-medium text-[var(--color-text-1)]"> <div class="mt-[16px] font-medium text-[var(--color-text-1)]">
{{ t('bugManagement.edit.file') }} {{ t('bugManagement.edit.file') }}
</div> </div>
<div class="mb-1"> <div class="mt-[16px] pb-[4px]">
<a-dropdown position="tr" trigger="hover"> <a-dropdown position="tr" trigger="hover">
<a-button v-permission="['PROJECT_BUG:READ+UPDATE']" type="outline"> <a-button v-permission="['PROJECT_BUG:READ+UPDATE']" type="outline">
<template #icon> <icon-plus class="text-[14px]" /> </template <template #icon> <icon-plus class="text-[14px]" /> </template
@ -162,6 +162,7 @@
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 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';
@ -171,6 +172,7 @@
import { import {
checkFileIsUpdateRequest, checkFileIsUpdateRequest,
createOrUpdateBug,
deleteFileOrCancelAssociation, deleteFileOrCancelAssociation,
downloadFileRequest, downloadFileRequest,
editorUploadFile, editorUploadFile,
@ -180,14 +182,12 @@
updateFile, updateFile,
uploadOrAssociationFile, uploadOrAssociationFile,
} from '@/api/modules/bug-management'; } from '@/api/modules/bug-management';
import { updateCaseRequest } from '@/api/modules/case-management/featureCase';
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 { scrollIntoView } from '@/utils/dom';
import { 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';
@ -197,6 +197,7 @@
const props = defineProps<{ const props = defineProps<{
detailInfo: BugEditFormObject; detailInfo: BugEditFormObject;
formItem: FormRuleItem[];
allowEdit?: boolean; // allowEdit?: boolean; //
}>(); }>();
@ -224,7 +225,7 @@
}, },
}); });
const form = ref({ const form = ref({
content: '', description: '',
deleteLocalFileIds: [] as string[], deleteLocalFileIds: [] as string[],
unLinkRefIds: [] as string[], unLinkRefIds: [] as string[],
linkFileIds: [] as string[], linkFileIds: [] as string[],
@ -251,36 +252,17 @@
} }
}; };
const confirmLoading = ref<boolean>(false);
const initCurrentDetail = async (detail: BugEditFormObject) => {
const { attachments, description } = detail;
form.value.description = description;
handleFileFunc(attachments);
};
function handleCancel() { function handleCancel() {
contentEditAble.value = false; contentEditAble.value = false;
} }
const confirmLoading = ref<boolean>(false);
// function handleOK() {
// caseFormRef.value?.validate().then(async (res: any) => {
// if (!res) {
// try {
// confirmLoading.value = true;
// await updateCaseRequest();
// Message.success(t('caseManagement.featureCase.editSuccess'));
// handleCancel();
// emit('updateSuccess');
// } catch (error) {
// // eslint-disable-next-line no-console
// console.log(error);
// } finally {
// confirmLoading.value = false;
// }
// }
// return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
// });
// }
const initCurrentDetail = async (detail: BugEditFormObject) => {
const { attachments, content } = detail;
form.value.content = content;
handleFileFunc(attachments);
};
// //
async function deleteFileHandler(item: MsFileItem) { async function deleteFileHandler(item: MsFileItem) {
@ -311,16 +293,6 @@
return Promise.resolve(true); return Promise.resolve(true);
} }
function handleChange(_fileList: MsFileItem[]) {
fileList.value = _fileList.map((e) => {
return {
...e,
enable: true, //
local: true, //
};
});
}
// //
async function handlePreview(item: MsFileItem) { async function handlePreview(item: MsFileItem) {
try { try {
@ -471,10 +443,50 @@
}, },
{ deep: true } { deep: true }
); );
//
async function handleSave() {
try {
confirmLoading.value = true;
const { formItem } = props;
const customFields: BugEditCustomFieldItem[] = [];
if (formItem && formItem.length) {
formItem.forEach((item: FormRuleItem) => {
customFields.push({
id: item.field as string,
name: item.title as string,
type: item.sourceType as string,
value: item.value as string,
});
});
}
const tmpObj: BugEditFormObject = {
...form.value,
id: props.detailInfo.id,
projectId: currentProjectId.value,
templateId: props.detailInfo.templateId,
customFields,
};
//
const res = await createOrUpdateBug({ request: tmpObj, fileList: fileList.value as unknown as File[] });
if (res) {
Message.success(t('common.updateSuccess'));
handleCancel();
emit('updateSuccess');
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
confirmLoading.value = false;
}
}
watchEffect(() => { watchEffect(() => {
initCurrentDetail(props.detailInfo); initCurrentDetail(props.detailInfo);
}); });
defineExpose({
handleSave,
});
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -487,6 +499,9 @@
color: var(--color-text-1); color: var(--color-text-1);
} }
&-action { &-action {
position: absolute;
top: 0;
right: 6px;
color: rgb(var(--primary-7)); color: rgb(var(--primary-7));
} }
} }

View File

@ -452,6 +452,7 @@
formRules.value = customFields.value.map((item: any) => { formRules.value = customFields.value.map((item: any) => {
const multipleType = ['MULTIPLE_SELECT', 'CHECKBOX', 'MULTIPLE_MEMBER', 'MULTIPLE_INPUT']; const multipleType = ['MULTIPLE_SELECT', 'CHECKBOX', 'MULTIPLE_MEMBER', 'MULTIPLE_INPUT'];
const currentDefaultValue = multipleType.includes(item.type) ? JSON.parse(item.defaultValue) : item.defaultValue; const currentDefaultValue = multipleType.includes(item.type) ? JSON.parse(item.defaultValue) : item.defaultValue;
console.log('currentDefaultValue', currentDefaultValue);
return { return {
...item, ...item,
type: item.type, type: item.type,