From a7538c1fad137c311306acebf4826422d5c5c4c4 Mon Sep 17 00:00:00 2001 From: "xinxin.wu" Date: Wed, 25 Oct 2023 12:42:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=B3=BB=E7=BB=9F=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E7=BB=84=E7=BB=87=E6=A8=A1=E6=9D=BF=E7=AE=A1=E7=90=86=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=92=8C=E5=B0=81=E8=A3=85=E8=A1=A8=E5=8D=95=E7=BA=A7?= =?UTF-8?q?=E8=81=94formCreate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/modules/setting/template.ts | 31 +- frontend/src/api/requrls/setting/template.ts | 2 + frontend/src/assets/style/arco-reset.less | 1 + .../business/ms-batch-modal/index.vue | 1 + .../business/ms-breadcrumb/index.vue | 2 +- .../pure/ms-form-create/form-create.ts | 163 ++++++++++ .../pure/ms-form-create/form-create.vue | 109 +++++++ .../pure/ms-form-create/formCreate.vue | 16 +- .../pure/ms-form-create/searchSelect.vue | 94 ++++++ .../components/pure/ms-form-create/types.ts | 77 +++++ .../components/pure/ms-popconfirm/index.vue | 10 +- .../pure/ms-rich-text/MsRichText.vue | 8 +- .../src/components/pure/ms-table/useTable.ts | 106 ++++--- frontend/src/enums/formCreateEnum.ts | 6 + frontend/src/enums/routeEnum.ts | 2 + frontend/src/enums/tableEnum.ts | 5 +- frontend/src/enums/templateEnum.ts | 12 + frontend/src/locale/en-US/common.ts | 1 + frontend/src/locale/en-US/index.ts | 3 + frontend/src/locale/zh-CN/common.ts | 1 + frontend/src/locale/zh-CN/index.ts | 3 + frontend/src/models/setting/template.ts | 69 +++- frontend/src/router/routes/modules/setting.ts | 48 ++- .../store/modules/form-create/form-create.ts | 109 +++++++ .../src/store/modules/setting/template.ts | 38 +++ .../projectVersion/index.vue | 156 +++++++++ .../components/addFieldToTemplateDrawer.vue | 295 ++++++++++++++++++ .../template/components/editFieldDrawer.vue | 116 +++---- .../template/components/fieldSetting.ts | 150 ++++++++- .../template/components/fieldSetting.vue | 112 +++---- .../template/components/templateDetail.vue | 207 ++++++++++++ .../template/components/templateItem.vue} | 31 +- .../components/templateManagement.vue | 210 +++++++++++++ .../components/templateManagementTable.vue | 290 +++++++++++++++++ .../template/components/viewTemplate.vue | 250 +++++++++++++++ .../setting/organization/template/index.vue | 22 +- .../organization/template/locale/en-US.ts | 39 ++- .../organization/template/locale/zh-CN.ts | 40 ++- .../pluginManager/components/tableExpand.vue | 12 +- .../system/pluginManager/locale/en-US.ts | 1 + .../system/pluginManager/locale/zh-CN.ts | 1 + 41 files changed, 2593 insertions(+), 256 deletions(-) create mode 100644 frontend/src/components/pure/ms-form-create/form-create.ts create mode 100644 frontend/src/components/pure/ms-form-create/form-create.vue create mode 100644 frontend/src/components/pure/ms-form-create/searchSelect.vue create mode 100644 frontend/src/components/pure/ms-form-create/types.ts create mode 100644 frontend/src/enums/formCreateEnum.ts create mode 100644 frontend/src/store/modules/form-create/form-create.ts create mode 100644 frontend/src/store/modules/setting/template.ts create mode 100644 frontend/src/views/setting/organization/template/components/addFieldToTemplateDrawer.vue create mode 100644 frontend/src/views/setting/organization/template/components/templateDetail.vue rename frontend/src/{components/business/ms-template-card/index.vue => views/setting/organization/template/components/templateItem.vue} (77%) create mode 100644 frontend/src/views/setting/organization/template/components/templateManagement.vue create mode 100644 frontend/src/views/setting/organization/template/components/templateManagementTable.vue create mode 100644 frontend/src/views/setting/organization/template/components/viewTemplate.vue diff --git a/frontend/src/api/modules/setting/template.ts b/frontend/src/api/modules/setting/template.ts index cd25556554..06be904969 100644 --- a/frontend/src/api/modules/setting/template.ts +++ b/frontend/src/api/modules/setting/template.ts @@ -9,20 +9,45 @@ import { GetFieldDetailUrl, GetOrganizeTemplateDetailUrl, GetOrganizeTemplateUrl, + GetProjectTemplateDetailUrl, + isEnableTemplateUrl, SetOrganizeTemplateUrl, UpdateFieldUrl, + UpdateOrganizeTemplateUrl, UpdateProjectTemplateUrl, } from '@/api/requrls/setting/template'; import { CommonList, TableQueryParams } from '@/models/common'; -import type { AddOrUpdateField, DefinedFieldItem, OrganizeTemplateItem } from '@/models/setting/template'; +import type { + ActionTemplateManage, + AddOrUpdateField, + DefinedFieldItem, + OrganizeTemplateItem, +} from '@/models/setting/template'; /** * * 模版 */ // 获取模版列表(组织) -export function getOrganizeTemplateList(organizationId: string, scene: string) { - return MSR.get({ url: GetOrganizeTemplateUrl, params: `/${organizationId}/${scene}` }); +export function getOrganizeTemplateList(params: TableQueryParams) { + return MSR.get({ url: `${GetOrganizeTemplateUrl}/${params.organizationId}/${params.scene}` }); +} +// 获取模版详情 +export function getOrganizeTemplateInfo(id: string) { + return MSR.get({ url: `${GetOrganizeTemplateDetailUrl}/${id}` }); +} +// 创建模板列表(组织) +export function createOrganizeTemplateInfo(data: ActionTemplateManage) { + return MSR.post({ url: `${CreateOrganizeTemplateUrl}`, data }); +} + +// 编辑模板列表(组织) +export function updateOrganizeTemplateInfo(data: ActionTemplateManage) { + return MSR.post({ url: `${UpdateOrganizeTemplateUrl}`, data }); +} +// 是否启用组织XX模板 +export function isEnableTemplate(organizationId: string, scene: string) { + return MSR.get({ url: `${isEnableTemplateUrl}/${organizationId}/${scene}` }); } /** * diff --git a/frontend/src/api/requrls/setting/template.ts b/frontend/src/api/requrls/setting/template.ts index 4547ad6787..637453bf1b 100644 --- a/frontend/src/api/requrls/setting/template.ts +++ b/frontend/src/api/requrls/setting/template.ts @@ -29,6 +29,8 @@ export const GetOrganizeTemplateDetailUrl = '/organization/template/get'; export const DeleteOrganizeTemplateUrl = '/organization/template/delete'; // 关闭组织模板,开启项目模版 export const EnableOrOffTemplateUrl = '/organization/template/disable'; +// 是否启用组织模板 +export const isEnableTemplateUrl = '/organization/template/is-enable'; // 系统设置-组织-自定义字段 diff --git a/frontend/src/assets/style/arco-reset.less b/frontend/src/assets/style/arco-reset.less index c8433e1191..035a0b1c9d 100644 --- a/frontend/src/assets/style/arco-reset.less +++ b/frontend/src/assets/style/arco-reset.less @@ -209,6 +209,7 @@ .arco-select-view, .arco-select-view-single, .arco-select { + width: 100%; border: 1px solid var(--color-text-input-border); background-color: var(--color-text-fff); &:not(:disabled):hover { diff --git a/frontend/src/components/business/ms-batch-modal/index.vue b/frontend/src/components/business/ms-batch-modal/index.vue index db56833462..70a2a11489 100644 --- a/frontend/src/components/business/ms-batch-modal/index.vue +++ b/frontend/src/components/business/ms-batch-modal/index.vue @@ -13,6 +13,7 @@ + + + + + +@/store/modules/form-create/form-create diff --git a/frontend/src/components/pure/ms-form-create/formCreate.vue b/frontend/src/components/pure/ms-form-create/formCreate.vue index 89f559cfc6..d56b68aa58 100644 --- a/frontend/src/components/pure/ms-form-create/formCreate.vue +++ b/frontend/src/components/pure/ms-form-create/formCreate.vue @@ -6,10 +6,12 @@ import { ref, watch, watchEffect } from 'vue'; import PassWord from './formcreate-password.vue'; + import SearchSelect from './searchSelect.vue'; import formCreate, { FormRule } from '@form-create/arco-design'; formCreate.component('PassWord', PassWord); + formCreate.component('SearchSelect', SearchSelect); const FormCreate = formCreate.$form(); const props = defineProps<{ @@ -21,7 +23,7 @@ const emits = defineEmits<{ (e: 'update:api', val: any): void; }>(); - const formApi = ref({}); + const formApi = ref({}); watchEffect(() => { formApi.value = props.api; @@ -32,6 +34,18 @@ emits('update:api', val); } ); + + const formRules = ref([]); + watchEffect(() => { + formRules.value = props.rule; + }); + watch( + () => props.rule, + (val) => { + formRules.value = val; + formApi.value?.refresh(); + } + ); diff --git a/frontend/src/components/pure/ms-form-create/searchSelect.vue b/frontend/src/components/pure/ms-form-create/searchSelect.vue new file mode 100644 index 0000000000..572302e2c9 --- /dev/null +++ b/frontend/src/components/pure/ms-form-create/searchSelect.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/frontend/src/components/pure/ms-form-create/types.ts b/frontend/src/components/pure/ms-form-create/types.ts new file mode 100644 index 0000000000..8ec4bae197 --- /dev/null +++ b/frontend/src/components/pure/ms-form-create/types.ts @@ -0,0 +1,77 @@ +import { FieldRule } from '@arco-design/web-vue'; + +import { FormRule } from '@form-create/arco-design'; + +export type FormItemType = + | 'INPUT' + | 'TEXTAREA' + | 'SELECT' + | 'MULTIPLE_SELECT' + | 'RADIO' + | 'CHECKBOX' + | 'MEMBER' + | 'MULTIPLE_MEMBER' + | 'DATE' + | 'DATETIME' + | 'INT' + | 'FLOAT' + | 'MULTIPLE_INPUT' + | 'INT' + | 'FLOAT' + | 'NUMBER'; + +// 表单选项 +export interface FormItemComplexCommonConfig { + options: { label: string; value: string | number; disabled: boolean }[]; // 选择器、复选框组、单选框组选项列表 + optionsRemoteMethodKey?: string; // 选择器、复选框组、单选框组选项列表远程搜索或初始化方法名 +} +export interface FormItemDefaultOptions { + text: string; + value: string; +} + +export type FormRuleItem = FormRule & { + props: Record; +}; +// 表单配置项 +export interface FormItem { + type: FormItemType; + name: string; // 表单项名称,作为唯一标志 --field + label: string; // 表单项文本 --- title + // 选择器的值绑定为Record,避免携带远程搜索时默认选中的选项不在 options 列表中,所以还需要设置 fallbackOptions + value: string | number | boolean | string[] | number[] | Record | Record[]; + subDesc?: string; // 表单项描述,在表单项下方显示 + inputSearch?: boolean; // 是否支持远程搜索 + tooltip?: ''; // 表单后边的提示info + instructionsIcon?: ''; // 是否有图片在表单后边展示 + optionMethod?: string; // 请求检索的方法 两个参数 表单项的所有值 + options?: FormItemDefaultOptions[]; + required: boolean; + validate?: FieldRule[]; + // 表单联动配置 + couplingConfig?: { + // 联动类型,visible:显示隐藏,disabled:禁用启用,filterOptions:过滤选项,disabledOptions:禁用选项,initOptions:初始化选项。都由联动的表单项触发 + type: 'visible' | 'disabled' | 'filterOptions' | 'disabledOptions' | 'initOptions'; // 目前初始化选项 + cascade: string; // 联动表单项名称 + matchRule: 'same' | 'includes' | 'excludes' | RegExp; // 联动匹配规则,same:值相同,includes:值包含,excludes:值不包含, RegExp:自定义匹配正则表达式 // 场景 目前只考虑等于情况 + }[]; + // 表单布局 + wrap?: Record; +} + +interface FomItemSelect extends FormItemComplexCommonConfig { + selectMultiple?: boolean; // 选择器是否多选 + selectMultipleLimit?: [number, number]; // 选择器多选时最少和最多可选项数,如:[1, 3],表示最少选1项,最多选3项;[0, 3]表示最多选3项,可不选;[1, 0]表示最少选1项,不限制最大可选数 +} + +interface FomItemCheckbox extends FormItemComplexCommonConfig { + checkedAll?: boolean; // 复选框组是否支持全选 + checkedAllLabel?: string; // 复选框组全选选项文本 + checkedMax?: number; // 复选框组最多可选项数 + direction?: 'horizontal' | 'vertical'; // 单选框组选项排列方向,默认为 'horizontal' +} + +interface FormItemRadio extends FormItemComplexCommonConfig { + type?: 'radio' | 'button'; // 单选框组选项排列方式,默认为 'radio' + direction?: 'horizontal' | 'vertical'; // 单选框组选项排列方向,默认为 'horizontal' +} diff --git a/frontend/src/components/pure/ms-popconfirm/index.vue b/frontend/src/components/pure/ms-popconfirm/index.vue index 1557336871..40470d0b4f 100644 --- a/frontend/src/components/pure/ms-popconfirm/index.vue +++ b/frontend/src/components/pure/ms-popconfirm/index.vue @@ -18,8 +18,8 @@ class="mr-[2px] text-xl text-[rgb(var(--danger-6))]" /> - - {{ characterLimit(props.title) || '' }} + + {{ props.title || '' }} @@ -64,7 +64,7 @@ {{ props.cancelText || t('common.cancel') }} - {{ props.isDelete ? t('common.remove') : props.okText || t('common.confirm') }} + {{ t(props.okText) || t('common.confirm') }} @@ -79,7 +79,6 @@ import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import { useI18n } from '@/hooks/useI18n'; - import { characterLimit } from '@/utils'; import type { FieldRule, FormInstance } from '@arco-design/web-vue'; @@ -113,6 +112,7 @@ { type: 'warning', isDelete: true, // 默认移除pop + okText: 'common.remove', } ); const emits = defineEmits<{ @@ -167,7 +167,7 @@ // 获取当前标题的样式 const titleClass = computed(() => { return props.isDelete - ? 'ml-2 font-[14px] text-[var(--color-text-1)]' + ? 'ml-2 font-medium text-[var(--color-text-1)] text-[14px]' : 'mb-[8px] font-medium text-[var(--color-text-1)] text-[14px]'; }); diff --git a/frontend/src/components/pure/ms-rich-text/MsRichText.vue b/frontend/src/components/pure/ms-rich-text/MsRichText.vue index 1bef2cadde..52af200a3a 100644 --- a/frontend/src/components/pure/ms-rich-text/MsRichText.vue +++ b/frontend/src/components/pure/ms-rich-text/MsRichText.vue @@ -135,7 +135,13 @@ + + diff --git a/frontend/src/components/pure/ms-table/useTable.ts b/frontend/src/components/pure/ms-table/useTable.ts index cdab1852ad..eacf226353 100644 --- a/frontend/src/components/pure/ms-table/useTable.ts +++ b/frontend/src/components/pure/ms-table/useTable.ts @@ -21,7 +21,7 @@ export interface Pagination { const appStore = useAppStore(); const tableStore = useTableStore(); export default function useTableProps( - loadListFunc: (v?: TableQueryParams | any) => Promise> | MsTableDataItem>, + loadListFunc?: (v?: TableQueryParams | any) => Promise> | MsTableDataItem>, props?: Partial>, // 数据处理的回调函数 dataTransform?: (item: TableData) => (TableData & T) | any, @@ -161,40 +161,42 @@ export default function useTableProps( const { current, pageSize } = propsRes.value.msPagination as Pagination; const { rowKey, selectorStatus, excludeKeys } = propsRes.value; try { - setLoading(true); - const data = await loadListFunc({ - current, - pageSize, - sort: sortItem.value, - filter: filterItem.value, - keyword: keyword.value, - ...loadListParams.value, - }); - const tmpArr = data.list; - propsRes.value.data = tmpArr.map((item: MsTableDataItem) => { - if (item.updateTime) { - item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'); - } - if (item.createTime) { - item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'); - } - if (dataTransform) { - item = dataTransform(item); - } - if (selectorStatus === SelectAllEnum.ALL) { - if (!excludeKeys.has(item[rowKey])) { - setTableSelected(item[rowKey]); + if (loadListFunc) { + setLoading(true); + const data = await loadListFunc({ + current, + pageSize, + sort: sortItem.value, + filter: filterItem.value, + keyword: keyword.value, + ...loadListParams.value, + }); + const tmpArr = data.list; + propsRes.value.data = tmpArr.map((item: MsTableDataItem) => { + if (item.updateTime) { + item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'); } + if (item.createTime) { + item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'); + } + if (dataTransform) { + item = dataTransform(item); + } + if (selectorStatus === SelectAllEnum.ALL) { + if (!excludeKeys.has(item[rowKey])) { + setTableSelected(item[rowKey]); + } + } + return item; + }); + if (data.total === 0) { + setTableErrorStatus('empty'); + } else { + setTableErrorStatus(false); } - return item; - }); - if (data.total === 0) { - setTableErrorStatus('empty'); - } else { - setTableErrorStatus(false); + setPagination({ current: data.current, total: data.total }); + return data; } - setPagination({ current: data.current, total: data.total }); - return data; } catch (err) { setTableErrorStatus('error'); } finally { @@ -208,26 +210,28 @@ export default function useTableProps( } else { // 没分页的情况下,直接调用loadListFunc try { - setLoading(true); - const data = await loadListFunc({ keyword: keyword.value, ...loadListParams.value }); - if (data.length === 0) { - setTableErrorStatus('empty'); - return; + if (loadListFunc) { + setLoading(true); + const data = await loadListFunc({ keyword: keyword.value, ...loadListParams.value }); + if (data.length === 0) { + setTableErrorStatus('empty'); + return; + } + setTableErrorStatus(false); + propsRes.value.data = data.map((item: MsTableDataItem) => { + if (item.updateTime) { + item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'); + } + if (item.createTime) { + item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'); + } + if (dataTransform) { + item = dataTransform(item); + } + return item; + }); + return data; } - setTableErrorStatus(false); - propsRes.value.data = data.map((item: MsTableDataItem) => { - if (item.updateTime) { - item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'); - } - if (item.createTime) { - item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'); - } - if (dataTransform) { - item = dataTransform(item); - } - return item; - }); - return data; } catch (err) { // eslint-disable-next-line no-console console.log(err); diff --git a/frontend/src/enums/formCreateEnum.ts b/frontend/src/enums/formCreateEnum.ts new file mode 100644 index 0000000000..4168541cbf --- /dev/null +++ b/frontend/src/enums/formCreateEnum.ts @@ -0,0 +1,6 @@ +export enum FormCreateKeyEnum { + ORGANIZE_TEMPLATE = 'OrganizeTemplate', + ORGANIZE_TEMPLATE_PREVIEW_TEMPLATE = 'OrganizeTemplatePreview', +} + +export default {}; diff --git a/frontend/src/enums/routeEnum.ts b/frontend/src/enums/routeEnum.ts index ef17510e21..8270313ba6 100644 --- a/frontend/src/enums/routeEnum.ts +++ b/frontend/src/enums/routeEnum.ts @@ -61,6 +61,8 @@ export enum SettingRouteEnum { SETTING_ORGANIZATION_PROJECT = 'settingOrganizationProject', SETTING_ORGANIZATION_TEMPLATE = 'settingOrganizationTemplate', SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING = 'settingOrganizationTemplateFiledSetting', + SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT = 'settingOrganizationTemplateManagement', + SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL = 'settingOrganizationTemplateManagementDetail', SETTING_ORGANIZATION_SERVICE = 'settingOrganizationService', SETTING_ORGANIZATION_LOG = 'settingOrganizationLog', } diff --git a/frontend/src/enums/tableEnum.ts b/frontend/src/enums/tableEnum.ts index 4d5110dec6..cd9c220c72 100644 --- a/frontend/src/enums/tableEnum.ts +++ b/frontend/src/enums/tableEnum.ts @@ -17,7 +17,10 @@ export enum TableKeyEnum { PROJECT_MEMBER = 'projectMember', PROJECT_USER_GROUP = 'projectUserGroup', ORGANIZATION_MEMBER = 'organizationMember', - ORGANIZATION_TEMPLATE = 'organizationTemplate', + ORGANIZATION_TEMPLATE_FIELD_SETTING = 'organizationTemplateFieldSetting', + ORGANIZATION_TEMPLATE_MANAGEMENT = 'organizationTemplateManagement', + ORGANIZATION_TEMPLATE_MANAGEMENT_FIELD = 'organizationTemplateManagementField', + ORGANIZATION_TEMPLATE_MANAGEMENT_STEP = 'organizationTemplateManagementStep', ORGANIZATION_PROJECT = 'organizationProject', ORGANIZATION_PROJECT_USER_DRAWER = 'organizationProjectUserDrawer', FILE_MANAGEMENT_FILE = 'fileManagementFile', diff --git a/frontend/src/enums/templateEnum.ts b/frontend/src/enums/templateEnum.ts index 574b6d6987..4b5dd36258 100644 --- a/frontend/src/enums/templateEnum.ts +++ b/frontend/src/enums/templateEnum.ts @@ -1,3 +1,4 @@ +// 模版展示字段icon export enum TemplateIconEnum { INPUT = 'icon-icon_input', // 输入框 TEXTAREA = 'icon-icon_style_one', // 文本 @@ -14,5 +15,16 @@ export enum TemplateIconEnum { FLOAT = 'icon-icon_pound', // 数字-浮点型 MULTIPLE_INPUT = 'icon-icon_tag', // 多值输入框、 NUMBER = 'icon-icon_pound', // 数字 + SYSTEM = 'icon-icon_pound', } + +// 模版列表图标卡片icon +export enum TemplateCardEnum { + FUNCTIONAL = 'caseTemplate', // 用例模版 + API = 'api_ui_Template', // ui模板 + UI = 'api_ui_Template', // API模板 + TEST_PLAN = 'testPlanTemplate', // 测试计划模板 + BUG = 'defectTemplate', // 缺陷模板 +} + export default {}; diff --git a/frontend/src/locale/en-US/common.ts b/frontend/src/locale/en-US/common.ts index f2d931f478..82f262739b 100644 --- a/frontend/src/locale/en-US/common.ts +++ b/frontend/src/locale/en-US/common.ts @@ -60,4 +60,5 @@ export default { 'common.allSelect': 'Select All', 'common.setting': 'Setting', 'common.resetDefault': 'Restore default', + 'common.pleaseSelect': 'Please Select', }; diff --git a/frontend/src/locale/en-US/index.ts b/frontend/src/locale/en-US/index.ts index 651fe5c4ce..6f20449086 100644 --- a/frontend/src/locale/en-US/index.ts +++ b/frontend/src/locale/en-US/index.ts @@ -54,6 +54,9 @@ export default { 'menu.settings.organization.project': 'Project', 'menu.settings.organization.template': 'Template', 'menu.settings.organization.templateFieldSetting': 'fieldSetting', + 'menu.settings.organization.templateManagementList': 'Template list', + 'menu.settings.organization.templateManagementEdit': 'Update Template', + 'menu.settings.organization.templateManagementDetail': 'Create Template', 'menu.settings.organization.serviceIntegration': 'Service Integration', 'menu.settings.organization.log': 'Log', 'navbar.action.locale': 'Switch to English', diff --git a/frontend/src/locale/zh-CN/common.ts b/frontend/src/locale/zh-CN/common.ts index d05d819a88..cb453de51d 100644 --- a/frontend/src/locale/zh-CN/common.ts +++ b/frontend/src/locale/zh-CN/common.ts @@ -64,4 +64,5 @@ export default { 'common.resetDefault': '恢复默认', 'common.tagPlaceholder': '添加标签回车结束', 'common.batchModify': '批量修改', + 'common.pleaseSelect': '请选择', }; diff --git a/frontend/src/locale/zh-CN/index.ts b/frontend/src/locale/zh-CN/index.ts index 01ab82d475..d6f1a03f76 100644 --- a/frontend/src/locale/zh-CN/index.ts +++ b/frontend/src/locale/zh-CN/index.ts @@ -54,6 +54,9 @@ export default { 'menu.settings.organization.serviceIntegration': '服务集成', 'menu.settings.organization.template': '模版', 'menu.settings.organization.templateFieldSetting': '字段设置', + 'menu.settings.organization.templateManagementList': '模版列表', + 'menu.settings.organization.templateManagementDetail': '创建模版', + 'menu.settings.organization.templateManagementEdit': '更新模板', 'menu.settings.organization.log': '日志', 'navbar.action.locale': '切换为中文', ...sys, diff --git a/frontend/src/models/setting/template.ts b/frontend/src/models/setting/template.ts index b45c90991b..244ac9b687 100644 --- a/frontend/src/models/setting/template.ts +++ b/frontend/src/models/setting/template.ts @@ -1,6 +1,10 @@ import { LocationQueryValue } from 'vue-router'; -// 模版管理 +import type { FormItem, FormItemType, FormRuleItem } from '@/components/pure/ms-form-create/types'; + +import { FormRule } from '@form-create/arco-design'; + +// 模版管理(组织) export interface OrganizeTemplateItem { id: string; name: string; @@ -18,21 +22,33 @@ export interface OrganizeTemplateItem { } export type SeneType = 'FUNCTIONAL' | 'BUG' | 'API' | 'UI' | 'TEST_PLAN' | LocationQueryValue[] | LocationQueryValue; + +export interface FieldOptions { + fieldId?: string; + value: string | string[] | number | number[]; + text: string; + internal?: boolean; +} + // 自定义字段 export interface DefinedFieldItem { id: string; name: string; scene: SeneType; // 使用场景 - type: string; + type: FormItemType; // 表单类型 remark: string; internal: boolean; // 是否是内置字段 scopeType: string; // 组织或项目级别字段(PROJECT, ORGANIZATION) createTime: number; updateTime: number; createUser: string; - refId: string; // 项目字段所关联的组织字段ID - enableOptionKey: boolean; // 是否需要手动输入选项key + refId: string | null; // 项目字段所关联的组织字段ID + enableOptionKey: boolean | null; // 是否需要手动输入选项key scopeId: string; // 组织或项目ID + options: FieldOptions[] | null; + required?: boolean | undefined; + fApi?: any; // 表单值 + formRules?: FormRuleItem[] | FormItem[] | FormRule[]; // 表单列表 } // 创建自定义字段 @@ -42,12 +58,55 @@ export interface FieldOption { text: string; } +// 新增 || 编辑参数 export interface AddOrUpdateField { id?: string; name: string; scene: SeneType; // 使用场景 - type: string; + type: FormItemType; remark: string; // 备注 scopeId: string; // 组织或项目ID options?: FieldOption[]; } + +export interface fieldIconAndNameModal { + key: string; + iconName: string; // 图标名称 + label: string; // 对应标签 +} + +// 模板管理列表(组织) +export interface OrdTemplateManagement { + id: string; + name: string; + remark: string; // 描述 + internal: boolean; // 是否是系统模板 + updateTime: number; + createTime: number; + createUser: string; // 创建人 + scopeType: string; // 组织或项目级别字段 + scopeId: string; // 组织或项目ID + enableThirdPart: boolean; // 是否开启api字段名配置 + enableDefault: boolean; // 是否是默认模板 + refId: string; // 项目模板所关联的组织模板ID + scene: string; // 使用场景 +} + +// 创建模板& 更新模板管理项 + +export interface CustomField { + fieldId: string; + required: boolean; // 是否必填 + apiFieldId: string; // api字段名 + defaultValue: string; // 默认值 +} + +export interface ActionTemplateManage { + id?: string; + name: string; + remark: string; + scopeId: string; + enableThirdPart?: boolean; // 是否开启api字段名配置 + scene: SeneType; + customFields: CustomField[]; +} diff --git a/frontend/src/router/routes/modules/setting.ts b/frontend/src/router/routes/modules/setting.ts index c3aadf9e03..50a132f2c3 100644 --- a/frontend/src/router/routes/modules/setting.ts +++ b/frontend/src/router/routes/modules/setting.ts @@ -188,7 +188,7 @@ const Setting: AppRouteRecordRaw = { isTopMenu: true, }, }, - // 模版字段设置 + // 模板列表-模版字段设置 { path: 'templateFiledSetting', name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING, @@ -209,6 +209,52 @@ const Setting: AppRouteRecordRaw = { ], }, }, + // 模版管理-模版列表 + { + path: 'templateManagement', + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, + component: () => import('@/views/setting/organization/template/components/templateManagement.vue'), + meta: { + locale: 'menu.settings.organization.templateManagementList', + roles: ['*'], + breadcrumbs: [ + { + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE, + locale: 'menu.settings.organization.template', + }, + { + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, + locale: 'menu.settings.organization.templateManagementList', + editLocale: 'menu.settings.organization.templateManagementList', + }, + ], + }, + }, + // 模板列表-模板管理-创建&编辑模版 + { + path: 'templateManagementDetail', + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL, + component: () => import('@/views/setting/organization/template/components/templateDetail.vue'), + meta: { + locale: 'menu.settings.organization.templateManagementDetail', + roles: ['*'], + breadcrumbs: [ + { + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE, + locale: 'menu.settings.organization.template', + }, + { + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, + locale: 'menu.settings.organization.templateManagementList', + }, + { + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, + locale: 'menu.settings.organization.templateManagementDetail', + editLocale: 'menu.settings.organization.templateManagementEdit', + }, + ], + }, + }, { path: 'log', name: SettingRouteEnum.SETTING_ORGANIZATION_LOG, diff --git a/frontend/src/store/modules/form-create/form-create.ts b/frontend/src/store/modules/form-create/form-create.ts new file mode 100644 index 0000000000..8ed6d196e4 --- /dev/null +++ b/frontend/src/store/modules/form-create/form-create.ts @@ -0,0 +1,109 @@ +import { defineStore } from 'pinia'; + +import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create'; +import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types'; + +import { FormCreateKeyEnum } from '@/enums/formCreateEnum'; + +const useFormCreateStore = defineStore('form-create', { + persist: false, + state: (): { + formRuleMap: Map; + formCreateRuleMap: Map; + } => ({ + formRuleMap: new Map(), + formCreateRuleMap: new Map(), + }), + actions: { + // 存储外边传递初始化数据格式存储form-item + setInitFormCreate(key: FormCreateKeyEnum[keyof FormCreateKeyEnum], formRule: FormItem[]) { + this.formRuleMap = new Map(); + this.formRuleMap.set(key, formRule); + }, + // 根据不同的类型初始化数据 + initFormCreateFormRules(key: FormCreateKeyEnum[keyof FormCreateKeyEnum]) { + const currentFormRule = this.formRuleMap.get(key); + // 处理数据结构 + const result = currentFormRule?.map((item: FormItem) => { + // 当前类型 + let fieldType; + const currentTypeForm = Object.keys(FieldTypeFormRules).find( + (formItemType: any) => item.type.toUpperCase() === formItemType + ); + if (currentTypeForm) { + fieldType = FieldTypeFormRules[currentTypeForm].type; + const options = item?.options; + const currentOptions = options?.map((optionsItem) => { + return { + label: optionsItem.text, + value: optionsItem.value, + }; + }); + return { + type: fieldType, // 表单类型 + field: item.name, // 字段 + title: item.label, // label 表单标签 + value: FieldTypeFormRules[currentTypeForm].value, // 目前的值 + effect: { + required: item.required, // 是否必填 + }, + // 级联关联到某一个form上 可能存在多个级联 + options: !item.optionMethod ? currentOptions : [], + link: item.couplingConfig?.map((cascadeItem: any) => cascadeItem.cascade), + rule: item.validate || [], + // 梳理表单所需要属性 + props: { + ...FieldTypeFormRules[currentTypeForm].props, + 'tooltip': item.tooltip, + // 表单后边展示图片 + 'instructionsIcon': item.instructionsIcon, + // 下拉选项请求 必须是开启远程搜索才有该方法 + 'subDesc': item.subDesc, + // 级联匹配规则 + 'couplingConfig': { ...item.couplingConfig }, + 'optionMethod': item.inputSearch && item.optionMethod ? item.optionMethod : '', + 'inputSearch': item.inputSearch, + 'allow-search': item.inputSearch, + 'keyword': '', + 'modelValue': item.value, + 'options': currentOptions, + }, + }; + } + return {}; + }); + if (result && result.length) { + this.setInitdRules(key, result as FormRuleItem[]); + } + }, + // 初始化好了的格式给formCreate + setInitdRules(key: FormCreateKeyEnum[keyof FormCreateKeyEnum], result: FormRuleItem[]) { + this.formCreateRuleMap.set(key, result); + }, + + /** ** + * @description 处理监视联动获取请求 + * @param key: 对应Map的Key + * @param item: 当前对应关联项-请求改变options + * @param formValueApi: 当前表单值实例可以获取表单的当前已经设置的值 + */ + async getOptions( + val: FormRuleItem, + key: FormCreateKeyEnum[keyof FormCreateKeyEnum], + cascadeItem: FormRuleItem, + formValueApi: any + ) { + const formValue = formValueApi.formData(); + // 设置自定义属性给到searchSelect + const formCreateRuleArr = this.formCreateRuleMap.get(key); + const formCreateItem = formCreateRuleArr?.find((items: FormRuleItem) => cascadeItem.field === items.field); + if (formCreateItem) { + formCreateItem.props.keyword = val.value; + formCreateItem.props.formValue = formValue; + } + }, + }, + getters: {}, +}); + +export default useFormCreateStore; diff --git a/frontend/src/store/modules/setting/template.ts b/frontend/src/store/modules/setting/template.ts new file mode 100644 index 0000000000..63d0b90313 --- /dev/null +++ b/frontend/src/store/modules/setting/template.ts @@ -0,0 +1,38 @@ +import { defineStore } from 'pinia'; + +import { isEnableTemplate } from '@/api/modules/setting/template'; + +import type { DefinedFieldItem } from '@/models/setting/template'; + +import useAppStore from '../app'; + +const useTemplateStore = defineStore('template', { + persist: true, + state: (): { templateStatus: Record; previewList: DefinedFieldItem[] } => ({ + templateStatus: { + FUNCTIONAL: false, + API: false, + UI: false, + TEST_PLAN: false, + BUG: false, + }, + previewList: [], + }), + actions: { + // 模板列表的状态 + setStatus() { + // 需要调整接口 + // const appStore = useAppStore(); + // Object.keys(this.templateStatus).forEach(async (item) => { + // const sceneStatus = await isEnableTemplate(appStore.currentOrgId, item); + // this.templateStatus[item] = sceneStatus; + // }); + }, + // 预览存储表数据 + setPreviewHandler(filedData: DefinedFieldItem[]) { + this.previewList = filedData; + }, + }, +}); + +export default useTemplateStore; diff --git a/frontend/src/views/project-management/projectAndPermission/projectVersion/index.vue b/frontend/src/views/project-management/projectAndPermission/projectVersion/index.vue index 29563f9126..18b0bb06a8 100644 --- a/frontend/src/views/project-management/projectAndPermission/projectVersion/index.vue +++ b/frontend/src/views/project-management/projectAndPermission/projectVersion/index.vue @@ -1,9 +1,165 @@ diff --git a/frontend/src/views/setting/organization/template/components/addFieldToTemplateDrawer.vue b/frontend/src/views/setting/organization/template/components/addFieldToTemplateDrawer.vue new file mode 100644 index 0000000000..f9e734bf31 --- /dev/null +++ b/frontend/src/views/setting/organization/template/components/addFieldToTemplateDrawer.vue @@ -0,0 +1,295 @@ + + + + + diff --git a/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue b/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue index 627cf3bc11..2e94ab99ac 100644 --- a/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue +++ b/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue @@ -22,14 +22,14 @@ - +
{{ item.label }}
{{ item.label }}
@@ -81,9 +82,7 @@
{{ item.label }}
@@ -108,6 +108,7 @@ import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue'; import MsDrawer from '@/components/pure/ms-drawer/index.vue'; + import type { FormItemType } from '@/components/pure/ms-form-create/types'; import MsBatchForm from '@/components/business/ms-batch-form/index.vue'; import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types'; @@ -116,10 +117,9 @@ import { useAppStore } from '@/store'; import { getGenerateId } from '@/utils'; - import type { AddOrUpdateField } from '@/models/setting/template'; - import { TemplateIconEnum } from '@/enums/templateEnum'; + import type { AddOrUpdateField, fieldIconAndNameModal } from '@/models/setting/template'; - import { getFieldType } from './fieldSetting'; + import { fieldIconAndName, getFieldType } from './fieldSetting'; const { t } = useI18n(); const route = useRoute(); @@ -136,7 +136,7 @@ const fieldFormRef = ref(); const initFieldForm: AddOrUpdateField = { name: '', - type: '', + type: 'INPUT', remark: '', scopeId: '', scene: 'FUNCTIONAL', @@ -144,19 +144,19 @@ }; const fieldForm = ref({ ...initFieldForm }); const isEdit = computed(() => !!fieldForm.value.id); - const selectFormat = ref(''); // 选择格式 + const selectFormat = ref(); // 选择格式 const isMultipleSelectMember = ref(false); // 成员多选 - const fieldType = ref(''); // 整体字段类型 + const fieldType = ref(); // 整体字段类型 // 是否展示选项添加面板 const showOptionsSelect = computed(() => { - const showOptionsType = ['RADIO', 'CHECKBOX', 'SELECT', 'MULTIPLE_SELECT']; - return showOptionsType.includes(fieldType.value); + const showOptionsType: FormItemType[] = ['RADIO', 'CHECKBOX', 'SELECT', 'MULTIPLE_SELECT']; + return showOptionsType.includes(fieldType.value as FormItemType); }); // 是否展示日期或数值 const showDateOrNumber = computed(() => { - return getFieldType(fieldType.value); + if (fieldType.value) return getFieldType(fieldType.value); }); // 批量表单-1.仅选项情况 @@ -174,9 +174,9 @@ const resetForm = () => { fieldForm.value = { ...initFieldForm }; - selectFormat.value = ''; + selectFormat.value = undefined; isMultipleSelectMember.value = false; - fieldType.value = ''; + fieldType.value = undefined; batchFormRef.value?.resetForm(); }; @@ -190,20 +190,23 @@ const confirmHandler = async (isContinue: boolean) => { try { drawerLoading.value = true; + if (fieldType.value) { + fieldForm.value.type = fieldType.value; + } + fieldForm.value.scene = route.query.type; - fieldForm.value = { - ...fieldForm.value, - scopeId: appStore.currentOrgId, - type: fieldType.value, - }; + fieldForm.value.scopeId = appStore.currentOrgId; + // 如果选择是日期或者数值 if (selectFormat.value) { fieldForm.value.type = selectFormat.value; } + // 如果选择是成员(单选||多选) if (isMultipleSelectMember.value) { fieldForm.value.type = isMultipleSelectMember.value ? 'MULTIPLE_MEMBER' : 'MEMBER'; } + // 如果选择是日期或者是数值 if (selectFormat.value) { fieldForm.value.type = selectFormat.value; @@ -212,6 +215,7 @@ // 处理参数 const { id, name, options, scopeId, scene, type, remark } = fieldForm.value; const params: AddOrUpdateField = { name, options, scopeId, scene, type, remark }; + if (isEdit) { params.id = id; } @@ -247,59 +251,7 @@ }; // 字段类型列表选项 - const fieldOptions = ref([ - { - id: 'TEXTAREA', - label: t('system.orgTemplate.textarea'), - value: TemplateIconEnum.TEXTAREA, - }, - { - id: 'INPUT', - label: t('system.orgTemplate.input'), - value: TemplateIconEnum.INPUT, - }, - { - id: 'RADIO', - label: t('system.orgTemplate.radio'), - value: TemplateIconEnum.RADIO, - }, - { - id: 'CHECKBOX', - label: t('system.orgTemplate.checkbox'), - value: TemplateIconEnum.CHECKBOX, - }, - { - id: 'SELECT', - label: t('system.orgTemplate.select'), - value: TemplateIconEnum.SELECT, - }, - { - id: 'MULTIPLE_SELECT', - label: t('system.orgTemplate.multipleSelect'), - value: TemplateIconEnum.MULTIPLE_SELECT, - }, - { - id: 'MEMBER', - label: t('system.orgTemplate.member'), - value: TemplateIconEnum.MEMBER, - }, - { - id: 'DATE', - label: t('system.orgTemplate.date'), - value: TemplateIconEnum.DATE, - }, - { - id: 'NUMBER', - label: t('system.orgTemplate.number'), - value: TemplateIconEnum.NUMBER, - }, - { - id: 'MULTIPLE_INPUT', - label: t('system.orgTemplate.multipleInput'), - value: TemplateIconEnum.MULTIPLE_INPUT, - }, - ]); - + const fieldOptions = ref([]); const fieldDefaultValues = ref([]); // 获取字段选项详情 @@ -317,7 +269,7 @@ }; // 处理特殊情况编辑回显 - const getSpecialHandler = (itemType: string): string => { + const getSpecialHandler = (itemType: FormItemType): FormItemType => { switch (itemType) { case 'INT': selectFormat.value = itemType; @@ -336,7 +288,7 @@ }; // 编辑 - const isEditHandler = (item: AddOrUpdateField) => { + const editHandler = (item: AddOrUpdateField) => { showDrawer.value = true; isMultipleSelectMember.value = item.type === 'MULTIPLE_MEMBER'; if (isEdit && item.id) { @@ -345,7 +297,7 @@ ...item, type: getSpecialHandler(item.type), }; - fieldType.value = getSpecialHandler(item.type); + fieldType.value = fieldForm.value.type; } }; @@ -368,9 +320,13 @@ showDrawer.value = val; } ); + onMounted(() => { + const excludeOptions = ['MULTIPLE_MEMBER', 'DATETIME', 'SYSTEM', 'INT', 'FLOAT']; + fieldOptions.value = fieldIconAndName.filter((item: any) => excludeOptions.indexOf(item.key) < 0); + }); defineExpose({ - isEditHandler, + editHandler, }); diff --git a/frontend/src/views/setting/organization/template/components/fieldSetting.ts b/frontend/src/views/setting/organization/template/components/fieldSetting.ts index 8b5a52c887..e94d798f59 100644 --- a/frontend/src/views/setting/organization/template/components/fieldSetting.ts +++ b/frontend/src/views/setting/organization/template/components/fieldSetting.ts @@ -1,8 +1,18 @@ -import { ref } from 'vue'; import dayjs from 'dayjs'; +import type { FormItemType } from '@/components/pure/ms-form-create/types'; + +import { useI18n } from '@/hooks/useI18n'; +import useTemplateStore from '@/store/modules/setting/template'; + +import type { fieldIconAndNameModal } from '@/models/setting/template'; +import { TemplateCardEnum, TemplateIconEnum } from '@/enums/templateEnum'; + +const { t } = useI18n(); +const templateStore = useTemplateStore(); + // 字段类型-日期 -const dateOptions = ref([ +const dateOptions = [ { label: dayjs().format('YYYY/MM/DD'), value: 'DATE', @@ -11,10 +21,10 @@ const dateOptions = ref([ label: dayjs().format('YYYY/MM/DD HH:mm:ss'), value: 'DATETIME', }, -]); +]; // 字段类型- 数字 -const numberTypeOptions = ref([ +const numberTypeOptions = [ { label: '整数', value: 'INT', @@ -23,17 +33,141 @@ const numberTypeOptions = ref([ label: '保留小数', value: 'FLOAT', }, -]); +]; -export const getFieldType = (selectFieldType: string) => { +// 获取字段类型是数值 || 日期 +export const getFieldType = (selectFieldType: FormItemType) => { switch (selectFieldType) { case 'DATE': - return dateOptions.value; + return dateOptions; case 'NUMBER': - return numberTypeOptions.value; + return numberTypeOptions; default: break; } }; +// 模板列表Icon +export const cardList = [ + { + id: 1001, + key: 'FUNCTIONAL', + value: TemplateCardEnum.FUNCTIONAL, + name: t('system.orgTemplate.caseTemplates'), + enable: templateStore.templateStatus.FUNCTIONAL, + }, + { + id: 1002, + key: 'API', + value: TemplateCardEnum.API, + name: t('system.orgTemplate.APITemplates'), + enable: templateStore.templateStatus.API, + }, + { + id: 1003, + key: 'UI', + value: TemplateCardEnum.UI, + name: t('system.orgTemplate.UITemplates'), + enable: templateStore.templateStatus.UI, + }, + { + id: 1004, + key: 'TEST_PLAN', + value: TemplateCardEnum.TEST_PLAN, + name: t('system.orgTemplate.testPlanTemplates'), + enable: templateStore.templateStatus.TEST_PLAN, + }, + { + id: 1005, + key: 'BUG', + value: TemplateCardEnum.BUG, + name: t('system.orgTemplate.defectTemplates'), + enable: templateStore.templateStatus.BUG, + }, +]; + +// table名称展示图标类型表格展示类型 +export const fieldIconAndName: fieldIconAndNameModal[] = [ + { + key: 'INPUT', + iconName: TemplateIconEnum.INPUT, + label: t('system.orgTemplate.input'), + }, + { + key: 'TEXTAREA', + iconName: TemplateIconEnum.TEXTAREA, + label: t('system.orgTemplate.textarea'), + }, + { + key: 'SELECT', + iconName: TemplateIconEnum.SELECT, + label: t('system.orgTemplate.select'), + }, + { + key: 'MULTIPLE_SELECT', + iconName: TemplateIconEnum.MULTIPLE_SELECT, + label: t('system.orgTemplate.multipleSelect'), + }, + { + key: 'RADIO', + iconName: TemplateIconEnum.RADIO, + label: t('system.orgTemplate.radio'), + }, + { + key: 'CHECKBOX', + iconName: TemplateIconEnum.CHECKBOX, + label: t('system.orgTemplate.checkbox'), + }, + { + key: 'MEMBER', + iconName: TemplateIconEnum.MEMBER, + label: t('system.orgTemplate.member'), + }, + { + key: 'MULTIPLE_MEMBER', + iconName: TemplateIconEnum.MULTIPLE_MEMBER, + label: t('system.orgTemplate.multipleMember'), + }, + { + key: 'DATE', + iconName: TemplateIconEnum.DATE, + label: t('system.orgTemplate.date'), + }, + { + key: 'DATETIME', + iconName: TemplateIconEnum.DATETIME, + label: t('system.orgTemplate.dateTime'), + }, + { + key: 'NUMBER', + iconName: TemplateIconEnum.NUMBER, + label: t('system.orgTemplate.number'), + }, + { + key: 'INT', + iconName: TemplateIconEnum.INT, + label: t('system.orgTemplate.number'), + }, + { + key: 'FLOAT', + iconName: TemplateIconEnum.FLOAT, + label: t('system.orgTemplate.number'), + }, + { + key: 'MULTIPLE_INPUT', + iconName: TemplateIconEnum.MULTIPLE_INPUT, + label: t('system.orgTemplate.multipleInput'), + }, + { + key: 'SYSTEM', + iconName: TemplateIconEnum.SYSTEM, + label: '', + }, +]; + +// 获取图标类型 +export const getIconType = (iconType: FormItemType) => { + return fieldIconAndName.find((item) => item.key === iconType); +}; + export default {}; diff --git a/frontend/src/views/setting/organization/template/components/fieldSetting.vue b/frontend/src/views/setting/organization/template/components/fieldSetting.vue index 3e85c6fcde..42701aa75f 100644 --- a/frontend/src/views/setting/organization/template/components/fieldSetting.vue +++ b/frontend/src/views/setting/organization/template/components/fieldSetting.vue @@ -1,9 +1,11 @@ + + diff --git a/frontend/src/components/business/ms-template-card/index.vue b/frontend/src/views/setting/organization/template/components/templateItem.vue similarity index 77% rename from frontend/src/components/business/ms-template-card/index.vue rename to frontend/src/views/setting/organization/template/components/templateItem.vue index 6607812b54..47f165bfae 100644 --- a/frontend/src/components/business/ms-template-card/index.vue +++ b/frontend/src/views/setting/organization/template/components/templateItem.vue @@ -3,7 +3,7 @@
- +
@@ -16,12 +16,14 @@ - {{ t('system.orgTemplate.TemplateManagement') }} + {{ t('system.orgTemplate.TemplateManagement') }} + - - {{ t('system.orgTemplate.workflowSetup') }} + + {{ t('system.orgTemplate.workflowSetup') }} + - +
@@ -58,14 +60,6 @@ }, ]); - const svgList = ref>({ - FUNCTIONAL: 'caseTemplate', - API: 'api_ui_Template', - UI: 'api_ui_Template', - TEST_PLAN: 'testPlanTemplate', - BUG: 'defectTemplate', - }); - const handleMoreActionSelect = (item: ActionsItem) => {}; // 字段设置 @@ -73,7 +67,16 @@ router.push({ name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING, query: { - type: props.cardItem.value, + type: props.cardItem.key, + }, + }); + }; + + const templateManagement = () => { + router.push({ + name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, + query: { + type: props.cardItem.key, }, }); }; diff --git a/frontend/src/views/setting/organization/template/components/templateManagement.vue b/frontend/src/views/setting/organization/template/components/templateManagement.vue new file mode 100644 index 0000000000..90f64a9b56 --- /dev/null +++ b/frontend/src/views/setting/organization/template/components/templateManagement.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/frontend/src/views/setting/organization/template/components/templateManagementTable.vue b/frontend/src/views/setting/organization/template/components/templateManagementTable.vue new file mode 100644 index 0000000000..83f9250e81 --- /dev/null +++ b/frontend/src/views/setting/organization/template/components/templateManagementTable.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/frontend/src/views/setting/organization/template/components/viewTemplate.vue b/frontend/src/views/setting/organization/template/components/viewTemplate.vue new file mode 100644 index 0000000000..fc5d65b4f2 --- /dev/null +++ b/frontend/src/views/setting/organization/template/components/viewTemplate.vue @@ -0,0 +1,250 @@ + + + + + diff --git a/frontend/src/views/setting/organization/template/index.vue b/frontend/src/views/setting/organization/template/index.vue index 2790b1646e..6402218296 100644 --- a/frontend/src/views/setting/organization/template/index.vue +++ b/frontend/src/views/setting/organization/template/index.vue @@ -35,25 +35,18 @@ import MsCard from '@/components/pure/ms-card/index.vue'; import MsCardList from '@/components/business/ms-card-list/index.vue'; - import TemplateItem from '@/components/business/ms-template-card/index.vue'; + import TemplateItem from './components/templateItem.vue'; import { useI18n } from '@/hooks/useI18n'; import useVisit from '@/hooks/useVisit'; + import useTemplateStore from '@/store/modules/setting/template'; + + import { cardList } from './components/fieldSetting'; + + const templateStore = useTemplateStore(); const { t } = useI18n(); - const cardList = ref([ - { - id: 1001, - value: 'FUNCTIONAL', - name: t('system.orgTemplate.caseTemplates'), - }, - { id: 1002, value: 'API', name: t('system.orgTemplate.APITemplates') }, - { id: 1003, value: 'UI', name: t('system.orgTemplate.UITemplates') }, - { id: 1004, value: 'TEST_PLAN', name: t('system.orgTemplate.testPlanTemplates') }, - { id: 1005, value: 'BUG', name: t('system.orgTemplate.defectTemplates') }, - ]); - const visitedKey = 'notRemind'; const { addVisited } = useVisit(visitedKey); const { getIsVisited } = useVisit(visitedKey); @@ -68,6 +61,9 @@ const doCheckIsTip = () => { isShowTip.value = !getIsVisited(); }; + onBeforeMount(() => { + templateStore.setStatus(); + }); onMounted(() => { doCheckIsTip(); diff --git a/frontend/src/views/setting/organization/template/locale/en-US.ts b/frontend/src/views/setting/organization/template/locale/en-US.ts index d0e92bc7ea..0dbc28e272 100644 --- a/frontend/src/views/setting/organization/template/locale/en-US.ts +++ b/frontend/src/views/setting/organization/template/locale/en-US.ts @@ -22,9 +22,11 @@ export default { 'system.orgTemplate.TemplateManagement': 'Management', 'system.orgTemplate.workflowSetup': 'Workflow', 'system.orgTemplate.enable': 'Enable', - 'system.orgTemplate.update': 'Update', - 'system.orgTemplate.addField': 'Add Field', + 'system.orgTemplate.update': 'Update Field', + 'system.orgTemplate.addField': 'Create Field', + 'system.orgTemplate.createField': 'Add Field', 'system.orgTemplate.enableDescription': 'The project template is enabled. Field Settings are inoperable', + 'system.orgTemplate.fieldLimit': 'You can add a maximum of 20 fields', 'system.orgTemplate.isSystem': 'System', 'system.orgTemplate.fieldList': 'Field list', 'system.orgTemplate.addOptions': 'Add an option', @@ -57,4 +59,37 @@ export default { 'system.orgTemplate.dateFormat': 'Date format', 'system.orgTemplate.formatPlaceholder': 'Please select a format', 'system.orgTemplate.optionsPlaceholder': 'Please enter options', + 'system.orgTemplate.columnTemplateName': 'Template name', + 'system.orgTemplate.copy': 'Copy', + 'system.orgTemplate.defaultValue': 'Default value', + 'system.orgTemplate.required': 'Required', + 'system.orgTemplate.enableTemplateTip': 'The project template is enabled. The organization template is unavailable', + 'system.orgTemplate.templateList': 'Template list', + 'system.orgTemplate.createTemplate': 'Create', + 'system.orgTemplate.templatePreview': 'Template preview', + 'system.orgTemplate.templateName': 'Template name', + 'system.orgTemplate.templateNamePlaceholder': 'Please enter a template name', + 'system.orgTemplate.optionalField': 'Optional Field', + 'system.orgTemplate.selectAll': 'select All', + 'system.orgTemplate.systemField': 'System Field', + 'system.orgTemplate.customField': 'Custom Field', + 'system.orgTemplate.selectedField': 'Selected Field', + 'system.orgTemplate.clear': 'Clear', + 'system.orgTemplate.addSuccess': 'create successfully', + 'system.orgTemplate.updateSuccess': 'update successfully', + 'system.orgTemplate.exitPreview': 'Exit Preview', + 'system.orgTemplate.useCaseStep': 'useCase Step', + 'system.orgTemplate.expectedResult': 'Expected Result', + 'system.orgTemplate.numberIndex': 'Index', + 'system.orgTemplate.addStep': 'Add Step', + 'system.orgTemplate.caseName': 'Use case name', + 'system.orgTemplate.caseNamePlaceholder': 'Please enter a use case name', + 'system.orgTemplate.precondition': 'precondition', + 'system.orgTemplate.stepDescription': 'Step description', + 'system.orgTemplate.changeType': 'Change type', + 'system.orgTemplate.textDescription': 'Text description', + 'system.orgTemplate.stepTip': 'Please enter steps', + 'system.orgTemplate.expectationTip': 'Please enter expectations', + 'system.orgTemplate.addAttachment': 'Add attachment', + 'system.orgTemplate.addAttachmentTip': 'Support any type of file, the file size does not exceed 500MB', }; diff --git a/frontend/src/views/setting/organization/template/locale/zh-CN.ts b/frontend/src/views/setting/organization/template/locale/zh-CN.ts index 7126700c33..28f7657fef 100644 --- a/frontend/src/views/setting/organization/template/locale/zh-CN.ts +++ b/frontend/src/views/setting/organization/template/locale/zh-CN.ts @@ -22,9 +22,11 @@ export default { 'system.orgTemplate.TemplateManagement': '模版管理', 'system.orgTemplate.workflowSetup': '工作流设置', 'system.orgTemplate.enable': '启用项目模版', - 'system.orgTemplate.update': '更新', + 'system.orgTemplate.update': '更新字段', 'system.orgTemplate.addField': '新增字段', + 'system.orgTemplate.createField': '添加字段', 'system.orgTemplate.enableDescription': '已启用项目模版,字段设置不可操作', + 'system.orgTemplate.fieldLimit': '最多可新增 20 个字段', 'system.orgTemplate.isSystem': '系统', 'system.orgTemplate.fieldList': '字段列表', 'system.orgTemplate.addOptions': '添加一个选项', @@ -56,4 +58,40 @@ export default { 'system.orgTemplate.dateFormat': '日期格式', 'system.orgTemplate.formatPlaceholder': '请选择格式', 'system.orgTemplate.optionsPlaceholder': '请输入选项', + 'system.orgTemplate.updateTip': '确认更新 `{name}` 吗?', + 'system.orgTemplate.updateDescription': '更新后,所使用该字段模版将一起修改', + 'system.orgTemplate.confirm': '确定', + 'system.orgTemplate.columnTemplateName': '模版名称', + 'system.orgTemplate.copy': '复制', + 'system.orgTemplate.defaultValue': '默认值', + 'system.orgTemplate.required': '是否必填', + 'system.orgTemplate.enableTemplateTip': '已启用项目模版,组织模版不可操作', + 'system.orgTemplate.templateList': '模板列表', + 'system.orgTemplate.createTemplate': '创建模版', + 'system.orgTemplate.templatePreview': '模板预览', + 'system.orgTemplate.templateName': '模板名称', + 'system.orgTemplate.templateNamePlaceholder': '请输入模版名称', + 'system.orgTemplate.optionalField': '可选字段', + 'system.orgTemplate.selectAll': '全选', + 'system.orgTemplate.systemField': '系统字段', + 'system.orgTemplate.customField': '自定义字段', + 'system.orgTemplate.selectedField': '已选字段', + 'system.orgTemplate.clear': '清空', + 'system.orgTemplate.addSuccess': '新增成功', + 'system.orgTemplate.updateSuccess': '更新成功', + 'system.orgTemplate.exitPreview': '退出预览', + 'system.orgTemplate.useCaseStep': '用例步骤', + 'system.orgTemplate.expectedResult': '预期结果', + 'system.orgTemplate.numberIndex': '序号', + 'system.orgTemplate.addStep': '添加步骤', + 'system.orgTemplate.caseName': '用例名称', + 'system.orgTemplate.caseNamePlaceholder': '请输入用例名称', + 'system.orgTemplate.precondition': '前置条件', + 'system.orgTemplate.stepDescription': '步骤描述', + 'system.orgTemplate.changeType': '更改类型', + 'system.orgTemplate.textDescription': '文本描述', + 'system.orgTemplate.stepTip': '请输入步骤', + 'system.orgTemplate.expectationTip': '请输入预期', + 'system.orgTemplate.addAttachment': '添加附件', + 'system.orgTemplate.addAttachmentTip': '支持任意类型文件,文件大小不超过 500MB', }; diff --git a/frontend/src/views/setting/system/pluginManager/components/tableExpand.vue b/frontend/src/views/setting/system/pluginManager/components/tableExpand.vue index 1c594a2d78..323f84243a 100644 --- a/frontend/src/views/setting/system/pluginManager/components/tableExpand.vue +++ b/frontend/src/views/setting/system/pluginManager/components/tableExpand.vue @@ -9,7 +9,7 @@
{{ index + 1 }} - {{ + {{ item.name }}
@@ -24,8 +24,16 @@ recordItem: PluginItem; }>(); const emit = defineEmits<{ - (e: 'MessageEvent', record: PluginItem, item: PluginForms): void; + (e: 'messageEvent', record: PluginItem, item: PluginForms): void; }>(); + + const originPluginId = ref(''); + const getScriptEmit = (record: PluginItem, item: PluginForms) => { + if (originPluginId.value !== item.id) { + emit('messageEvent', record, item); + originPluginId.value = item.id; + } + };