refactor(系统设置): 重构系统设置模板&项目模板
This commit is contained in:
parent
f21064cb3e
commit
53226e689e
|
@ -28,7 +28,10 @@ export function getCommonScriptPage(data: TableQueryParams) {
|
|||
}
|
||||
|
||||
// 添加公共脚本
|
||||
export function addCommonScriptReq(data: AddOrUpdateCommonScript) {
|
||||
export function addOrUpdateCommonScriptReq(data: AddOrUpdateCommonScript) {
|
||||
if (data.id) {
|
||||
return MSR.post({ url: UpdateCommonScriptUrl, data });
|
||||
}
|
||||
return MSR.post({ url: AddCommonScriptUrl, data });
|
||||
}
|
||||
// 更新公共脚本
|
||||
|
|
|
@ -58,6 +58,15 @@
|
|||
</template>
|
||||
</ms-base-table>
|
||||
</MsDrawer>
|
||||
<AddScriptDrawer
|
||||
v-model:visible="showScriptDrawer"
|
||||
v-model:params="paramsList"
|
||||
:confirm-loading="confirmLoading"
|
||||
:script-id="isEditId"
|
||||
ok-text="project.commonScript.apply"
|
||||
:enable-radio-selected="radioSelected"
|
||||
@save="saveHandler"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -71,14 +80,20 @@
|
|||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
|
||||
import { getInsertCommonScriptPage } from '@/api/modules/project-management/commonScript';
|
||||
import { addOrUpdateCommonScriptReq, getInsertCommonScriptPage } from '@/api/modules/project-management/commonScript';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import type { AddOrUpdateCommonScript, ParamsRequestType } from '@/models/projectManagement/commonScript';
|
||||
|
||||
import Message from '@arco-design/web-vue/es/message';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
const AddScriptDrawer = defineAsyncComponent(
|
||||
() => import('@/components/business/ms-common-script/ms-addScriptDrawer.vue')
|
||||
);
|
||||
const { t } = useI18n();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -93,7 +108,7 @@
|
|||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['update:visible', 'update:checkedId', 'save', 'addScript']);
|
||||
const emit = defineEmits(['update:visible', 'update:checkedId', 'save']);
|
||||
const insertScriptDrawer = computed({
|
||||
get() {
|
||||
return props.visible;
|
||||
|
@ -229,9 +244,41 @@
|
|||
function handleDrawerCancel() {
|
||||
insertScriptDrawer.value = false;
|
||||
}
|
||||
|
||||
const showScriptDrawer = ref<boolean>(false);
|
||||
function addCommonScript() {
|
||||
emit('addScript');
|
||||
showScriptDrawer.value = true;
|
||||
}
|
||||
|
||||
const paramsList = ref<ParamsRequestType[]>([]);
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
const isEditId = ref<string>('');
|
||||
const radioSelected = ref<boolean>(false);
|
||||
|
||||
// 保存自定义代码片段应用
|
||||
async function saveHandler(form: AddOrUpdateCommonScript) {
|
||||
try {
|
||||
confirmLoading.value = true;
|
||||
const { status } = form;
|
||||
const paramTableList = paramsList.value.slice(0, -1);
|
||||
const paramsObj: AddOrUpdateCommonScript = {
|
||||
...form,
|
||||
status: status || 'DRAFT',
|
||||
projectId: currentProjectId.value,
|
||||
params: JSON.stringify(paramTableList),
|
||||
};
|
||||
await addOrUpdateCommonScriptReq(paramsObj);
|
||||
showScriptDrawer.value = false;
|
||||
initData();
|
||||
Message.success(
|
||||
form.status === 'DRAFT'
|
||||
? t('project.commonScript.saveDraftSuccessfully')
|
||||
: t('project.commonScript.appliedSuccessfully')
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<MsDrawer
|
||||
v-model:visible="showScriptDrawer"
|
||||
:title="t('project.commonScript.addPublicScript')"
|
||||
:title="form.id ? t('project.commonScript.editPublicScript') : t('project.commonScript.addPublicScript')"
|
||||
:width="768"
|
||||
:footer="true"
|
||||
unmount-on-close
|
||||
|
@ -43,6 +43,7 @@
|
|||
:scroll="{ x: '100%' }"
|
||||
:columns="columns"
|
||||
:height-used="heightUsed"
|
||||
:selectable="false"
|
||||
@change="handleParamTableChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
@ -218,12 +219,23 @@
|
|||
}
|
||||
);
|
||||
|
||||
// watchEffect(() => {
|
||||
// editScriptId.value = props.scriptId;
|
||||
// if (editScriptId.value) {
|
||||
// getDetail();
|
||||
// }
|
||||
// });
|
||||
|
||||
watch(
|
||||
() => showScriptDrawer.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
form.value = { ...initForm };
|
||||
innerParams.value = [];
|
||||
editScriptId.value = props.scriptId;
|
||||
if (editScriptId.value) {
|
||||
getDetail();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -311,6 +323,10 @@
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
// onBeforeUnmount(() => {
|
||||
// editScriptId.value = '';
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
v-model:visible="showInsertDrawer"
|
||||
:script-language="innerLanguagesType"
|
||||
:enable-radio-selected="props.enableRadioSelected"
|
||||
@add-script="insertCommonScript"
|
||||
@save="saveHandler"
|
||||
/>
|
||||
<FormApiImportDrawer
|
||||
|
@ -168,6 +167,7 @@ ${item.script}
|
|||
`;
|
||||
});
|
||||
codeEditorRef.value?.insertContent(scriptStr);
|
||||
showInsertDrawer.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ export const TEXTAREA = {
|
|||
props: {
|
||||
'placeholder': t('formCreate.PleaseEnter'),
|
||||
'max-length': 1000,
|
||||
'auto-size': '{ minRows: 1 }',
|
||||
'auto-size': { minRows: 1 },
|
||||
},
|
||||
};
|
||||
export const JIRAKEY = {
|
||||
|
|
|
@ -73,7 +73,8 @@
|
|||
emits('reload');
|
||||
}
|
||||
|
||||
function handleChange() {
|
||||
function handleChange(value: any) {
|
||||
formApi.value?.validateField(value);
|
||||
emits('change');
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -480,7 +480,7 @@ export const pathMap: PathMapItem[] = [
|
|||
locale: 'menu.projectManagement.templateManager',
|
||||
route: RouteEnum.PROJECT_MANAGEMENT_TEMPLATE,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
children: [
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_FUNCTIONAL', // 模板管理-用例模板
|
||||
|
@ -490,7 +490,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'FUNCTIONAL',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
children: [
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_FUNCTIONAL_FIELD', // 模板管理-用例模板-用例模板字段管理
|
||||
|
@ -500,7 +500,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'FUNCTIONAL',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_FUNCTIONAL_TEMPLATE', // 模板管理-用例模板-用例模板管理
|
||||
|
@ -510,7 +510,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'FUNCTIONAL',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -522,7 +522,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'API',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
children: [
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_API_FIELD', // 模板管理-接口模板-接口模板字段管理
|
||||
|
@ -532,7 +532,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'API',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_API_TEMPLATE', // 模板管理-接口模板-接口模板管理
|
||||
|
@ -542,7 +542,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'API',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -554,7 +554,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'BUG',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
children: [
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_BUG_FIELD', // 模板管理-缺陷模板管理-字段管理
|
||||
|
@ -564,7 +564,7 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'BUG',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_BUG_TEMPLATE', // 模板管理-缺陷模板-缺陷模板管理
|
||||
|
@ -574,14 +574,14 @@ export const pathMap: PathMapItem[] = [
|
|||
routeQuery: {
|
||||
type: 'BUG',
|
||||
},
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_TEMPLATE_BUG_WORKFLOW', // 模板管理-缺陷模板-缺陷工作流
|
||||
locale: 'menu.settings.organization.templateManagementWorkFlow',
|
||||
route: RouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_WORKFLOW,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[1],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@ export enum ApiTestRouteEnum {
|
|||
API_TEST = 'apiTest',
|
||||
API_TEST_DEBUG = 'apiTestDebug',
|
||||
API_TEST_MANAGEMENT = 'apiTestManagement',
|
||||
API_TEST_REPORT = 'apiTestReport',
|
||||
}
|
||||
|
||||
export enum BugManagementRouteEnum {
|
||||
|
@ -41,7 +42,9 @@ export enum ProjectManagementRouteEnum {
|
|||
PROJECT_MANAGEMENT_PERMISSION_MENU_MANAGEMENT = 'projectManagementPermissionMenuManagement',
|
||||
PROJECT_MANAGEMENT_TEMPLATE = 'projectManagementTemplate',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT = 'projectManagementTemplateManagement',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL = 'projectManagementTemplateManagementDetail',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_CASE_DETAIL = 'projectManagementTemplateManagementCaseDetail',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_API_DETAIL = 'projectManagementTemplateManagementCaseDetail',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_BUG_DETAIL = 'projectManagementTemplateManagementBugDetail',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_WORKFLOW = 'projectManagementTemplateManagementWorkFlow',
|
||||
PROJECT_MANAGEMENT_TEMPLATE_FIELD_SETTING = 'projectManagementTemplateFiledSetting',
|
||||
PROJECT_MANAGEMENT_PERMISSION_VERSION = 'projectManagementPermissionVersion',
|
||||
|
@ -84,7 +87,9 @@ export enum SettingRouteEnum {
|
|||
SETTING_ORGANIZATION_TEMPLATE = 'settingOrganizationTemplate',
|
||||
SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING = 'settingOrganizationTemplateFiledSetting',
|
||||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT = 'settingOrganizationTemplateManagement',
|
||||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL = 'settingOrganizationTemplateManagementDetail',
|
||||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_CASE_DETAIL = 'settingOrganizationTemplateManagementCaseDetail',
|
||||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_API_DETAIL = 'settingOrganizationTemplateManagementApiDetail',
|
||||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_BUG_DETAIL = 'settingOrganizationTemplateManagementBugDetail',
|
||||
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_WORKFLOW = 'settingOrganizationTemplateWorkFlow',
|
||||
SETTING_ORGANIZATION_SERVICE = 'settingOrganizationService',
|
||||
SETTING_ORGANIZATION_LOG = 'settingOrganizationLog',
|
||||
|
|
|
@ -116,6 +116,8 @@ export interface ActionTemplateManage {
|
|||
fieldType?: string;
|
||||
systemFields?: Record<string, any>[];
|
||||
internal?: boolean; // 是否为系统模板
|
||||
platForm?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 工作流列表字段
|
||||
|
|
|
@ -177,11 +177,11 @@ const ProjectManagement: AppRouteRecordRaw = {
|
|||
},
|
||||
// 项目-模板-创建模板和模板详情
|
||||
{
|
||||
path: 'templateDetail/:mode?',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
component: () => import('@/views/project-management/template/components/proTemplateDetail.vue'),
|
||||
path: 'templateCaseDetail/:mode?',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_CASE_DETAIL,
|
||||
component: () => import('@/views/project-management/template/components/detail.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.organization.templateManagementDetail',
|
||||
locale: 'system.orgTemplate.createCaseTemplate',
|
||||
roles: ['PROJECT_TEMPLATE:READ+UPDATE', 'PROJECT_TEMPLATE:READ+ADD'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
|
@ -194,9 +194,65 @@ const ProjectManagement: AppRouteRecordRaw = {
|
|||
query: ['type'],
|
||||
},
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
locale: 'menu.settings.organization.templateManagementDetail',
|
||||
editLocale: 'menu.settings.organization.templateManagementEdit',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_CASE_DETAIL,
|
||||
locale: 'system.orgTemplate.createCaseTemplate',
|
||||
editLocale: 'system.orgTemplate.updateCaseTemplate',
|
||||
editTag: 'id',
|
||||
query: ['type'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// 项目-模板-接口模板
|
||||
{
|
||||
path: 'templateApiDetail/:mode?',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_API_DETAIL,
|
||||
component: () => import('@/views/project-management/template/components/detail.vue'),
|
||||
meta: {
|
||||
locale: 'system.orgTemplate.createApiTemplate',
|
||||
roles: ['PROJECT_TEMPLATE:READ+UPDATE', 'PROJECT_TEMPLATE:READ+ADD'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE,
|
||||
locale: 'menu.settings.organization.template',
|
||||
},
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT,
|
||||
locale: 'menu.settings.organization.templateManagementList',
|
||||
query: ['type'],
|
||||
},
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_API_DETAIL,
|
||||
locale: 'system.orgTemplate.createApiTemplate',
|
||||
editLocale: 'system.orgTemplate.updateApiTemplate',
|
||||
editTag: 'id',
|
||||
query: ['type'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// 项目-模板-缺陷模板
|
||||
{
|
||||
path: 'templateBugDetail/:mode?',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_BUG_DETAIL,
|
||||
component: () => import('@/views/project-management/template/components/detail.vue'),
|
||||
meta: {
|
||||
locale: 'system.orgTemplate.createDefectTemplate',
|
||||
roles: ['PROJECT_TEMPLATE:READ+UPDATE', 'PROJECT_TEMPLATE:READ+ADD'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE,
|
||||
locale: 'menu.settings.organization.template',
|
||||
},
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT,
|
||||
locale: 'menu.settings.organization.templateManagementList',
|
||||
query: ['type'],
|
||||
},
|
||||
{
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_BUG_DETAIL,
|
||||
locale: 'system.orgTemplate.createDefectTemplate',
|
||||
editLocale: 'system.orgTemplate.updateDefectTemplate',
|
||||
editTag: 'id',
|
||||
query: ['type'],
|
||||
},
|
||||
|
|
|
@ -285,13 +285,14 @@ const Setting: AppRouteRecordRaw = {
|
|||
],
|
||||
},
|
||||
},
|
||||
// 模板列表-模板管理-创建&编辑模板
|
||||
// 模板列表-创建&编辑模板
|
||||
// 用例模板
|
||||
{
|
||||
path: 'templateManagementDetail/:mode?',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
component: () => import('@/views/setting/organization/template/components/templateDetail.vue'),
|
||||
path: 'templateManagementCaseDetail/:mode?',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_CASE_DETAIL,
|
||||
component: () => import('@/views/setting/organization/template/components/detail.vue'),
|
||||
meta: {
|
||||
locale: 'menu.settings.organization.templateManagementDetail',
|
||||
locale: 'system.orgTemplate.createCaseTemplate',
|
||||
roles: ['ORGANIZATION_TEMPLATE:READ+UPDATE', 'ORGANIZATION_TEMPLATE:READ+ADD'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
|
@ -305,9 +306,65 @@ const Setting: AppRouteRecordRaw = {
|
|||
},
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||
locale: 'menu.settings.organization.templateManagementDetail',
|
||||
locale: 'system.orgTemplate.createCaseTemplate',
|
||||
editTag: 'id',
|
||||
editLocale: 'menu.settings.organization.templateManagementEdit',
|
||||
editLocale: 'system.orgTemplate.updateCaseTemplate',
|
||||
query: ['type'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// 接口模板
|
||||
{
|
||||
path: 'templateManagementApiDetail/:mode?',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_API_DETAIL,
|
||||
component: () => import('@/views/setting/organization/template/components/detail.vue'),
|
||||
meta: {
|
||||
locale: 'system.orgTemplate.createApiTemplate',
|
||||
roles: ['ORGANIZATION_TEMPLATE:READ+UPDATE', 'ORGANIZATION_TEMPLATE:READ+ADD'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE,
|
||||
locale: 'menu.settings.organization.template',
|
||||
},
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||
locale: 'menu.settings.organization.templateManagementList',
|
||||
query: ['type'],
|
||||
},
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||
locale: 'system.orgTemplate.createApiTemplate',
|
||||
editTag: 'id',
|
||||
editLocale: 'system.orgTemplate.updateApiTemplate',
|
||||
query: ['type'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// 缺陷模板
|
||||
{
|
||||
path: 'templateManagementBugDetail/:mode?',
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_BUG_DETAIL,
|
||||
component: () => import('@/views/setting/organization/template/components/detail.vue'),
|
||||
meta: {
|
||||
locale: 'system.orgTemplate.createDefectTemplate',
|
||||
roles: ['ORGANIZATION_TEMPLATE:READ+UPDATE', 'ORGANIZATION_TEMPLATE:READ+ADD'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE,
|
||||
locale: 'menu.settings.organization.template',
|
||||
},
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||
locale: 'menu.settings.organization.templateManagementList',
|
||||
query: ['type'],
|
||||
},
|
||||
{
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||
locale: 'system.orgTemplate.createDefectTemplate',
|
||||
editTag: 'id',
|
||||
editLocale: 'system.orgTemplate.updateDefectTemplate',
|
||||
query: ['type'],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
import { createCaseRequest, updateCaseRequest } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import { useAppStore } from '@/store';
|
||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
|
@ -48,10 +49,13 @@
|
|||
|
||||
import Message from '@arco-design/web-vue/es/message';
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
setState(false);
|
||||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
|
||||
const visitedKey = 'doNotNextTipCreateCase';
|
||||
|
@ -83,6 +87,7 @@
|
|||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE,
|
||||
query: { organizationId: route.query.organizationId, projectId: route.query.projectId },
|
||||
});
|
||||
setState(true);
|
||||
// 创建用例
|
||||
} else {
|
||||
// 创建并关联
|
||||
|
@ -119,6 +124,7 @@
|
|||
},
|
||||
});
|
||||
}
|
||||
setState(true);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -229,15 +229,6 @@
|
|||
import { CommentParams } from '@/components/business/ms-comment/types';
|
||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||
import SettingDrawer from './tabContent/settingDrawer.vue';
|
||||
import TabDefect from './tabContent/tabBug/tabDefect.vue';
|
||||
import TabCaseTable from './tabContent/tabCase/tabCaseTable.vue';
|
||||
import TabCaseReview from './tabContent/tabCaseReview.vue';
|
||||
import TabChangeHistory from './tabContent/tabChangeHistory.vue';
|
||||
import TabComment from './tabContent/tabComment/tabCommentIndex.vue';
|
||||
import TabDemand from './tabContent/tabDemand/demand.vue';
|
||||
import TabDependency from './tabContent/tabDependency/tabDependency.vue';
|
||||
import TabDetail from './tabContent/tabDetail.vue';
|
||||
import TabTestPlan from './tabContent/tabTestPlan.vue';
|
||||
|
||||
import {
|
||||
createCommentList,
|
||||
|
@ -261,6 +252,16 @@
|
|||
import { getCaseLevels } from './utils';
|
||||
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
// 异步加载组件
|
||||
const TabDefect = defineAsyncComponent(() => import('./tabContent/tabBug/tabDefect.vue'));
|
||||
const TabCaseTable = defineAsyncComponent(() => import('./tabContent/tabCase/tabCaseTable.vue'));
|
||||
const TabCaseReview = defineAsyncComponent(() => import('./tabContent/tabCaseReview.vue'));
|
||||
const TabChangeHistory = defineAsyncComponent(() => import('./tabContent/tabChangeHistory.vue'));
|
||||
const TabComment = defineAsyncComponent(() => import('./tabContent/tabComment/tabCommentIndex.vue'));
|
||||
const TabDemand = defineAsyncComponent(() => import('./tabContent/tabDemand/demand.vue'));
|
||||
const TabDependency = defineAsyncComponent(() => import('./tabContent/tabDependency/tabDependency.vue'));
|
||||
const TabDetail = defineAsyncComponent(() => import('./tabContent/tabDetail.vue'));
|
||||
const TabTestPlan = defineAsyncComponent(() => import('./tabContent/tabTestPlan.vue'));
|
||||
|
||||
const router = useRouter();
|
||||
const detailDrawerRef = ref<InstanceType<typeof MsDetailDrawer>>();
|
||||
|
|
|
@ -465,21 +465,18 @@
|
|||
'slotName': 'num',
|
||||
'dataIndex': 'num',
|
||||
'width': 200,
|
||||
'showInTable': true,
|
||||
'sortable': {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
sorter: true,
|
||||
},
|
||||
'filter-icon-align-left': true,
|
||||
'showTooltip': true,
|
||||
'ellipsis': true,
|
||||
'showDrag': false,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnName',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
editType: hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) ? ColumnEditTypeEnum.INPUT : undefined,
|
||||
|
@ -487,7 +484,6 @@
|
|||
sortDirections: ['ascend', 'descend'],
|
||||
sorter: true,
|
||||
},
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
|
@ -791,7 +787,7 @@
|
|||
}
|
||||
}
|
||||
const initDefaultFields = ref<CustomAttributes[]>([]);
|
||||
let fullColumns: MsTableColumn = []; // 全量列表
|
||||
// let fullColumns: MsTableColumn = []; // 全量列表
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setKeyword, setAdvanceFilter } = useTable(
|
||||
getCaseList,
|
||||
|
@ -1183,6 +1179,7 @@
|
|||
// 处理自定义字段列
|
||||
let customFieldsColumns: Record<string, any>[] = [];
|
||||
const tableRef = ref<InstanceType<typeof MsBaseTable> | null>(null);
|
||||
const fullColumns = ref<MsTableColumn>([]);
|
||||
|
||||
// 处理自定义字段展示
|
||||
async function getDefaultFields() {
|
||||
|
@ -1204,12 +1201,12 @@
|
|||
|
||||
caseLevelFields.value = result.customFields.find((item: any) => item.internal && item.fieldName === '用例等级');
|
||||
caseFilters.value = caseLevelFields.value.options.map((item: any) => item.value);
|
||||
fullColumns = [
|
||||
fullColumns.value = [
|
||||
...columns.slice(0, columns.length - 1),
|
||||
...customFieldsColumns,
|
||||
...columns.slice(columns.length - 1, columns.length),
|
||||
];
|
||||
await tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_TABLE, fullColumns, 'drawer');
|
||||
await tableStore.initColumn(TableKeyEnum.CASE_MANAGEMENT_TABLE, fullColumns.value, 'drawer');
|
||||
}
|
||||
|
||||
// 如果是用例等级
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
/> -->
|
||||
<MsButton @click="saveAsHandler(record)">{{ t('caseManagement.featureCase.saveAsVersion') }}</MsButton>
|
||||
</template>
|
||||
|
||||
</ms-base-table>
|
||||
<a-modal
|
||||
v-model:visible="showModal"
|
||||
|
@ -129,15 +128,15 @@
|
|||
dataIndex: 'createTime',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnActions',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
// {
|
||||
// title: 'caseManagement.featureCase.tableColumnActions',
|
||||
// slotName: 'operation',
|
||||
// dataIndex: 'operation',
|
||||
// fixed: 'right',
|
||||
// width: 140,
|
||||
// showInTable: true,
|
||||
// showDrag: false,
|
||||
// },
|
||||
];
|
||||
|
||||
const typeOptions = [
|
||||
|
@ -159,9 +158,9 @@
|
|||
columns,
|
||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_CHANGE_HISTORY,
|
||||
scroll: { x: '100%' },
|
||||
selectable: true,
|
||||
selectable: false,
|
||||
heightUsed: 340,
|
||||
enableDrag: true,
|
||||
enableDrag: false,
|
||||
});
|
||||
|
||||
const form = ref({
|
||||
|
@ -240,8 +239,8 @@
|
|||
setLoadListParams({
|
||||
projectId: appStore.currentProjectId,
|
||||
sourceId: props.caseId,
|
||||
type:['IMPORT','ADD','UPDATE'],
|
||||
module: ['CASE_MANAGEMENT_CASE_CREATE','CASE_MANAGEMENT_CASE_UPDATE'],
|
||||
type: ['IMPORT', 'ADD', 'UPDATE'],
|
||||
module: ['CASE_MANAGEMENT_CASE_CREATE', 'CASE_MANAGEMENT_CASE_UPDATE'],
|
||||
});
|
||||
await loadList();
|
||||
featureCaseStore.getCaseCounts(props.caseId);
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.name',
|
||||
title: 'caseManagement.featureCase.demandName',
|
||||
slotName: 'demandName',
|
||||
dataIndex: 'demandName',
|
||||
width: 300,
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
import ScriptDetailDrawer from './components/scriptDetailDrawer.vue';
|
||||
|
||||
import {
|
||||
addCommonScriptReq,
|
||||
addOrUpdateCommonScriptReq,
|
||||
deleteCommonScript,
|
||||
getCommonScriptPage,
|
||||
} from '@/api/modules/project-management/commonScript';
|
||||
|
@ -279,13 +279,14 @@
|
|||
try {
|
||||
confirmLoading.value = true;
|
||||
const { status } = form;
|
||||
const paramTableList = paramsList.value.slice(0, -1);
|
||||
const paramsObj: AddOrUpdateCommonScript = {
|
||||
...form,
|
||||
status: status || 'DRAFT',
|
||||
projectId: currentProjectId.value,
|
||||
params: JSON.stringify(paramsList.value),
|
||||
params: JSON.stringify(paramTableList),
|
||||
};
|
||||
await addCommonScriptReq(paramsObj);
|
||||
await addOrUpdateCommonScriptReq(paramsObj);
|
||||
showScriptDrawer.value = false;
|
||||
initData();
|
||||
Message.success(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export default {
|
||||
'project.commonScript.searchByNameAndId': 'Search by name',
|
||||
'project.commonScript.addPublicScript': 'Add public Script',
|
||||
'project.commonScript.editPublicScript': 'Edit public Script',
|
||||
'project.commonScript.name': 'Name',
|
||||
'project.commonScript.description': 'Description',
|
||||
'project.commonScript.enable': 'Enable',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export default {
|
||||
'project.commonScript.searchByNameAndId': '通过名称搜索',
|
||||
'project.commonScript.addPublicScript': '添加公共脚本',
|
||||
'project.commonScript.editPublicScript': '编辑公共脚本',
|
||||
'project.commonScript.name': '名称',
|
||||
'project.commonScript.description': '描述',
|
||||
'project.commonScript.enable': '状态',
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<AddTemplate mode="project" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AddTemplate from '@/views/setting/organization/template/components/addTemplate.vue';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,408 +0,0 @@
|
|||
<template>
|
||||
<MsCard
|
||||
:loading="loading"
|
||||
:title="title"
|
||||
:is-edit="isEdit && route.params.mode !== 'copy'"
|
||||
has-breadcrumb
|
||||
@save="saveHandler"
|
||||
@save-and-continue="saveHandler(true)"
|
||||
>
|
||||
<template #headerRight>
|
||||
<div class="rightBtn">
|
||||
<a-button v-show="isPreview" type="outline" class="text-[var(--color-text-1)]" @click="togglePreview">{{
|
||||
t('system.orgTemplate.templatePreview')
|
||||
}}</a-button>
|
||||
<a-button v-show="!isPreview" type="outline" class="text-[var(--color-text-1)]" @click="togglePreview">{{
|
||||
t('system.orgTemplate.exitPreview')
|
||||
}}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 非预览模式 -->
|
||||
<div v-if="isPreview" class="nonPreview">
|
||||
<a-form ref="formRef" :model="templateForm" layout="vertical">
|
||||
<a-form-item
|
||||
:label="t('system.orgTemplate.templateName')"
|
||||
field="name"
|
||||
asterisk-position="end"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.templateNameRules') }]"
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="templateForm.name"
|
||||
:placeholder="t('system.orgTemplate.templateNamePlaceholder')"
|
||||
:max-length="255"
|
||||
class="max-w-[732px]"
|
||||
:disabled="templateForm?.internal"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" :label="t('system.orgTemplate.description')" asterisk-position="end">
|
||||
<a-textarea
|
||||
v-model="templateForm.remark"
|
||||
:max-length="1000"
|
||||
:placeholder="t('system.orgTemplate.resDescription')"
|
||||
:auto-size="{ minRows: 1 }"
|
||||
style="resize: vertical"
|
||||
class="max-w-[732px]"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="route.query.type === 'BUG'" field="remark" label="" asterisk-position="end"
|
||||
><a-checkbox v-model="templateForm.enableThirdPart">{{ t('system.orgTemplate.thirdParty') }}</a-checkbox>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="route.query.type === 'BUG'"
|
||||
field="fieldType"
|
||||
:label="t('system.orgTemplate.columnFieldType')"
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-radio-group v-model="fieldType" type="button">
|
||||
<a-radio value="custom">{{ t('system.orgTemplate.custom') }}</a-radio>
|
||||
<a-radio value="detail">{{ t('system.orgTemplate.details') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<!-- 已有字段表 -->
|
||||
<TemplateManagementTable
|
||||
v-if="fieldType === 'custom'"
|
||||
ref="templateFieldTableRef"
|
||||
v-model:select-data="selectData"
|
||||
:data="(totalTemplateField as DefinedFieldItem[])"
|
||||
:enable-third-part="templateForm.enableThirdPart"
|
||||
mode="project"
|
||||
@update="updateHandler"
|
||||
/>
|
||||
<!-- 缺陷详情表 -->
|
||||
<a-form
|
||||
v-if="fieldType === 'detail'"
|
||||
ref="defectFormRef"
|
||||
class="rounded-[4px]"
|
||||
:model="defectForm"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item
|
||||
class="max-w-[732px]"
|
||||
field="name"
|
||||
:label="t('system.orgTemplate.defectName')"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.defectNamePlaceholder') }]"
|
||||
required
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-input
|
||||
v-model="defectForm.name"
|
||||
:max-length="255"
|
||||
:placeholder="t('system.orgTemplate.defectNamePlaceholder')"
|
||||
allow-clear
|
||||
></a-input>
|
||||
<MsFormItemSub :text="t('system.orgTemplate.defectNameTip')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="precondition"
|
||||
:label="t('system.orgTemplate.defectContent')"
|
||||
asterisk-position="end"
|
||||
class="max-w-[732px]"
|
||||
>
|
||||
<MsRichText v-model:raw="defectForm.description" />
|
||||
<MsFormItemSub :text="t('system.orgTemplate.defectContentTip')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<!-- 预览模式 -->
|
||||
<PreviewTemplate
|
||||
v-else
|
||||
:select-field="(selectData as DefinedFieldItem[])"
|
||||
:template-type="route.query.type"
|
||||
:defect-form="defectForm"
|
||||
/>
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @description 系统管理-项目-模板-模板管理-创建&编辑
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
import TemplateManagementTable from '@/views/setting/organization/template/components/templateManagementTable.vue';
|
||||
import PreviewTemplate from '@/views/setting/organization/template/components/viewTemplate.vue';
|
||||
|
||||
import {
|
||||
createProjectTemplateInfo,
|
||||
getProjectFieldList,
|
||||
getProjectTemplateInfo,
|
||||
updateProjectTemplateInfo,
|
||||
} from '@/api/modules/setting/template';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import { useAppStore } from '@/store';
|
||||
import { sleep } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
|
||||
import type { ActionTemplateManage, CustomField, DefinedFieldItem } from '@/models/setting/template';
|
||||
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import {
|
||||
getCardList,
|
||||
getCustomDetailFields,
|
||||
getTemplateName,
|
||||
getTotalFieldOptionList,
|
||||
} from '@/views/setting/organization/template/components/fieldSetting';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
|
||||
setState(false);
|
||||
|
||||
const title = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const initTemplateForm: ActionTemplateManage = {
|
||||
id: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
scopeId: currentProjectId.value,
|
||||
enableThirdPart: false,
|
||||
};
|
||||
|
||||
const fieldType = ref<string>('custom'); // 缺陷模板字段类型
|
||||
|
||||
const templateForm = ref<ActionTemplateManage>({ ...initTemplateForm });
|
||||
|
||||
const selectData = ref<DefinedFieldItem[]>([]); // 表格已选择字段
|
||||
const selectFiled = ref<DefinedFieldItem[]>([]);
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
||||
const isEdit = computed(() => !!route.query.id);
|
||||
const isEditField = ref<boolean>(false);
|
||||
const systemFieldData = ref<CustomField[]>([]);
|
||||
|
||||
// 获取模板详情
|
||||
const getTemplateInfo = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getProjectTemplateInfo(route.query.id as string);
|
||||
const { name, customFields, systemFields } = res;
|
||||
templateForm.value = {
|
||||
...res,
|
||||
name: route.params.mode === 'copy' ? `copy_${name}` : name,
|
||||
};
|
||||
if (route.params.mode === 'copy') {
|
||||
templateForm.value.id = undefined;
|
||||
}
|
||||
selectData.value = getCustomDetailFields(totalTemplateField.value as DefinedFieldItem[], customFields);
|
||||
systemFieldData.value = systemFields;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理表单数据格式
|
||||
const getFieldOptionList = () => {
|
||||
totalTemplateField.value = getTotalFieldOptionList(totalTemplateField.value as DefinedFieldItem[]);
|
||||
// 创建默认系统字段
|
||||
if (!isEdit.value && !isEditField.value) {
|
||||
selectData.value = totalTemplateField.value.filter((item) => item.internal);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取字段列表数据
|
||||
const getClassifyField = async () => {
|
||||
try {
|
||||
totalTemplateField.value = await getProjectFieldList({
|
||||
scopedId: currentProjectId.value,
|
||||
scene: route.query.type,
|
||||
});
|
||||
getFieldOptionList();
|
||||
// 编辑字段就需要单独处理过滤
|
||||
if (isEditField.value) {
|
||||
selectData.value = totalTemplateField.value.filter(
|
||||
(item) => selectFiled.value.map((it) => it.id).indexOf(item.id) > -1
|
||||
);
|
||||
}
|
||||
if (isEdit.value) {
|
||||
getTemplateInfo();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
if (isEdit.value && route.params.mode === 'copy') {
|
||||
title.value = t('system.orgTemplate.copyTemplate', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
getClassifyField();
|
||||
} else if (isEdit.value) {
|
||||
title.value = t('system.orgTemplate.editTemplateType', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
getClassifyField();
|
||||
} else {
|
||||
title.value = t('system.orgTemplate.createTemplateType', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const initDefectForm = {
|
||||
name: '',
|
||||
description: '',
|
||||
};
|
||||
// 缺陷详情字段
|
||||
const defectForm = ref<Record<string, any>>({ ...initDefectForm });
|
||||
|
||||
// 获取模板参数
|
||||
function getTemplateParams(): ActionTemplateManage {
|
||||
const result = selectData.value.map((item) => {
|
||||
if (item.formRules?.length) {
|
||||
const { value } = item.formRules[0];
|
||||
return {
|
||||
fieldId: item.id,
|
||||
required: item.required,
|
||||
apiFieldId: item.apiFieldId,
|
||||
defaultValue: value,
|
||||
};
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// 处理缺陷系统详情字段
|
||||
const sysDetailFields = Object.keys(defectForm.value).map((formKey: string) => {
|
||||
return {
|
||||
fieldId: formKey,
|
||||
defaultValue: defectForm.value[formKey],
|
||||
};
|
||||
});
|
||||
const { name, remark, enableThirdPart, id } = templateForm.value;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
remark,
|
||||
enableThirdPart,
|
||||
customFields: result as CustomField[],
|
||||
scopeId: currentProjectId.value,
|
||||
scene: route.query.type,
|
||||
systemFields: sysDetailFields,
|
||||
};
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
templateForm.value = { ...initTemplateForm };
|
||||
}
|
||||
|
||||
const isContinueFlag = ref(false);
|
||||
|
||||
// 保存回调
|
||||
async function save() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const params = getTemplateParams();
|
||||
if (isEdit.value && route.params.mode !== 'copy') {
|
||||
await updateProjectTemplateInfo(params);
|
||||
Message.success(t('system.orgTemplate.updateSuccess'));
|
||||
} else {
|
||||
await createProjectTemplateInfo(params);
|
||||
Message.success(t('system.orgTemplate.addSuccess'));
|
||||
}
|
||||
if (isContinueFlag.value) {
|
||||
resetForm();
|
||||
} else {
|
||||
await sleep(300);
|
||||
router.push({ name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT, query: route.query });
|
||||
setState(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存
|
||||
function saveHandler(isContinue = false) {
|
||||
isContinueFlag.value = isContinue;
|
||||
formRef.value?.validate().then((res) => {
|
||||
if (!res) {
|
||||
return save();
|
||||
}
|
||||
return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||
});
|
||||
}
|
||||
|
||||
// 是否预览模式
|
||||
const isPreview = ref<boolean>(true); // 默认非预览模式
|
||||
function togglePreview() {
|
||||
isPreview.value = !isPreview.value;
|
||||
}
|
||||
|
||||
// 计算当前级别title
|
||||
const breadTitle = computed(() => {
|
||||
const firstBreadTitle = getCardList('organization').find((item: any) => item.key === route.query.type)?.name;
|
||||
const ThirdBreadTitle = title.value;
|
||||
return {
|
||||
firstBreadTitle,
|
||||
ThirdBreadTitle,
|
||||
};
|
||||
});
|
||||
|
||||
// 更新面包屑标题
|
||||
const setBreadText = () => {
|
||||
const { breadcrumbList } = appStore;
|
||||
const { firstBreadTitle, ThirdBreadTitle } = breadTitle.value;
|
||||
if (firstBreadTitle) {
|
||||
breadcrumbList[0].locale = firstBreadTitle;
|
||||
if (appStore.breadcrumbList.length > 2) {
|
||||
breadcrumbList[2].locale = ThirdBreadTitle;
|
||||
}
|
||||
appStore.setBreadcrumbList(breadcrumbList);
|
||||
}
|
||||
};
|
||||
|
||||
// 字段表编辑更新表
|
||||
const updateHandler = (flag: boolean) => {
|
||||
isEditField.value = flag;
|
||||
selectFiled.value = selectData.value;
|
||||
getClassifyField();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => systemFieldData.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
defectForm.value = { ...initDefectForm };
|
||||
systemFieldData.value.forEach((item) => {
|
||||
defectForm.value[item.fieldId] = item.defaultValue;
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
// setBreadText();
|
||||
getClassifyField();
|
||||
if (!isEdit.value) {
|
||||
selectData.value = totalTemplateField.value.filter((item) => item.internal);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.rightBtn {
|
||||
:deep(.arco-btn-outline) {
|
||||
border-color: var(--color-text-input-border) !important;
|
||||
color: var(--color-text-1) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -301,11 +301,11 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
const routeName = ref<string>('');
|
||||
// 创建模板
|
||||
const createTemplate = () => {
|
||||
router.push({
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
name: routeName.value,
|
||||
query: {
|
||||
type: route.query.type,
|
||||
},
|
||||
|
@ -318,7 +318,7 @@
|
|||
// 编辑模板
|
||||
const editTemplate = (id: string) => {
|
||||
router.push({
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
name: routeName.value,
|
||||
query: {
|
||||
id,
|
||||
type: route.query.type,
|
||||
|
@ -332,7 +332,7 @@
|
|||
// 复制模板
|
||||
const copyTemplate = (id: string) => {
|
||||
router.push({
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
name: routeName.value,
|
||||
query: {
|
||||
id,
|
||||
type: route.query.type,
|
||||
|
@ -413,6 +413,13 @@
|
|||
onMounted(() => {
|
||||
fetchData();
|
||||
updateColumns();
|
||||
if (route.query.type === 'FUNCTIONAL') {
|
||||
routeName.value = ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_CASE_DETAIL;
|
||||
} else if (route.query.type === 'API') {
|
||||
routeName.value = ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_API_DETAIL;
|
||||
} else {
|
||||
routeName.value = ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT_API_DETAIL;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,713 @@
|
|||
<template>
|
||||
<MsCard
|
||||
:loading="loading"
|
||||
:title="title"
|
||||
:is-edit="isEdit && route.params.mode !== 'copy'"
|
||||
has-breadcrumb
|
||||
:hide-back="true"
|
||||
@save="saveHandler"
|
||||
@save-and-continue="saveHandler(true)"
|
||||
>
|
||||
<template #headerLeft>
|
||||
<a-alert v-if="templateForm.enableThirdPart && route.query.type === 'BUG'" class="mb-[16px] w-full">
|
||||
{{ t('system.orgTemplate.enableApiAlert') }}
|
||||
</a-alert>
|
||||
<a-form ref="formRef" class="mt-1 max-w-[710px]" :model="templateForm">
|
||||
<a-form-item
|
||||
v-if="!templateForm?.internal"
|
||||
field="name"
|
||||
asterisk-position="end"
|
||||
:hide-label="true"
|
||||
hide-asterisk
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.templateNameRules') }]"
|
||||
content-class="contentClass"
|
||||
class="mb-0 max-w-[710px]"
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="templateForm.name"
|
||||
:placeholder="t('system.orgTemplate.templateNamePlaceholder')"
|
||||
:max-length="255"
|
||||
class="max-w-[732px]"
|
||||
:disabled="templateForm?.internal"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<span v-else class="font-medium text-[var(--color-text-1)] underline">{{ templateForm.name }}</span>
|
||||
</a-form>
|
||||
</template>
|
||||
<template #headerRight>
|
||||
<div class="flex items-center">
|
||||
<!-- <a-select
|
||||
v-if="templateForm.enableThirdPart && route.query.type === 'BUG'"
|
||||
v-model="templateForm.platForm"
|
||||
class="!my-0 w-[240px]"
|
||||
:placeholder="t('system.orgTemplate.selectThirdPlatType')"
|
||||
>
|
||||
<a-option v-for="item of platFormList" :key="item.value" :value="item.value">{{ item.label }}</a-option>
|
||||
</a-select> -->
|
||||
<a-checkbox v-if="route.query.type === 'BUG'" v-model="templateForm.enableThirdPart" class="mx-2">{{
|
||||
t('system.orgTemplate.thirdParty')
|
||||
}}</a-checkbox>
|
||||
<MsTag size="large" class="cursor-pointer" theme="outline" @click="brash">
|
||||
<MsIcon class="text-[var(color-text-4)]" :size="16" type="icon-icon_reset_outlined" />
|
||||
</MsTag>
|
||||
</div>
|
||||
</template>
|
||||
<div class="wrapper-preview">
|
||||
<div class="preview-left pr-4">
|
||||
<DefectTemplateLeftContent v-if="route.query.type === 'BUG'" :defect-form="defectForm" />
|
||||
<CaseTemplateLeftContent v-else />
|
||||
</div>
|
||||
<div class="preview-right px-4">
|
||||
<!-- 自定义字段开始 -->
|
||||
<VueDraggable v-model="selectData" handle=".form" ghost-class="ghost" @change="changeDrag">
|
||||
<div v-for="(formItem, index) of selectData" :key="formItem.id" class="customWrapper">
|
||||
<div class="action">
|
||||
<span class="required">
|
||||
<a-checkbox
|
||||
v-model="formItem.required"
|
||||
class="mr-1"
|
||||
@change="(value) => changeState(value, formItem)"
|
||||
>{{ t('ms.assertion.mustInclude') }}</a-checkbox
|
||||
>
|
||||
</span>
|
||||
<div class="actionList">
|
||||
<a-tooltip :content="t('system.orgTemplate.toTop')">
|
||||
<MsIcon
|
||||
type="icon-icon_up_outlined"
|
||||
size="16"
|
||||
:class="getColor(index, 'top')"
|
||||
@click="moveField(formItem as DefinedFieldItem, 'top')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-divider direction="vertical" class="!m-0 !mx-2" />
|
||||
<a-tooltip :content="t('system.orgTemplate.toBottom')">
|
||||
<MsIcon
|
||||
:class="getColor(index, 'bottom')"
|
||||
type="icon-icon_down_outlined"
|
||||
size="16"
|
||||
@click="moveField(formItem as DefinedFieldItem, 'bottom')"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-divider v-if="!formItem.internal" direction="vertical" class="!m-0 !mx-2" />
|
||||
<a-tooltip :content="t('common.edit')">
|
||||
<MsIcon
|
||||
v-if="!formItem.internal"
|
||||
type="icon-icon_edit_outlined"
|
||||
size="16"
|
||||
@click="editField(formItem as DefinedFieldItem)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-divider v-if="!formItem.internal" direction="vertical" class="!m-0 !mx-2" />
|
||||
<a-tooltip :content="t('common.delete')">
|
||||
<MsIcon
|
||||
v-if="!formItem.internal"
|
||||
type="icon-icon_delete-trash_outlined"
|
||||
size="16"
|
||||
@click="deleteSelectedField(formItem as DefinedFieldItem)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="form"
|
||||
:class="{
|
||||
'hover:border-[var(--color-text-n8)]': activeIndex !== index,
|
||||
'activeStyle': activeIndex === index,
|
||||
}"
|
||||
@click="activeHandler(index)"
|
||||
>
|
||||
<!-- 表单 -->
|
||||
<MsFormCreate
|
||||
v-model:api="formItem.api"
|
||||
v-model:rule="formItem.formRules"
|
||||
:option="configOptions"
|
||||
@click="activeHandler(index)"
|
||||
/>
|
||||
<a-form
|
||||
v-if="templateForm.enableThirdPart && route.query.type === 'BUG'"
|
||||
:ref="(el: refItem) => setStepRefMap(el, formItem as DefinedFieldItem)"
|
||||
:model="formItem"
|
||||
>
|
||||
<a-form-item
|
||||
row-class="apiFieldIdClass"
|
||||
hide-asterisk
|
||||
hide-label
|
||||
field="apiFieldId"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.apiFieldNotEmpty') }]"
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="formItem.apiFieldId"
|
||||
:placeholder="t('system.orgTemplate.pleaseEnterAPITip')"
|
||||
class="mt-1"
|
||||
:max-length="255"
|
||||
/></a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
<!-- 自定义字段结束 -->
|
||||
<div class="flex items-center">
|
||||
<a-button class="mr-1 mt-1 px-0" type="text" @click="associatedField">
|
||||
<template #icon>
|
||||
<icon-plus class="text-[14px]" />
|
||||
</template>
|
||||
{{ t('system.orgTemplate.associatedField') }}
|
||||
</a-button>
|
||||
<a-tooltip :content="t('system.orgTemplate.associatedHasField')" placement="top" effect="dark">
|
||||
<IconQuestionCircle
|
||||
class="mr-8 mt-1 h-[16px] w-[16px] text-[--color-text-4] hover:text-[rgb(var(--primary-5))]"
|
||||
/>
|
||||
</a-tooltip>
|
||||
|
||||
<a-button class="mr-1 mt-1 px-0" type="text" :disabled="totalTemplateField.length >= 20" @click="createField">
|
||||
<template #icon>
|
||||
<icon-plus class="text-[14px]" />
|
||||
</template>
|
||||
{{ t('system.orgTemplate.addField') }}
|
||||
</a-button>
|
||||
<a-tooltip :content="t('system.orgTemplate.addFieldDesc')" placement="top" effect="dark">
|
||||
<IconQuestionCircle
|
||||
class="mt-1 h-[16px] w-[16px] text-[--color-text-4] hover:text-[rgb(var(--primary-5))]"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 添加字段到模板抽屉 -->
|
||||
<AddFieldToTemplateDrawer
|
||||
ref="fieldSelectRef"
|
||||
v-model:visible="showDrawer"
|
||||
:total-data="(totalTemplateField as DefinedFieldItem[])"
|
||||
:table-select-data="(selectData as DefinedFieldItem[])"
|
||||
:mode="props.mode"
|
||||
@confirm="confirmHandler"
|
||||
/>
|
||||
<EditFieldDrawer
|
||||
ref="fieldDrawerRef"
|
||||
v-model:visible="showFieldDrawer"
|
||||
:mode="props.mode"
|
||||
@success="updateFieldHandler"
|
||||
/>
|
||||
</div>
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @description 模板-创建模板&编辑模板-预览模板
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import AddFieldToTemplateDrawer from './addFieldToTemplateDrawer.vue';
|
||||
import CaseTemplateLeftContent from './caseTemplateLeftContent.vue';
|
||||
import DefectTemplateLeftContent from './defectTemplateLeftContent.vue';
|
||||
import EditFieldDrawer from './editFieldDrawer.vue';
|
||||
|
||||
import {
|
||||
createOrganizeTemplateInfo,
|
||||
createProjectTemplateInfo,
|
||||
getFieldList,
|
||||
getOrganizeTemplateInfo,
|
||||
getProjectFieldList,
|
||||
getProjectTemplateInfo,
|
||||
updateOrganizeTemplateInfo,
|
||||
updateProjectTemplateInfo,
|
||||
} from '@/api/modules/setting/template';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import { useAppStore } from '@/store';
|
||||
import { sleep } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
|
||||
import type { ActionTemplateManage, CustomField, DefinedFieldItem, SeneType } from '@/models/setting/template';
|
||||
import { ProjectManagementRouteEnum, SettingRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { getTemplateName, getTotalFieldOptionList } from './fieldSetting';
|
||||
import { FormInstance } from '@arco-design/web-vue/es/form';
|
||||
|
||||
const props = defineProps<{
|
||||
mode: 'organization' | 'project';
|
||||
}>();
|
||||
|
||||
type refItem = Element | ComponentPublicInstance | null;
|
||||
const appStore = useAppStore();
|
||||
const currentOrgId = computed(() => appStore.currentOrgId);
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
|
||||
setState(false);
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const title = ref('');
|
||||
const initTemplateForm: ActionTemplateManage = {
|
||||
id: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
scopeId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value,
|
||||
enableThirdPart: false,
|
||||
platForm: '',
|
||||
};
|
||||
|
||||
const initBugForm = {
|
||||
name: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
const defectForm = ref({ ...initBugForm });
|
||||
|
||||
const isEdit = computed(() => !!route.query.id);
|
||||
const loading = ref(false);
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const templateForm = ref<ActionTemplateManage>({ ...initTemplateForm });
|
||||
const isContinueFlag = ref(false);
|
||||
|
||||
const platFormList = ref<{ value: string; label: string }[]>([
|
||||
{
|
||||
value: 'zental',
|
||||
label: '禅道',
|
||||
},
|
||||
{
|
||||
value: 'JIRA',
|
||||
label: 'JIRA',
|
||||
},
|
||||
]);
|
||||
|
||||
const templateApiMaps: Record<string, any> = {
|
||||
organization: {
|
||||
fieldList: getFieldList,
|
||||
create: createOrganizeTemplateInfo,
|
||||
update: updateOrganizeTemplateInfo,
|
||||
detail: getOrganizeTemplateInfo,
|
||||
},
|
||||
project: {
|
||||
fieldList: getProjectFieldList,
|
||||
create: createProjectTemplateInfo,
|
||||
update: updateProjectTemplateInfo,
|
||||
detail: getProjectTemplateInfo,
|
||||
},
|
||||
};
|
||||
|
||||
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
||||
const selectData = ref<DefinedFieldItem[]>([]);
|
||||
|
||||
// 获取模板参数
|
||||
function getTemplateParams(): ActionTemplateManage {
|
||||
const result = selectData.value.map((item) => {
|
||||
if (item.formRules?.length) {
|
||||
const { value } = item.formRules[0];
|
||||
return {
|
||||
fieldId: item.id,
|
||||
required: item.required,
|
||||
apiFieldId: item.apiFieldId || '',
|
||||
defaultValue: value || '',
|
||||
};
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const { name, remark, enableThirdPart, id } = templateForm.value;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
remark,
|
||||
enableThirdPart,
|
||||
customFields: result as CustomField[],
|
||||
scopeId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value,
|
||||
scene: route.query.type,
|
||||
};
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
templateForm.value = { ...initTemplateForm };
|
||||
}
|
||||
|
||||
// 保存回调
|
||||
async function save() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const params = getTemplateParams();
|
||||
if (isEdit.value && route.params.mode !== 'copy') {
|
||||
await templateApiMaps[props.mode].update(params);
|
||||
Message.success(t('system.orgTemplate.updateSuccess'));
|
||||
} else {
|
||||
await templateApiMaps[props.mode].create(params);
|
||||
Message.success(t('system.orgTemplate.addSuccess'));
|
||||
}
|
||||
if (isContinueFlag.value) {
|
||||
resetForm();
|
||||
} else {
|
||||
await sleep(300);
|
||||
if (props.mode === 'organization') {
|
||||
router.push({ name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, query: route.query });
|
||||
} else {
|
||||
router.push({ name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_TEMPLATE_MANAGEMENT, query: route.query });
|
||||
}
|
||||
|
||||
setState(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
const refStepMap: Record<string, any> = {};
|
||||
function setStepRefMap(el: refItem, formItem: DefinedFieldItem) {
|
||||
if (el) {
|
||||
refStepMap[`${formItem.id}`] = el;
|
||||
}
|
||||
}
|
||||
// 保存
|
||||
function saveHandler(isContinue = false) {
|
||||
isContinueFlag.value = isContinue;
|
||||
formRef.value?.validate().then((res) => {
|
||||
if (!res) {
|
||||
const allValidatePromises = Object.keys(refStepMap).map((key) => {
|
||||
return refStepMap[key].validate();
|
||||
});
|
||||
|
||||
Promise.all(allValidatePromises).then((results) => {
|
||||
const allValid = results.every((result) => !result);
|
||||
if (allValid) {
|
||||
return save();
|
||||
}
|
||||
});
|
||||
}
|
||||
return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||
});
|
||||
}
|
||||
|
||||
// 处理表单数据格式
|
||||
const getFieldOptionList = () => {
|
||||
totalTemplateField.value = getTotalFieldOptionList(totalTemplateField.value as DefinedFieldItem[]);
|
||||
selectData.value = totalTemplateField.value.filter((item) => item.internal);
|
||||
};
|
||||
|
||||
// 获取字段列表数据
|
||||
const getClassifyField = async () => {
|
||||
try {
|
||||
totalTemplateField.value = await templateApiMaps[props.mode].fieldList({
|
||||
scopedId: props.mode === 'organization' ? currentOrgId.value : currentProjectId.value,
|
||||
scene: route.query.type,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
if (isEdit.value && route.params.mode === 'copy') {
|
||||
title.value = t('system.orgTemplate.copyTemplate');
|
||||
} else if (isEdit.value) {
|
||||
title.value = t('system.orgTemplate.editTemplateType', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
} else {
|
||||
title.value = t('system.orgTemplate.createTemplateType', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
templateForm.value.name = title.value;
|
||||
}
|
||||
});
|
||||
|
||||
const showFieldDrawer = ref<boolean>(false);
|
||||
function createField() {
|
||||
showFieldDrawer.value = true;
|
||||
}
|
||||
|
||||
const showDrawer = ref<boolean>(false);
|
||||
function associatedField() {
|
||||
showDrawer.value = true;
|
||||
}
|
||||
const selectFiled = ref<DefinedFieldItem[]>([]);
|
||||
const selectedIds = ref<string[]>();
|
||||
|
||||
// 编辑更新已选择字段
|
||||
const isEditField = ref<boolean>(false);
|
||||
|
||||
// 添加字段或编辑字段
|
||||
const updateFieldHandler = async (editFlag: boolean, fieldId: string) => {
|
||||
selectFiled.value = selectData.value;
|
||||
isEditField.value = editFlag;
|
||||
await getClassifyField();
|
||||
totalTemplateField.value = getTotalFieldOptionList(totalTemplateField.value as DefinedFieldItem[]);
|
||||
// 编辑字段
|
||||
if (isEditField.value) {
|
||||
const index = selectData.value.findIndex((e: any) => e.id === fieldId);
|
||||
const newUpdateData = totalTemplateField.value.find((item: any) => item.id === fieldId);
|
||||
if (index > -1 && newUpdateData) {
|
||||
selectData.value.splice(index, 1);
|
||||
selectData.value.splice(index, 0, newUpdateData);
|
||||
}
|
||||
}
|
||||
// 新增字段
|
||||
if (!isEditField.value && fieldId) {
|
||||
const newUpdateData = totalTemplateField.value.find((item: any) => item.id === fieldId);
|
||||
if (newUpdateData) {
|
||||
selectData.value.push(newUpdateData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除已选择字段
|
||||
const deleteSelectedField = (record: DefinedFieldItem) => {
|
||||
selectData.value = selectData.value.filter((item) => item.id !== record.id);
|
||||
};
|
||||
const fieldDrawerRef = ref();
|
||||
// 编辑字段
|
||||
const editField = (record: DefinedFieldItem) => {
|
||||
showFieldDrawer.value = true;
|
||||
fieldDrawerRef.value.editHandler(record);
|
||||
};
|
||||
|
||||
// 确定处理字段表单数据
|
||||
const confirmHandler = (dataList: DefinedFieldItem[]) => {
|
||||
const selectFieldIds = selectData.value.map((e) => e.id);
|
||||
const newData = dataList.filter((item) => !selectFieldIds.includes(item.id));
|
||||
selectData.value = [...selectData.value, ...newData];
|
||||
};
|
||||
|
||||
function changeState(value: boolean | (string | number | boolean)[], formItem) {
|
||||
formItem.required = value;
|
||||
formItem.formRules[0].effect.required = value;
|
||||
}
|
||||
const systemFieldData = ref<CustomField[]>([]);
|
||||
|
||||
function getSelectData(customFields: DefinedFieldItem[]) {
|
||||
return customFields.map((item: any) => {
|
||||
const currentFormRules = FieldTypeFormRules[item.type];
|
||||
let selectOptions: any = [];
|
||||
if (item.options && item.options.length) {
|
||||
selectOptions = item.options.map((optionItem: any) => {
|
||||
return {
|
||||
label: optionItem.text,
|
||||
value: optionItem.value,
|
||||
};
|
||||
});
|
||||
currentFormRules.options = selectOptions;
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
id: item.fieldId,
|
||||
formRules: [
|
||||
{
|
||||
...currentFormRules,
|
||||
title: item.fieldName,
|
||||
effect: {
|
||||
required: item.required,
|
||||
},
|
||||
value: item.defaultValue,
|
||||
props: { ...currentFormRules.props, options: selectOptions, modelValue: item.defaultValue },
|
||||
},
|
||||
],
|
||||
fApi: null,
|
||||
required: item.required,
|
||||
};
|
||||
});
|
||||
}
|
||||
// 获取模板详情
|
||||
const getTemplateInfo = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await templateApiMaps[props.mode].detail(route.query.id as string);
|
||||
const { name, customFields, systemFields } = res;
|
||||
templateForm.value = {
|
||||
...res,
|
||||
name: route.params.mode === 'copy' ? `copy_${name}` : name,
|
||||
};
|
||||
if (route.params.mode === 'copy') {
|
||||
templateForm.value.id = undefined;
|
||||
}
|
||||
selectData.value = getSelectData(customFields);
|
||||
systemFieldData.value = systemFields;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
function moveField(formItem: DefinedFieldItem, type: string) {
|
||||
const moveIndex = selectData.value.findIndex((item: any) => item.id === formItem.id);
|
||||
if (type === 'top') {
|
||||
if (moveIndex === 0) {
|
||||
return;
|
||||
}
|
||||
selectData.value.splice(moveIndex, 1);
|
||||
selectData.value.splice(moveIndex - 1, 0, formItem);
|
||||
} else {
|
||||
if (moveIndex === selectData.value.length - 1) {
|
||||
return;
|
||||
}
|
||||
selectData.value.splice(moveIndex, 1);
|
||||
selectData.value.splice(moveIndex + 1, 0, formItem);
|
||||
}
|
||||
}
|
||||
|
||||
function getColor(index: number, type: string) {
|
||||
if (type === 'top' && index === 0) {
|
||||
return ['text-[rgb(var(--primary-3))]'];
|
||||
}
|
||||
if (type === 'bottom' && index === selectData.value.length - 1) {
|
||||
return ['text-[rgb(var(--primary-3))]'];
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getClassifyField();
|
||||
getFieldOptionList();
|
||||
if (isEdit.value) {
|
||||
getTemplateInfo();
|
||||
}
|
||||
});
|
||||
|
||||
const activeIndex = ref(-1);
|
||||
async function brash() {
|
||||
activeIndex.value = -1;
|
||||
await getClassifyField();
|
||||
getFieldOptionList();
|
||||
if (isEdit.value) {
|
||||
getTemplateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
function activeHandler(index: number) {
|
||||
activeIndex.value = index;
|
||||
}
|
||||
|
||||
function changeDrag() {
|
||||
activeIndex.value = -1;
|
||||
}
|
||||
const configOptions = ref({
|
||||
resetBtn: false,
|
||||
submitBtn: false,
|
||||
on: false,
|
||||
form: {
|
||||
layout: 'vertical',
|
||||
labelAlign: 'left',
|
||||
},
|
||||
row: {
|
||||
gutter: 0,
|
||||
},
|
||||
wrap: {
|
||||
'asterisk-position': 'end',
|
||||
'validate-trigger': ['change'],
|
||||
'row-class': 'selfClass',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.wrapper-preview {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.preview-left {
|
||||
width: 100%;
|
||||
border-right: 1px solid var(--color-text-n8);
|
||||
}
|
||||
.preview-right {
|
||||
padding-top: 8px;
|
||||
width: 428px;
|
||||
min-width: 428px;
|
||||
.customWrapper {
|
||||
position: relative;
|
||||
margin-bottom: 4px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6px;
|
||||
@apply flex flex-col justify-between;
|
||||
.form {
|
||||
padding: 8px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.form.activeStyle {
|
||||
border-color: rgb(var(--primary-5));
|
||||
background: var(--color-text-n9);
|
||||
}
|
||||
.action {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: 16px;
|
||||
z-index: 99999999 !important;
|
||||
background: white;
|
||||
opacity: 0;
|
||||
@apply flex items-center justify-end;
|
||||
.actionList {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
@apply flex items-center justify-center;
|
||||
}
|
||||
.required > .arco-checkbox {
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 10px -1px rgba(100 100 102/ 15%);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
border: 1px solid var(--color-text-n8);
|
||||
background: var(--color-text-n9);
|
||||
}
|
||||
&:hover > .action {
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover > .action > .actionList {
|
||||
color: rgb(var(--primary-5));
|
||||
box-shadow: 0 4px 10px -1px rgba(100 100 102/ 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.hoverStyle {
|
||||
.customWrapper:hover > .form {
|
||||
border-color: var(--color-text-n8) !important;
|
||||
}
|
||||
}
|
||||
.activeStyle {
|
||||
.customWrapper:hover .form {
|
||||
border-color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
:deep(.selfClass) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:deep(.contentClass > .arco-input-wrapper) {
|
||||
border-color: transparent;
|
||||
&:hover {
|
||||
border-color: var(--color-text-input-border);
|
||||
}
|
||||
&:hover > .arco-input {
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
& > .arco-input {
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
:deep(.contentClass > .arco-input-focus) {
|
||||
border-color: rgb(var(--primary-5));
|
||||
& > .arco-input {
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.ghost {
|
||||
border: 1px solid rgba(var(--primary-5));
|
||||
background-color: var(--color-text-n9);
|
||||
}
|
||||
:deep(.apiFieldIdClass) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<AddTemplate mode="organization" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AddTemplate from './addTemplate.vue';
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -252,6 +252,7 @@
|
|||
// 保存
|
||||
const confirmHandler = async (isContinue = false) => {
|
||||
try {
|
||||
drawerLoading.value = true;
|
||||
const formCopy = cloneDeep(fieldForm.value);
|
||||
|
||||
formCopy.scene = route.query.type;
|
||||
|
@ -287,13 +288,13 @@
|
|||
if (id) {
|
||||
params.id = id;
|
||||
}
|
||||
await addOrUpdate(params);
|
||||
const res = await addOrUpdate(params);
|
||||
Message.success(isEdit.value ? t('common.updateSuccess') : t('common.newSuccess'));
|
||||
if (!isContinue) {
|
||||
handleDrawerCancel();
|
||||
}
|
||||
resetForm();
|
||||
emit('success', isEdit.value);
|
||||
emit('success', isEdit.value, res.id);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
|
@ -302,30 +303,34 @@
|
|||
};
|
||||
const fieldDefaultValues = ref<FormItemModel[]>([]);
|
||||
function userFormFiledValidate(cb: () => Promise<any>) {
|
||||
fieldFormRef.value?.validate((errors: undefined | Record<string, ValidatedError>) => {
|
||||
fieldFormRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
batchFormRef.value?.formValidate(async (list: any) => {
|
||||
try {
|
||||
drawerLoading.value = true;
|
||||
fieldDefaultValues.value = [...list];
|
||||
if (showOptionsSelect) {
|
||||
fieldForm.value.options = (batchFormRef.value?.getFormResult() || []).map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
value: fieldForm.value.enableOptionKey ? item.value : getGenerateId(),
|
||||
};
|
||||
});
|
||||
if (showOptionsSelect.value) {
|
||||
batchFormRef.value?.formValidate(async (list: any) => {
|
||||
try {
|
||||
drawerLoading.value = true;
|
||||
fieldDefaultValues.value = [...list];
|
||||
if (showOptionsSelect.value) {
|
||||
fieldForm.value.options = (batchFormRef.value?.getFormResult() || []).map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
value: fieldForm.value.enableOptionKey ? item.value : getGenerateId(),
|
||||
};
|
||||
});
|
||||
}
|
||||
await cb();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
drawerLoading.value = false;
|
||||
}
|
||||
await cb();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
drawerLoading.value = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
await cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -342,21 +347,6 @@
|
|||
|
||||
// 字段类型列表选项
|
||||
const fieldOptions = ref<fieldIconAndNameModal[]>([]);
|
||||
|
||||
// 获取字段选项详情
|
||||
const getFieldDetail = async (id: string) => {
|
||||
try {
|
||||
const fieldDetail = await detail(id);
|
||||
fieldDefaultValues.value = fieldDetail.options.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理特殊情况编辑回显
|
||||
const getSpecialHandler = (itemType: FormItemType): FormItemType => {
|
||||
switch (itemType) {
|
||||
|
@ -377,16 +367,30 @@
|
|||
}
|
||||
};
|
||||
|
||||
// 获取字段选项详情
|
||||
const getFieldDetail = async (id: string) => {
|
||||
try {
|
||||
const fieldDetail = await detail(id);
|
||||
fieldForm.value = {
|
||||
...fieldDetail,
|
||||
type: getSpecialHandler(fieldDetail.type),
|
||||
};
|
||||
fieldDefaultValues.value = fieldDetail.options.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const editHandler = (item: AddOrUpdateField) => {
|
||||
showDrawer.value = true;
|
||||
isMultipleSelectMember.value = item.type === 'MULTIPLE_MEMBER';
|
||||
if (item.id) {
|
||||
getFieldDetail(item.id);
|
||||
fieldForm.value = {
|
||||
...item,
|
||||
type: getSpecialHandler(item.type),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -281,11 +281,15 @@ export const getTotalFieldOptionList = (totalData: DefinedFieldItem[]) => {
|
|||
formRules: [
|
||||
{
|
||||
...currentFormRules,
|
||||
title: item.name,
|
||||
effect: {
|
||||
required: false,
|
||||
},
|
||||
props: { ...currentFormRules.props, options: selectOptions },
|
||||
},
|
||||
],
|
||||
fApi: null,
|
||||
required: item.internal,
|
||||
required: false,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -305,6 +309,9 @@ export const getCustomDetailFields = (totalData: DefinedFieldItem[], customField
|
|||
return {
|
||||
...it,
|
||||
value: customFields[currentCustomFieldIndex].defaultValue,
|
||||
effect: {
|
||||
required: item.required,
|
||||
},
|
||||
};
|
||||
});
|
||||
const formItem = item;
|
||||
|
|
|
@ -1,368 +0,0 @@
|
|||
<template>
|
||||
<MsCard
|
||||
:loading="loading"
|
||||
:title="title"
|
||||
:is-edit="isEdit && route.params.mode !== 'copy'"
|
||||
has-breadcrumb
|
||||
@save="saveHandler"
|
||||
@save-and-continue="saveHandler(true)"
|
||||
>
|
||||
<template #headerRight>
|
||||
<div class="rightBtn">
|
||||
<a-button v-show="isPreview" type="outline" class="text-[var(--color-text-1)]" @click="togglePreview">{{
|
||||
t('system.orgTemplate.templatePreview')
|
||||
}}</a-button>
|
||||
<a-button v-show="!isPreview" type="outline" class="text-[var(--color-text-1)]" @click="togglePreview">{{
|
||||
t('system.orgTemplate.exitPreview')
|
||||
}}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 非预览模式 -->
|
||||
<div v-if="isPreview" class="nonPreview">
|
||||
<a-form ref="formRef" :model="templateForm" layout="vertical">
|
||||
<a-form-item
|
||||
:label="t('system.orgTemplate.templateName')"
|
||||
field="name"
|
||||
asterisk-position="end"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.templateNameRules') }]"
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="templateForm.name"
|
||||
:placeholder="t('system.orgTemplate.templateNamePlaceholder')"
|
||||
:max-length="255"
|
||||
class="max-w-[732px]"
|
||||
:disabled="templateForm?.internal"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" :label="t('system.orgTemplate.description')" asterisk-position="end">
|
||||
<a-textarea
|
||||
v-model="templateForm.remark"
|
||||
:max-length="1000"
|
||||
:placeholder="t('system.orgTemplate.resDescription')"
|
||||
:auto-size="{ minRows: 1 }"
|
||||
style="resize: vertical"
|
||||
class="max-w-[732px]"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="route.query.type === 'BUG'" field="remark" label="" asterisk-position="end"
|
||||
><a-checkbox v-model="templateForm.enableThirdPart">{{ t('system.orgTemplate.thirdParty') }}</a-checkbox>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="route.query.type === 'BUG'"
|
||||
field="fieldType"
|
||||
:label="t('system.orgTemplate.columnFieldType')"
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-radio-group v-model="fieldType" type="button">
|
||||
<a-radio value="custom">{{ t('system.orgTemplate.custom') }}</a-radio>
|
||||
<a-radio value="detail">{{ t('system.orgTemplate.details') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<!-- 已有字段表 -->
|
||||
<TemplateManagementTable
|
||||
v-if="fieldType === 'custom'"
|
||||
ref="templateFieldTableRef"
|
||||
v-model:select-data="selectData"
|
||||
:data="(totalTemplateField as DefinedFieldItem[])"
|
||||
:enable-third-part="templateForm.enableThirdPart"
|
||||
mode="organization"
|
||||
@update="updateHandler"
|
||||
/>
|
||||
<!-- 缺陷详情表 -->
|
||||
<a-form
|
||||
v-if="fieldType === 'detail'"
|
||||
ref="defectFormRef"
|
||||
class="rounded-[4px]"
|
||||
:model="defectForm"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item
|
||||
field="name"
|
||||
:label="t('system.orgTemplate.defectName')"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.defectNamePlaceholder') }]"
|
||||
required
|
||||
class="max-w-[732px]"
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-input
|
||||
v-model="defectForm.name"
|
||||
:max-length="255"
|
||||
:placeholder="t('system.orgTemplate.defectNamePlaceholder')"
|
||||
allow-clear
|
||||
></a-input>
|
||||
<MsFormItemSub :text="t('system.orgTemplate.defectNameTip')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="precondition"
|
||||
:label="t('system.orgTemplate.defectContent')"
|
||||
asterisk-position="end"
|
||||
class="max-w-[732px]"
|
||||
>
|
||||
<MsRichText v-model:raw="defectForm.description" />
|
||||
<MsFormItemSub :text="t('system.orgTemplate.defectContentTip')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<!-- 预览模式 -->
|
||||
<PreviewTemplate
|
||||
v-else
|
||||
:select-field="(selectData as DefinedFieldItem[])"
|
||||
:template-type="route.query.type"
|
||||
:defect-form="defectForm"
|
||||
/>
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @description 系统管理-组织-模板-模板管理-创建&编辑
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
import TemplateManagementTable from './templateManagementTable.vue';
|
||||
import PreviewTemplate from './viewTemplate.vue';
|
||||
|
||||
import {
|
||||
createOrganizeTemplateInfo,
|
||||
getFieldList,
|
||||
getOrganizeTemplateInfo,
|
||||
updateOrganizeTemplateInfo,
|
||||
} from '@/api/modules/setting/template';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||
import { useAppStore } from '@/store';
|
||||
import { sleep } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
|
||||
import type { ActionTemplateManage, CustomField, DefinedFieldItem } from '@/models/setting/template';
|
||||
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { getCardList, getCustomDetailFields, getTemplateName, getTotalFieldOptionList } from './fieldSetting';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const currentOrgId = computed(() => appStore.currentOrgId);
|
||||
const { setState } = useLeaveUnSaveTip();
|
||||
|
||||
setState(false);
|
||||
|
||||
const title = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const initTemplateForm: ActionTemplateManage = {
|
||||
id: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
scopeId: currentOrgId.value,
|
||||
enableThirdPart: false,
|
||||
};
|
||||
|
||||
const fieldType = ref<string>('custom'); // 缺陷模板字段类型
|
||||
|
||||
const templateForm = ref<ActionTemplateManage>({ ...initTemplateForm });
|
||||
|
||||
const selectData = ref<DefinedFieldItem[]>([]); // 表格已选择字段
|
||||
const selectFiled = ref<DefinedFieldItem[]>([]);
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
||||
const isEdit = computed(() => !!route.query.id);
|
||||
const isEditField = ref<boolean>(false);
|
||||
const systemFieldData = ref<CustomField[]>([]);
|
||||
|
||||
// 获取模板详情
|
||||
const getTemplateInfo = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getOrganizeTemplateInfo(route.query.id as string);
|
||||
const { name, customFields, systemFields } = res;
|
||||
templateForm.value = {
|
||||
...res,
|
||||
name: route.params.mode === 'copy' ? `copy_${name}` : name,
|
||||
};
|
||||
if (route.params.mode === 'copy') {
|
||||
templateForm.value.id = undefined;
|
||||
}
|
||||
selectData.value = getCustomDetailFields(totalTemplateField.value as DefinedFieldItem[], customFields);
|
||||
systemFieldData.value = systemFields;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理表单数据格式
|
||||
const getFieldOptionList = () => {
|
||||
totalTemplateField.value = getTotalFieldOptionList(totalTemplateField.value as DefinedFieldItem[]);
|
||||
// 创建默认系统字段
|
||||
if (!isEdit.value && !isEditField.value) {
|
||||
selectData.value = totalTemplateField.value.filter((item) => item.internal);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取字段列表数据
|
||||
const getClassifyField = async () => {
|
||||
try {
|
||||
totalTemplateField.value = await getFieldList({ scopedId: currentOrgId.value, scene: route.query.type });
|
||||
getFieldOptionList();
|
||||
// 编辑字段需要单独处理过滤
|
||||
if (isEditField.value) {
|
||||
selectData.value = totalTemplateField.value.filter(
|
||||
(item) => selectFiled.value.map((it) => it.id).indexOf(item.id) > -1
|
||||
);
|
||||
}
|
||||
if (isEdit.value) {
|
||||
getTemplateInfo();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
if (isEdit.value && route.params.mode === 'copy') {
|
||||
title.value = t('system.orgTemplate.copyTemplate');
|
||||
getClassifyField();
|
||||
} else if (isEdit.value) {
|
||||
title.value = t('system.orgTemplate.editTemplateType', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
getClassifyField();
|
||||
} else {
|
||||
title.value = t('system.orgTemplate.createTemplateType', {
|
||||
type: getTemplateName('organization', route.query.type as string),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const defectForm = ref<Record<string, any>>({}); // 缺陷详情表单
|
||||
|
||||
// 获取模板参数
|
||||
function getTemplateParams(): ActionTemplateManage {
|
||||
const result = selectData.value.map((item) => {
|
||||
if (item.formRules?.length) {
|
||||
const { value } = item.formRules[0];
|
||||
return {
|
||||
fieldId: item.id,
|
||||
required: item.required,
|
||||
apiFieldId: item.apiFieldId || '',
|
||||
defaultValue: value || '',
|
||||
};
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// 处理缺陷系统详情字段
|
||||
const sysDetailFields = Object.keys(defectForm.value).map((formKey: string) => {
|
||||
return {
|
||||
fieldId: formKey,
|
||||
defaultValue: defectForm.value[formKey],
|
||||
};
|
||||
});
|
||||
const { name, remark, enableThirdPart, id } = templateForm.value;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
remark,
|
||||
enableThirdPart,
|
||||
customFields: result as CustomField[],
|
||||
scopeId: currentOrgId.value,
|
||||
scene: route.query.type,
|
||||
systemFields: sysDetailFields,
|
||||
};
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
templateForm.value = { ...initTemplateForm };
|
||||
}
|
||||
|
||||
const isContinueFlag = ref(false);
|
||||
|
||||
// 保存回调
|
||||
async function save() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const params = getTemplateParams();
|
||||
if (isEdit.value && route.params.mode !== 'copy') {
|
||||
await updateOrganizeTemplateInfo(params);
|
||||
Message.success(t('system.orgTemplate.updateSuccess'));
|
||||
} else {
|
||||
await createOrganizeTemplateInfo(params);
|
||||
Message.success(t('system.orgTemplate.addSuccess'));
|
||||
}
|
||||
if (isContinueFlag.value) {
|
||||
resetForm();
|
||||
} else {
|
||||
await sleep(300);
|
||||
router.push({ name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, query: route.query });
|
||||
setState(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存
|
||||
function saveHandler(isContinue = false) {
|
||||
isContinueFlag.value = isContinue;
|
||||
formRef.value?.validate().then((res) => {
|
||||
if (!res) {
|
||||
return save();
|
||||
}
|
||||
return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||
});
|
||||
}
|
||||
|
||||
// 是否预览模式
|
||||
const isPreview = ref<boolean>(true); // 默认非预览模式
|
||||
function togglePreview() {
|
||||
isPreview.value = !isPreview.value;
|
||||
}
|
||||
|
||||
// 字段表编辑更新表
|
||||
const updateHandler = (flag: boolean) => {
|
||||
isEditField.value = flag;
|
||||
selectFiled.value = selectData.value;
|
||||
getClassifyField();
|
||||
};
|
||||
|
||||
// 缺陷详情表单回显
|
||||
watch(
|
||||
() => systemFieldData.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
systemFieldData.value.forEach((item) => {
|
||||
defectForm.value[item.fieldId] = item.defaultValue;
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getClassifyField();
|
||||
if (!isEdit.value) {
|
||||
selectData.value = totalTemplateField.value.filter((item) => item.internal);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.rightBtn {
|
||||
:deep(.arco-btn-outline) {
|
||||
border-color: var(--color-text-input-border) !important;
|
||||
color: var(--color-text-1) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -230,10 +230,12 @@
|
|||
await loadList();
|
||||
};
|
||||
|
||||
const routeName = ref<string>('');
|
||||
|
||||
// 创建模板
|
||||
const createTemplate = () => {
|
||||
router.push({
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
name: routeName.value,
|
||||
query: {
|
||||
type: route.query.type,
|
||||
},
|
||||
|
@ -246,7 +248,7 @@
|
|||
// 编辑模板
|
||||
const editTemplate = (id: string) => {
|
||||
router.push({
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
name: routeName.value,
|
||||
query: {
|
||||
id,
|
||||
type: route.query.type,
|
||||
|
@ -260,7 +262,7 @@
|
|||
// 复制模板
|
||||
const copyTemplate = (id: string) => {
|
||||
router.push({
|
||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||
name: routeName.value,
|
||||
query: {
|
||||
id,
|
||||
type: route.query.type,
|
||||
|
@ -307,6 +309,13 @@
|
|||
onMounted(() => {
|
||||
fetchData();
|
||||
updateColumns();
|
||||
if (route.query.type === 'FUNCTIONAL') {
|
||||
routeName.value = SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_CASE_DETAIL;
|
||||
} else if (route.query.type === 'API') {
|
||||
routeName.value = SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_API_DETAIL;
|
||||
} else {
|
||||
routeName.value = SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_BUG_DETAIL;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,296 +0,0 @@
|
|||
<template>
|
||||
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<div class="flex items-center">
|
||||
<MsIcon v-if="!record.internal" :type="getIconType(record.type)?.iconName || ''" size="16" />
|
||||
<span class="ml-2">{{ record.name }}</span>
|
||||
<MsTag v-if="record.internal" size="small" class="ml-2">{{ t('system.orgTemplate.isSystem') }}</MsTag>
|
||||
</div>
|
||||
</template>
|
||||
<template #apiFieldId="{ record }">
|
||||
<a-input
|
||||
v-model="record.apiFieldId"
|
||||
:max-length="255"
|
||||
class="min-w-[200px] max-w-[300px]"
|
||||
:placeholder="t('system.orgTemplate.apiInputPlaceholder')"
|
||||
allow-clear
|
||||
/>
|
||||
</template>
|
||||
<template #defaultValue="{ record }">
|
||||
<div class="form-create-wrapper w-full">
|
||||
<MsFormCreate v-model:api="record.fApi" :rule="record.formRules" :option="configOptions" />
|
||||
</div>
|
||||
</template>
|
||||
<template #required="{ record }">
|
||||
<a-checkbox v-model="record.required"></a-checkbox>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<div class="flex flex-row flex-nowrap">
|
||||
<MsButton class="!mr-0" @click="editField(record)">{{ t('system.orgTemplate.edit') }}</MsButton>
|
||||
<a-divider v-if="!record.internal" direction="vertical" />
|
||||
<MsButton v-if="!record.internal" class="!mr-0" @click="deleteSelectedField(record)">{{
|
||||
t('system.orgTemplate.delete')
|
||||
}}</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<!-- 添加字段到模板抽屉 -->
|
||||
<AddFieldToTemplateDrawer
|
||||
ref="fieldSelectRef"
|
||||
v-model:visible="showDrawer"
|
||||
:total-data="(totalData as DefinedFieldItem[])"
|
||||
:table-select-data="(selectList as DefinedFieldItem[])"
|
||||
:mode="props.mode"
|
||||
@confirm="confirmHandler"
|
||||
@update-data="updateFieldHandler"
|
||||
/>
|
||||
<a-button class="mt-3 px-0" type="text" @click="addField">
|
||||
<template #icon>
|
||||
<icon-plus class="text-[14px]" />
|
||||
</template>
|
||||
{{ t('system.orgTemplate.createField') }}
|
||||
</a-button>
|
||||
<EditFieldDrawer
|
||||
ref="fieldDrawerRef"
|
||||
v-model:visible="showFieldDrawer"
|
||||
:mode="props.mode"
|
||||
@success="updateFieldHandler"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* @description 系统管理-组织-模板管理-创建模板-非缺陷模板自定义字段表格
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import AddFieldToTemplateDrawer from './addFieldToTemplateDrawer.vue';
|
||||
import EditFieldDrawer from './editFieldDrawer.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import type { DefinedFieldItem } from '@/models/setting/template';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
import { getIconType } from './fieldSetting';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
mode: 'organization' | 'project';
|
||||
enableThirdPart: boolean; // 是否对接第三方平台
|
||||
data: DefinedFieldItem[]; // 总字段数据
|
||||
selectData: Record<string, any>[]; // 选择数据
|
||||
}>(),
|
||||
{
|
||||
enableThirdPart: false,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(['update:select-data', 'update']);
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'system.orgTemplate.name',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
width: 300,
|
||||
fixed: 'left',
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.defaultValue',
|
||||
dataIndex: 'defaultValue',
|
||||
slotName: 'defaultValue',
|
||||
width: 300,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.required',
|
||||
dataIndex: 'required',
|
||||
slotName: 'required',
|
||||
width: 180,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.description',
|
||||
dataIndex: 'remark',
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.operation',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
];
|
||||
|
||||
function getApiColumns() {
|
||||
return {
|
||||
title: 'system.orgTemplate.apiFieldId',
|
||||
dataIndex: 'apiFieldId',
|
||||
slotName: 'apiFieldId',
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
};
|
||||
}
|
||||
|
||||
const tableRef = ref();
|
||||
|
||||
const { propsRes, propsEvent, setProps } = useTable(undefined, {
|
||||
tableKey: TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT_FIELD,
|
||||
columns,
|
||||
scroll: { x: '1800px' },
|
||||
selectable: false,
|
||||
noDisable: true,
|
||||
size: 'default',
|
||||
showSetting: false,
|
||||
showPagination: false,
|
||||
enableDrag: true,
|
||||
});
|
||||
|
||||
const configOptions = ref({
|
||||
resetBtn: false,
|
||||
submitBtn: false,
|
||||
on: false,
|
||||
form: {
|
||||
layout: 'vertical',
|
||||
labelAlign: 'left',
|
||||
},
|
||||
row: {
|
||||
gutter: 0,
|
||||
},
|
||||
wrap: {
|
||||
'asterisk-position': 'end',
|
||||
'validate-trigger': ['change'],
|
||||
'hide-label': true,
|
||||
'hide-asterisk': true,
|
||||
},
|
||||
});
|
||||
const showDrawer = ref<boolean>(false);
|
||||
const totalData = ref<DefinedFieldItem[]>([]);
|
||||
|
||||
watchEffect(() => {
|
||||
totalData.value = props.data;
|
||||
});
|
||||
|
||||
const selectList = ref<DefinedFieldItem[]>([]);
|
||||
|
||||
// 确定处理字段表单数据
|
||||
const confirmHandler = (dataList: DefinedFieldItem[]) => {
|
||||
selectList.value = dataList;
|
||||
};
|
||||
|
||||
const showFieldDrawer = ref<boolean>(false);
|
||||
const fieldDrawerRef = ref();
|
||||
|
||||
// 编辑字段
|
||||
const editField = (record: DefinedFieldItem) => {
|
||||
showFieldDrawer.value = true;
|
||||
fieldDrawerRef.value.editHandler(record);
|
||||
};
|
||||
|
||||
// 添加字段
|
||||
const addField = async () => {
|
||||
showDrawer.value = true;
|
||||
};
|
||||
|
||||
// 编辑更新已选择字段
|
||||
const updateFieldHandler = async (isEdit: boolean) => {
|
||||
emit('update', isEdit);
|
||||
};
|
||||
|
||||
// 删除已选择字段
|
||||
const deleteSelectedField = (record: DefinedFieldItem) => {
|
||||
selectList.value = selectList.value.filter((item) => item.id !== record.id);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => selectList.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
emit('update:select-data', val);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.selectData,
|
||||
(val) => {
|
||||
if (val) {
|
||||
selectList.value = val as DefinedFieldItem[];
|
||||
setProps({ data: val });
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(val) => {
|
||||
totalData.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
// 是否开启三方API
|
||||
watch(
|
||||
() => props.enableThirdPart,
|
||||
(val) => {
|
||||
if (val && route.query.type === 'BUG') {
|
||||
const result = [...columns.slice(0, 1), getApiColumns(), ...columns.slice(1)];
|
||||
tableRef.value.initColumn(result);
|
||||
} else {
|
||||
tableRef.value.initColumn(columns);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 获取拖拽数据
|
||||
const dragTableData = computed(() => {
|
||||
return propsRes.value.data;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => dragTableData.value,
|
||||
(val) => {
|
||||
selectList.value = dragTableData.value as DefinedFieldItem[];
|
||||
emit('update:select-data', val);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-create-wrapper {
|
||||
:deep(.arco-form-item) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
:deep(.arco-picker) {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
.system-flag {
|
||||
background: var(--color-text-n8);
|
||||
@apply ml-2 rounded p-1 text-xs;
|
||||
}
|
||||
</style>
|
|
@ -189,4 +189,16 @@ export default {
|
|||
'system.orgTemplate.caseTemplateManagement': 'Use case template management',
|
||||
'system.orgTemplate.apiTemplateManagement': 'Interface template management',
|
||||
'system.orgTemplate.bugTemplateManagement': 'Defect template management',
|
||||
'system.orgTemplate.templateTypeName': ' {type}name',
|
||||
'system.orgTemplate.createCaseTemplate': 'Create a use case template',
|
||||
'system.orgTemplate.updateCaseTemplate': 'Update the use case template',
|
||||
'system.orgTemplate.createApiTemplate': 'Creating Api Template',
|
||||
'system.orgTemplate.updateApiTemplate': 'Update Api Template',
|
||||
'system.orgTemplate.createDefectTemplate': 'Create defect templates',
|
||||
'system.orgTemplate.updateDefectTemplate': 'Update defect templates',
|
||||
'system.orgTemplate.enableApiAlert':
|
||||
'After connecting to the third-party platform, you need to fill in the field API for the custom field. When using the template, the API field is not displayed',
|
||||
'system.orgTemplate.pleaseEnterAPITip': 'Please enter the field API',
|
||||
'system.orgTemplate.apiFieldNotEmpty': 'The field API cannot be empty',
|
||||
'system.orgTemplate.selectThirdPlatType': 'Please select the third party platform',
|
||||
};
|
||||
|
|
|
@ -180,4 +180,15 @@ export default {
|
|||
'system.orgTemplate.caseTemplateManagement': '用例模板管理',
|
||||
'system.orgTemplate.apiTemplateManagement': '接口模板管理',
|
||||
'system.orgTemplate.bugTemplateManagement': '缺陷模板管理',
|
||||
'system.orgTemplate.templateTypeName': ' {type}名称',
|
||||
'system.orgTemplate.createCaseTemplate': '创建用例模板',
|
||||
'system.orgTemplate.updateCaseTemplate': '更新用例模板',
|
||||
'system.orgTemplate.createApiTemplate': '创建接口模板',
|
||||
'system.orgTemplate.updateApiTemplate': '更新接口模板',
|
||||
'system.orgTemplate.createDefectTemplate': '创建缺陷模板',
|
||||
'system.orgTemplate.updateDefectTemplate': '更新缺陷模板',
|
||||
'system.orgTemplate.enableApiAlert': '对接第三方平台后,自定义字段需要填写字段 API,使用模版时,不显示 API 字段',
|
||||
'system.orgTemplate.pleaseEnterAPITip': '请输入字段API',
|
||||
'system.orgTemplate.apiFieldNotEmpty': '字段 API 不能为空',
|
||||
'system.orgTemplate.selectThirdPlatType': '请选择三方平台',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue