feat: 项目&组织模板联调

This commit is contained in:
xinxin.wu 2024-08-01 14:07:10 +08:00 committed by 刘瑞斌
parent b5a3592e46
commit 0deac9e9f4
13 changed files with 147 additions and 31 deletions

View File

@ -46,6 +46,16 @@ export default mergeConfig(
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/test-plan\/report/, ''), rewrite: (path: string) => path.replace(/^\/front\/test-plan\/report/, ''),
}, },
'/organization': {
target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/organization/, ''),
},
'/project': {
target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/project/, ''),
},
'/plugin/image': { '/plugin/image': {
target: process.env.VITE_DEV_DOMAIN, target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,

View File

@ -26,6 +26,7 @@ import {
OrdUpdateFlowStatusUrl, OrdUpdateFlowStatusUrl,
OrdUpdateStateFlowUrl, OrdUpdateStateFlowUrl,
OrdWorkFlowUrl, OrdWorkFlowUrl,
orgRichUploadImageUrl,
ProjectCreateFlowStatusUrl, ProjectCreateFlowStatusUrl,
ProjectDeleteFlowStatusUrl, ProjectDeleteFlowStatusUrl,
ProjectSetStateUrl, ProjectSetStateUrl,
@ -33,6 +34,7 @@ import {
ProjectUpdateFlowStatusUrl, ProjectUpdateFlowStatusUrl,
ProjectUpdateStateFlowUrl, ProjectUpdateStateFlowUrl,
ProjectWorkFlowUrl, ProjectWorkFlowUrl,
proRichUploadImageUrl,
SetProjectTemplateUrl, SetProjectTemplateUrl,
UpdateFieldUrl, UpdateFieldUrl,
UpdateOrganizeTemplateUrl, UpdateOrganizeTemplateUrl,
@ -40,11 +42,10 @@ import {
UpdateProjectTemplateUrl, UpdateProjectTemplateUrl,
} from '@/api/requrls/setting/template'; } from '@/api/requrls/setting/template';
import { CommonList, TableQueryParams } from '@/models/common'; import { TableQueryParams } from '@/models/common';
import type { import type {
ActionTemplateManage, ActionTemplateManage,
AddOrUpdateField, AddOrUpdateField,
OrdTemplateManagement,
OrdWorkStatus, OrdWorkStatus,
SeneType, SeneType,
SetStateType, SetStateType,
@ -178,6 +179,12 @@ export function getProjectFieldDetail(id: string) {
return MSR.get({ url: GetFieldProjectDetailUrl, params: id }); return MSR.get({ url: GetFieldProjectDetailUrl, params: id });
} }
// 富文本编辑器上传图片文件
export function editorUploadFile(data: { fileList: File[] }, mode: 'organization' | 'project') {
const url = mode === 'organization' ? orgRichUploadImageUrl : proRichUploadImageUrl;
return MSR.uploadFile({ url }, { fileList: data.fileList }, '', false);
}
/** * /** *
* () * ()
*/ */

View File

@ -93,3 +93,11 @@ export const ProjectSetStateUrl = '/project/status/flow/setting/status/definitio
export const ProjectStateSortUrl = '/project/status/flow/setting/status/sort'; export const ProjectStateSortUrl = '/project/status/flow/setting/status/sort';
// 更新状态流转 // 更新状态流转
export const ProjectUpdateStateFlowUrl = '/project/status/flow/setting/status/flow/update'; export const ProjectUpdateStateFlowUrl = '/project/status/flow/setting/status/flow/update';
// 组织模板富文本图片链接
export const orgRichUploadImageUrl = '/organization/template/upload/temp/img';
// 项目模板富文本图片链接
export const proRichUploadImageUrl = '/project/template/upload/temp/img';
// 组织预览富文本图片
export const previewOrgImageUrl = '/organization/template/img/preview';
// 项目预览富文本图片
export const previewProImageUrl = '/project/template/img/preview';

View File

@ -12,6 +12,7 @@
* import rehypeStringify from 'rehype-stringify'; * import rehypeStringify from 'rehype-stringify';
* return unified().use(rehypeParse).use(rehypeFormat).use(rehypeStringify).processSync(content.value); * return unified().use(rehypeParse).use(rehypeFormat).use(rehypeStringify).processSync(content.value);
*/ */
import { useRoute } from 'vue-router';
import { useDebounceFn, useVModel } from '@vueuse/core'; import { useDebounceFn, useVModel } from '@vueuse/core';
import type { MsFileItem } from '@/components/pure/ms-upload/types'; import type { MsFileItem } from '@/components/pure/ms-upload/types';
@ -20,7 +21,6 @@
import { editorUploadFile } from '@/api/modules/case-management/featureCase'; import { editorUploadFile } from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useLocale from '@/locale/useLocale'; import useLocale from '@/locale/useLocale';
import { useAppStore } from '@/store';
import '@halo-dev/richtext-editor/dist/style.css'; import '@halo-dev/richtext-editor/dist/style.css';
import ExtensionImage from './extensions/image/index'; import ExtensionImage from './extensions/image/index';
@ -70,10 +70,8 @@
import type { queueAsPromised } from 'fastq'; import type { queueAsPromised } from 'fastq';
import * as fastq from 'fastq'; import * as fastq from 'fastq';
const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute();
// image drag and paste upload
type Task = { type Task = {
file: File; file: File;
process: (permalink: string, fileId: string) => void; process: (permalink: string, fileId: string) => void;
@ -122,8 +120,8 @@
} }
const uploadFileId = await props.uploadImage(arg.file); const uploadFileId = await props.uploadImage(arg.file);
if (uploadFileId) { if (uploadFileId) {
// const permanentUrl = `${PreviewEditorImageUrl}/${appStore.currentProjectId}/${uploadFileId}/${true}`; const infoId = route.query.orgId && route.query.pId ? route.query.pId : route.query.orgId;
const permanentUrl = `${props.previewUrl}/${appStore.currentProjectId}/${uploadFileId}/${true}`; const permanentUrl = `${props.previewUrl}/${infoId}/${uploadFileId}/${true}`;
arg.process(permanentUrl, uploadFileId); arg.process(permanentUrl, uploadFileId);
} }
} }

View File

@ -120,6 +120,7 @@ export interface ActionTemplateManage {
enablePlatformDefault?: boolean; enablePlatformDefault?: boolean;
internal?: boolean; // 是否为系统模板 internal?: boolean; // 是否为系统模板
platForm?: string; platForm?: string;
uploadImgFileIds: string[]; // 模板附件
[key: string]: any; [key: string]: any;
} }

View File

@ -315,6 +315,7 @@
router.push({ router.push({
name: routeName.value, name: routeName.value,
query: { query: {
...route.query,
type: route.query.type, type: route.query.type,
}, },
params: { params: {
@ -328,6 +329,7 @@
router.push({ router.push({
name: routeName.value, name: routeName.value,
query: { query: {
...route.query,
id, id,
type: route.query.type, type: route.query.type,
}, },
@ -342,6 +344,7 @@
router.push({ router.push({
name: routeName.value, name: routeName.value,
query: { query: {
...route.query,
id, id,
type: route.query.type, type: route.query.type,
}, },

View File

@ -30,7 +30,7 @@
* @description 项目设置--模板 * @description 项目设置--模板
*/ */
import { useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
import MsCardList from '@/components/business/ms-card-list/index.vue'; import MsCardList from '@/components/business/ms-card-list/index.vue';
@ -43,12 +43,14 @@
import { getCardList } from '@/views/setting/organization/template/components/fieldSetting'; import { getCardList } from '@/views/setting/organization/template/components/fieldSetting';
const router = useRouter(); const router = useRouter();
const route = useRoute();
const templateStore = useTemplateStore(); const templateStore = useTemplateStore();
// //
const fieldSetting = (key: string) => { const fieldSetting = (key: string) => {
router.push({ router.push({
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_FIELD_SETTING, name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_FIELD_SETTING,
query: { query: {
...route.query,
type: key, type: key,
}, },
}); });
@ -59,6 +61,7 @@
router.push({ router.push({
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT, name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT,
query: { query: {
...route.query,
type: key, type: key,
}, },
}); });
@ -69,6 +72,7 @@
router.push({ router.push({
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_WORKFLOW, name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_WORKFLOW,
query: { query: {
...route.query,
type: key, type: key,
}, },
}); });

View File

@ -56,8 +56,18 @@
</template> </template>
<div class="wrapper-preview"> <div class="wrapper-preview">
<div class="preview-left pr-4"> <div class="preview-left pr-4">
<DefectTemplateLeftContent v-if="route.query.type === 'BUG'" v-model:defaultForm="defaultBugForm" /> <DefectTemplateLeftContent
<CaseTemplateLeftContent v-else v-model:defaultForm="defaultCaseForm" /> v-if="route.query.type === 'BUG'"
v-model:uploadImgFileIds="uploadBugImgFileIds"
v-model:defaultForm="defaultBugForm"
:mode="props.mode"
/>
<CaseTemplateLeftContent
v-else
v-model:uploadImgFileIds="uploadCaseImgFileIds"
v-model:defaultForm="defaultCaseForm"
:mode="props.mode"
/>
</div> </div>
<div class="preview-right px-4"> <div class="preview-right px-4">
<!-- 系统内置的字段 {处理人, 状态...} --> <!-- 系统内置的字段 {处理人, 状态...} -->
@ -308,6 +318,7 @@
scopeId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value, scopeId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value,
enableThirdPart: false, enableThirdPart: false,
platForm: '', platForm: '',
uploadImgFileIds: [],
}; };
const initBugForm = { const initBugForm = {
@ -359,6 +370,9 @@
// //
const defaultBugForm = ref<defaultBugField>(cloneDeep(defaultTemplateBugDetail)); const defaultBugForm = ref<defaultBugField>(cloneDeep(defaultTemplateBugDetail));
const uploadBugImgFileIds = ref<string[]>([]);
const uploadCaseImgFileIds = ref<string[]>([]);
// //
function getSystemFields(form: Record<string, any>) { function getSystemFields(form: Record<string, any>) {
const tempFormField: Record<string, any>[] = []; const tempFormField: Record<string, any>[] = [];
@ -397,6 +411,8 @@
const systemFields: Record<string, any>[] = const systemFields: Record<string, any>[] =
route.query.type === 'BUG' ? getSystemFields(defaultBugForm.value) : getSystemFields(defaultCaseForm.value); route.query.type === 'BUG' ? getSystemFields(defaultBugForm.value) : getSystemFields(defaultCaseForm.value);
const uploadImgFileIds = route.query.type === 'BUG' ? uploadBugImgFileIds.value : uploadCaseImgFileIds.value;
const { name, remark, enableThirdPart, id } = templateForm.value; const { name, remark, enableThirdPart, id } = templateForm.value;
return { return {
id, id,
@ -407,6 +423,7 @@
scopeId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value, scopeId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value,
scene: route.query.type, scene: route.query.type,
systemFields, systemFields,
uploadImgFileIds,
}; };
} }

View File

@ -20,7 +20,7 @@
v-model:raw="form.prerequisite" v-model:raw="form.prerequisite"
v-model:filed-ids="prerequisiteFileIds" v-model:filed-ids="prerequisiteFileIds"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
:preview-url="PreviewEditorImageUrl" :preview-url="previewEditorImageUrl"
:editable="!props.isDisabled" :editable="!props.isDisabled"
/> />
</a-form-item> </a-form-item>
@ -55,7 +55,7 @@
v-model:raw="form.textDescription" v-model:raw="form.textDescription"
v-model:filed-ids="textDescriptionFileIds" v-model:filed-ids="textDescriptionFileIds"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
:preview-url="PreviewEditorImageUrl" :preview-url="previewEditorImageUrl"
:editable="!props.isDisabled" :editable="!props.isDisabled"
/> />
</a-form-item> </a-form-item>
@ -68,7 +68,7 @@
v-model:raw="form.expectedResult" v-model:raw="form.expectedResult"
v-model:filed-ids="expectedResultFileIds" v-model:filed-ids="expectedResultFileIds"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
:preview-url="PreviewEditorImageUrl" :preview-url="previewEditorImageUrl"
:editable="!props.isDisabled" :editable="!props.isDisabled"
/> />
</a-form-item> </a-form-item>
@ -77,7 +77,7 @@
v-model:raw="form.description" v-model:raw="form.description"
v-model:filed-ids="descriptionFileIds" v-model:filed-ids="descriptionFileIds"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
:preview-url="PreviewEditorImageUrl" :preview-url="previewEditorImageUrl"
:editable="!props.isDisabled" :editable="!props.isDisabled"
/> />
</a-form-item> </a-form-item>
@ -95,8 +95,8 @@
import AddAttachment from '@/components/business/ms-add-attachment/index.vue'; import AddAttachment from '@/components/business/ms-add-attachment/index.vue';
import AddStep from '@/views/case-management/caseManagementFeature/components/addStep.vue'; import AddStep from '@/views/case-management/caseManagementFeature/components/addStep.vue';
import { editorUploadFile } from '@/api/modules/case-management/featureCase'; import { editorUploadFile } from '@/api/modules/setting/template';
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase'; import { previewOrgImageUrl, previewProImageUrl } from '@/api/requrls/setting/template';
import { defaultTemplateCaseDetail } from '@/config/template'; import { defaultTemplateCaseDetail } from '@/config/template';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { getGenerateId } from '@/utils'; import { getGenerateId } from '@/utils';
@ -107,12 +107,16 @@
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
mode: 'organization' | 'project';
isDisabled?: boolean; isDisabled?: boolean;
}>(); }>();
const form = defineModel<defaultCaseField>('defaultForm', { const form = defineModel<defaultCaseField>('defaultForm', {
default: defaultTemplateCaseDetail, default: defaultTemplateCaseDetail,
}); });
const uploadImgFileIds = defineModel<string[]>('uploadImgFileIds', {
default: [],
});
const fileList = ref([]); const fileList = ref([]);
@ -177,13 +181,41 @@
} }
} }
); );
// TODO //
async function handleUploadImage(file: File) { async function handleUploadImage(file: File) {
const { data } = await editorUploadFile({ const { data } = await editorUploadFile(
{
fileList: [file], fileList: [file],
}); },
props.mode
);
return data; return data;
} }
const previewEditorImageUrl = computed(() =>
props.mode === 'organization' ? previewOrgImageUrl : previewProImageUrl
);
const fileIds = computed(() => {
return [
...prerequisiteFileIds.value,
...textDescriptionFileIds.value,
...expectedResultFileIds.value,
...descriptionFileIds.value,
];
});
watch(
() => fileIds.value,
(val) => {
if (val) {
uploadImgFileIds.value = val;
}
},
{
deep: true,
}
);
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -17,7 +17,7 @@
v-model:raw="form.description" v-model:raw="form.description"
v-model:filed-ids="descriptionFileIds" v-model:filed-ids="descriptionFileIds"
:upload-image="handleUploadImage" :upload-image="handleUploadImage"
:preview-url="EditorPreviewFileUrl" :preview-url="previewEditorImageUrl"
:editable="!props.isDisabled" :editable="!props.isDisabled"
/> />
</a-form-item> </a-form-item>
@ -34,8 +34,8 @@
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue'; import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
import AddAttachment from '@/components/business/ms-add-attachment/index.vue'; import AddAttachment from '@/components/business/ms-add-attachment/index.vue';
import { editorUploadFile } from '@/api/modules/bug-management'; import { editorUploadFile } from '@/api/modules/setting/template';
import { EditorPreviewFileUrl } from '@/api/requrls/bug-management'; import { previewOrgImageUrl, previewProImageUrl } from '@/api/requrls/setting/template';
import { defaultTemplateBugDetail } from '@/config/template'; import { defaultTemplateBugDetail } from '@/config/template';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
@ -44,6 +44,7 @@
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
mode: 'organization' | 'project';
isDisabled?: boolean; isDisabled?: boolean;
}>(); }>();
@ -53,16 +54,39 @@
default: defaultTemplateBugDetail, default: defaultTemplateBugDetail,
}); });
const uploadImgFileIds = defineModel<string[]>('uploadImgFileIds', {
default: [],
});
// ID // ID
const descriptionFileIds = ref<string[]>([]); const descriptionFileIds = ref<string[]>([]);
// TODO //
async function handleUploadImage(file: File) { async function handleUploadImage(file: File) {
const { data } = await editorUploadFile({ const { data } = await editorUploadFile(
{
fileList: [file], fileList: [file],
}); },
props.mode
);
return data; return data;
} }
const previewEditorImageUrl = computed(() =>
props.mode === 'organization' ? previewOrgImageUrl : previewProImageUrl
);
watch(
() => descriptionFileIds.value,
(val) => {
if (val) {
uploadImgFileIds.value = val;
}
},
{
deep: true,
}
);
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -238,6 +238,7 @@
router.push({ router.push({
name: routeName.value, name: routeName.value,
query: { query: {
...route.query,
type: route.query.type, type: route.query.type,
}, },
params: { params: {
@ -251,6 +252,7 @@
router.push({ router.push({
name: routeName.value, name: routeName.value,
query: { query: {
...route.query,
id, id,
type: route.query.type, type: route.query.type,
}, },
@ -265,6 +267,7 @@
router.push({ router.push({
name: routeName.value, name: routeName.value,
query: { query: {
...route.query,
id, id,
type: route.query.type, type: route.query.type,
}, },

View File

@ -1,8 +1,13 @@
<template> <template>
<div class="wrapper-preview"> <div class="wrapper-preview">
<div class="preview-left pr-4"> <div class="preview-left pr-4">
<DefectTemplateLeftContent v-if="props.templateType === 'BUG'" v-model:defaultForm="defaultBugForm" is-disabled /> <DefectTemplateLeftContent
<CaseTemplateLeftContent v-else v-model:defaultForm="defaultCaseForm" is-disabled /> v-if="props.templateType === 'BUG'"
v-model:defaultForm="defaultBugForm"
mode="project"
is-disabled
/>
<CaseTemplateLeftContent v-else v-model:defaultForm="defaultCaseForm" mode="project" is-disabled />
</div> </div>
<div class="preview-right px-4"> <div class="preview-right px-4">
<!-- 系统内置的字段 {处理人, 状态...} --> <!-- 系统内置的字段 {处理人, 状态...} -->

View File

@ -39,7 +39,7 @@
/** /**
* @description 系统设置--组织--模版 * @description 系统设置--组织--模版
*/ */
import { useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
import MsCardList from '@/components/business/ms-card-list/index.vue'; import MsCardList from '@/components/business/ms-card-list/index.vue';
@ -56,6 +56,7 @@
const templateStore = useTemplateStore(); const templateStore = useTemplateStore();
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const route = useRoute();
const visitedKey = 'notRemind'; const visitedKey = 'notRemind';
const { addVisited } = useVisit(visitedKey); const { addVisited } = useVisit(visitedKey);
const { getIsVisited } = useVisit(visitedKey); const { getIsVisited } = useVisit(visitedKey);
@ -78,6 +79,7 @@
router.push({ router.push({
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING, name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING,
query: { query: {
...route.query,
type: key, type: key,
}, },
}); });
@ -88,6 +90,7 @@
router.push({ router.push({
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
query: { query: {
...route.query,
type: key, type: key,
}, },
}); });
@ -98,6 +101,7 @@
router.push({ router.push({
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_WORKFLOW, name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_WORKFLOW,
query: { query: {
...route.query,
type: key, type: key,
}, },
}); });