diff --git a/frontend/src/api/modules/setting/pluginManger.ts b/frontend/src/api/modules/setting/pluginManger.ts index 0dd615001e..dbae952315 100644 --- a/frontend/src/api/modules/setting/pluginManger.ts +++ b/frontend/src/api/modules/setting/pluginManger.ts @@ -2,12 +2,20 @@ import MSR from '@/api/http/index'; import { DeletePluginUrl, GetPluginListUrl, + GetPluginOptionsUrl, GetScriptUrl, UpdatePluginUrl, UploadPluginUrl, } from '@/api/requrls/setting/plugin'; -import type { AddReqData, PluginItem, PluginList, UpdatePluginModel, UploadFile } from '@/models/setting/plugin'; +import type { + AddReqData, + OptionsParams, + PluginItem, + PluginList, + UpdatePluginModel, + UploadFile, +} from '@/models/setting/plugin'; export function getPluginList() { return MSR.get({ url: GetPluginListUrl }); @@ -24,3 +32,8 @@ export function deletePluginReq(id: string) { export function getScriptDetail(pluginId: string, scriptId: string) { return MSR.get({ url: GetScriptUrl, params: `${pluginId}/${scriptId}` }); } + +// 获取插件下拉选项级联统一接口 +export function getPluginOptions(data: OptionsParams) { + return MSR.post<{ text: string; value: string }[]>({ url: GetPluginOptionsUrl, data }); +} diff --git a/frontend/src/api/modules/setting/template.ts b/frontend/src/api/modules/setting/template.ts index 06be904969..527f07cc73 100644 --- a/frontend/src/api/modules/setting/template.ts +++ b/frontend/src/api/modules/setting/template.ts @@ -46,8 +46,12 @@ 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}` }); +export function isEnableTemplate(organizationId: string) { + return MSR.get>({ url: `${isEnableTemplateUrl}/${organizationId}` }); +} +// 删除模板 +export function deleteOrdTemplate(id: string) { + return MSR.get({ url: `${DeleteOrganizeTemplateUrl}/${id}` }); } /** * diff --git a/frontend/src/api/requrls/setting/plugin.ts b/frontend/src/api/requrls/setting/plugin.ts index 82fbfb10be..e92f06893c 100644 --- a/frontend/src/api/requrls/setting/plugin.ts +++ b/frontend/src/api/requrls/setting/plugin.ts @@ -3,3 +3,4 @@ export const UploadPluginUrl = '/plugin/add'; export const UpdatePluginUrl = '/plugin/update'; export const DeletePluginUrl = '/plugin/delete'; export const GetScriptUrl = '/plugin/script/get'; +export const GetPluginOptionsUrl = '/plugin/options'; diff --git a/frontend/src/api/requrls/setting/template.ts b/frontend/src/api/requrls/setting/template.ts index 637453bf1b..d5a75029eb 100644 --- a/frontend/src/api/requrls/setting/template.ts +++ b/frontend/src/api/requrls/setting/template.ts @@ -30,7 +30,7 @@ export const DeleteOrganizeTemplateUrl = '/organization/template/delete'; // 关闭组织模板,开启项目模版 export const EnableOrOffTemplateUrl = '/organization/template/disable'; // 是否启用组织模板 -export const isEnableTemplateUrl = '/organization/template/is-enable'; +export const isEnableTemplateUrl = '/organization/template/enable/config'; // 系统设置-组织-自定义字段 diff --git a/frontend/src/assets/style/arco-reset.less b/frontend/src/assets/style/arco-reset.less index 4e9fd31b2b..90c2ee610e 100644 --- a/frontend/src/assets/style/arco-reset.less +++ b/frontend/src/assets/style/arco-reset.less @@ -359,6 +359,9 @@ border: 1px solid var(--color-text-input-border); } } +.arco-checkbox-disabled.arco-checkbox-checked .arco-checkbox-icon { + background: rgb(var(--primary-2)) !important ; +} /** radio **/ .arco-radio-group-button { diff --git a/frontend/src/components/pure/ms-form-create/form-create.ts b/frontend/src/components/pure/ms-form-create/form-create.ts index 0d1cf90471..7ebf1ba7e5 100644 --- a/frontend/src/components/pure/ms-form-create/form-create.ts +++ b/frontend/src/components/pure/ms-form-create/form-create.ts @@ -20,6 +20,7 @@ export const SELECT = { multiple: false, placeholder: '请选择', options: [], + modelValue: '', }, }; @@ -33,6 +34,7 @@ export const MULTIPLE_SELECT = { multiple: true, placeholder: '请选择', options: [], + modelValue: [], }, }; @@ -61,6 +63,7 @@ export const MEMBER = { props: { multiple: false, placeholder: '请选择', + modelValue: '', }, }; @@ -74,6 +77,7 @@ export const MULTIPLE_MEMBER = { multiple: true, placeholder: '请选择', options: [], + modelValue: [], }, }; diff --git a/frontend/src/components/pure/ms-form-create/form-create.vue b/frontend/src/components/pure/ms-form-create/form-create.vue index eea4f7ea7d..0dfff39698 100644 --- a/frontend/src/components/pure/ms-form-create/form-create.vue +++ b/frontend/src/components/pure/ms-form-create/form-create.vue @@ -1,5 +1,5 @@ diff --git a/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue b/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue index 2e94ab99ac..fced96f217 100644 --- a/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue +++ b/frontend/src/views/setting/organization/template/components/editFieldDrawer.vue @@ -4,7 +4,7 @@ :title="isEdit ? t('system.orgTemplate.update') : t('system.orgTemplate.addField')" :ok-text="t(isEdit ? 'system.orgTemplate.update' : 'system.orgTemplate.addField')" :ok-loading="drawerLoading" - :width="680" + :width="800" :show-continue="!isEdit" @confirm="handleDrawerConfirm" @continue="handleDrawerConfirm(true)" @@ -36,9 +36,9 @@ }" > - + +
+ 选项KEY值 +
@@ -82,7 +92,9 @@ (); @@ -136,14 +149,15 @@ const fieldFormRef = ref(); const initFieldForm: AddOrUpdateField = { name: '', - type: 'INPUT', + type: undefined, remark: '', scopeId: '', scene: 'FUNCTIONAL', options: [], + enableOptionKey: false, }; const fieldForm = ref({ ...initFieldForm }); - const isEdit = computed(() => !!fieldForm.value.id); + const isEdit = ref(false); const selectFormat = ref(); // 选择格式 const isMultipleSelectMember = ref(false); // 成员多选 const fieldType = ref(); // 整体字段类型 @@ -151,12 +165,12 @@ // 是否展示选项添加面板 const showOptionsSelect = computed(() => { const showOptionsType: FormItemType[] = ['RADIO', 'CHECKBOX', 'SELECT', 'MULTIPLE_SELECT']; - return showOptionsType.includes(fieldType.value as FormItemType); + return showOptionsType.includes(fieldForm.value.type as FormItemType); }); // 是否展示日期或数值 const showDateOrNumber = computed(() => { - if (fieldType.value) return getFieldType(fieldType.value); + if (fieldForm.value.type) return getFieldType(fieldForm.value.type); }); // 批量表单-1.仅选项情况 @@ -164,12 +178,43 @@ filed: 'text', type: 'input', label: '', - rules: [{ required: true, message: t('system.orgTemplate.optionContentRules') }], + rules: [ + { required: true, message: t('system.orgTemplate.optionContentRules') }, + { notRepeat: true, message: t('system.orgTemplate.optionsContentNoRepeat') }, + ], placeholder: t('system.orgTemplate.optionsPlaceholder'), hideAsterisk: true, hideLabel: true, }); - const optionsModels: Ref = ref([{ ...onlyOptions.value }]); + + // 批量表单-2 缺陷情况 + const bugBatchFormRules = ref([ + { + filed: 'text', + type: 'input', + label: '', + rules: [ + { required: true, message: t('system.orgTemplate.optionContentRules') }, + { notRepeat: true, message: t('system.orgTemplate.optionsContentNoRepeat') }, + ], + placeholder: 'system.orgTemplate.optionsPlaceholder', + hideAsterisk: true, + hideLabel: true, + }, + { + filed: 'value', + type: 'input', + label: '', + rules: [ + { required: true, message: t('system.orgTemplate.optionsIdTip') }, + { notRepeat: true, message: t('system.orgTemplate.optionsIdNoRepeat') }, + ], + placeholder: 'system.orgTemplate.optionsIdPlaceholder', + hideAsterisk: true, + hideLabel: true, + }, + ]); + const optionsModels: Ref = ref([]); const batchFormRef = ref(null); const resetForm = () => { @@ -190,42 +235,49 @@ 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.scopeId = appStore.currentOrgId; + const formCopy = cloneDeep(fieldForm.value); + + formCopy.scene = route.query.type; + formCopy.scopeId = appStore.currentOrgId; // 如果选择是日期或者数值 if (selectFormat.value) { - fieldForm.value.type = selectFormat.value; + formCopy.type = selectFormat.value; } // 如果选择是成员(单选||多选) if (isMultipleSelectMember.value) { - fieldForm.value.type = isMultipleSelectMember.value ? 'MULTIPLE_MEMBER' : 'MEMBER'; + formCopy.type = isMultipleSelectMember.value ? 'MULTIPLE_MEMBER' : 'MEMBER'; } // 如果选择是日期或者是数值 if (selectFormat.value) { - fieldForm.value.type = selectFormat.value; + formCopy.type = selectFormat.value; } // 处理参数 - const { id, name, options, scopeId, scene, type, remark } = fieldForm.value; - const params: AddOrUpdateField = { name, options, scopeId, scene, type, remark }; + const { id, name, options, scopeId, scene, type, remark, enableOptionKey } = formCopy; - if (isEdit) { + const params: AddOrUpdateField = { + name, + options, + scopeId, + scene, + type, + remark, + enableOptionKey, + }; + if (id) { params.id = id; } await addOrUpdateOrdField(params); - Message.success(isEdit ? t('common.addSuccess') : t('common.updateSuccess')); + Message.success(isEdit.value ? t('common.updateSuccess') : t('common.addSuccess')); if (!isContinue) { handleDrawerCancel(); } resetForm(); - emit('success'); + emit('success', isEdit.value); } catch (error) { console.log(error); } finally { @@ -241,7 +293,7 @@ fieldForm.value.options = (batchFormRef.value?.getFormResult() || []).map((item: any) => { return { ...item, - value: getGenerateId(), + value: fieldForm.value.enableOptionKey ? item.value : getGenerateId(), }; }); } @@ -291,13 +343,12 @@ const editHandler = (item: AddOrUpdateField) => { showDrawer.value = true; isMultipleSelectMember.value = item.type === 'MULTIPLE_MEMBER'; - if (isEdit && item.id) { + if (item.id) { getFieldDetail(item.id); fieldForm.value = { ...item, type: getSpecialHandler(item.type), }; - fieldType.value = fieldForm.value.type; } }; @@ -320,6 +371,27 @@ showDrawer.value = val; } ); + + watchEffect(() => { + if (fieldForm.value.id) { + isEdit.value = true; + } else { + isEdit.value = false; + } + }); + + // 监视是否显示KEY值 + watch( + () => fieldForm.value.enableOptionKey, + (val) => { + if (val && sceneType === 'BUG') { + optionsModels.value = cloneDeep(bugBatchFormRules.value); + } else { + optionsModels.value = [{ ...onlyOptions.value }]; + } + }, + { immediate: true } + ); onMounted(() => { const excludeOptions = ['MULTIPLE_MEMBER', 'DATETIME', 'SYSTEM', 'INT', 'FLOAT']; fieldOptions.value = fieldIconAndName.filter((item: any) => excludeOptions.indexOf(item.key) < 0); @@ -330,4 +402,10 @@ }); - + diff --git a/frontend/src/views/setting/organization/template/components/fieldSetting.vue b/frontend/src/views/setting/organization/template/components/fieldSetting.vue index 42701aa75f..ff2342cbf7 100644 --- a/frontend/src/views/setting/organization/template/components/fieldSetting.vue +++ b/frontend/src/views/setting/organization/template/components/fieldSetting.vue @@ -5,7 +5,7 @@ }}
{{ t('system.orgTemplate.fieldList') }} - + {{ t('system.orgTemplate.addField') }} (''); + const scene = ref(route.query.type); + + // 获取字段列表数据 const fetchData = async () => { scene.value = route.query.type; setLoadListParams({ organizationId: currentOrd, scene }); await loadList(); + totalData.value = await getFieldList({ organizationId: currentOrd, scene: route.query.type }); }; + const isDisabled = computed(() => { + return totalData.value.length > 20; + }); + const tableRef = ref(); - const isEnable = ref(templateStore.templateStatus[scene.value as string]); // 开始默认未启用 + const isEnable = computed(() => { + return templateStore.templateStatus[scene.value as string]; + }); // 开始默认未启用 // 切换模版是否启用展示操作列 const isEnableOperation = () => { @@ -218,13 +228,12 @@ const showDrawer = ref(false); const fieldDrawerRef = ref(); - const fieldHandler = (type: string, record?: AddOrUpdateField) => { + const fieldHandler = () => { showDrawer.value = true; - if (type === 'edit' && record) fieldDrawerRef.value.editHandler(record); }; const handleOk = (record: AddOrUpdateField) => { - fieldHandler('edit', record); + fieldDrawerRef.value.editHandler(record); }; const successHandler = () => { @@ -242,9 +251,9 @@ }; onMounted(() => { - fetchData(); - isEnableOperation(); updateBreadcrumbList(); + isEnableOperation(); + fetchData(); }); diff --git a/frontend/src/views/setting/organization/template/components/templateDetail.vue b/frontend/src/views/setting/organization/template/components/templateDetail.vue index 734055d437..2e78845bfa 100644 --- a/frontend/src/views/setting/organization/template/components/templateDetail.vue +++ b/frontend/src/views/setting/organization/template/components/templateDetail.vue @@ -2,7 +2,7 @@ + {{ t('system.orgTemplate.thirdParty') }} + - +
- + @@ -56,54 +65,92 @@ import { ref } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue'; + import { cloneDeep } from 'lodash-es'; import MsCard from '@/components/pure/ms-card/index.vue'; + import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create'; 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 useTemplateStore from '@/store/modules/setting/template'; import { sleep } from '@/utils'; import { scrollIntoView } from '@/utils/dom'; - import type { ActionTemplateManage, CustomField } from '@/models/setting/template'; + import type { ActionTemplateManage, CustomField, DefinedFieldItem } from '@/models/setting/template'; import { SettingRouteEnum } from '@/enums/routeEnum'; + import { cardList } from './fieldSetting'; + const { t } = useI18n(); const route = useRoute(); const router = useRouter(); const appStore = useAppStore(); - const templateStore = useTemplateStore(); - useLeaveUnSaveTip(); + // useLeaveUnSaveTip(); const title = ref(''); const loading = ref(false); - const initTemplateForm = { + const initTemplateForm: ActionTemplateManage = { + id: '', name: '', remark: '', + scopeId: appStore.currentOrgId, + enableThirdPart: false, }; - const templateForm = ref({ ...initTemplateForm }); + const templateForm = ref({ ...initTemplateForm }); + + const selectData = ref([]); // 表格已选择字段 + const selectFiled = ref([]); + + const formRef = ref(); + const totalTemplateField = ref([]); + const isEdit = computed(() => !!route.query.id); + const currentOrd = appStore.currentOrgId; + const isEditField = ref(false); - const tableFiledDetailList = ref([]); // 获取模板详情 const getTemplateInfo = async () => { try { loading.value = true; const res = await getOrganizeTemplateInfo(route.query.id as string); - const { name, remark, customFields, scoped, enableThirdPart, scene } = res; - templateForm.value.name = name; - templateForm.value.remark = remark; - // 处理 字段列表 - tableFiledDetailList.value = customFields; + const { name, customFields } = res; + templateForm.value = { + ...res, + name: route.params.mode === 'copy' ? `${name}_copy` : name, + }; + if (route.params.mode === 'copy') { + templateForm.value.id = undefined; + } + // 处理字段列表 + const customFieldsIds = customFields.map((index: any) => index.fieldId); + const result = totalTemplateField.value.filter((item) => { + const currentCustomFieldIndex = customFieldsIds.findIndex((it: any) => it === item.id); + if (customFieldsIds.indexOf(item.id) > -1) { + const currentForm = item.formRules?.map((it: any) => { + it.props.modelValue = customFields[currentCustomFieldIndex].defaultValue; + return { + ...it, + value: customFields[currentCustomFieldIndex].defaultValue, + }; + }); + const formItem = item; + formItem.formRules = cloneDeep(currentForm); + formItem.apiFieldId = customFields[currentCustomFieldIndex].apiFieldId; + formItem.required = customFields[currentCustomFieldIndex].required; + return true; + } + return false; + }); + selectData.value = result; } catch (error) { console.log(error); } finally { @@ -111,29 +158,88 @@ } }; - const isEdit = ref(false); - const templateFieldTableRef = ref(); - watchEffect(() => { - if (route.query.id) { + // 处理表单数据格式 + const getFieldOptionList = () => { + totalTemplateField.value = totalTemplateField.value.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, + formRules: [ + { ...currentFormRules, value: item.value, props: { ...currentFormRules.props, options: selectOptions } }, + ], + fApi: null, + required: item.internal, + }; + }); + // 创建默认系统字段 + if (!isEdit.value && !isEditField.value) { + selectData.value = totalTemplateField.value.filter((item) => item.internal); + } + }; + + // 获取字段列表数据 + const getClassifyField = async () => { + try { + totalTemplateField.value = await getFieldList({ organizationId: currentOrd, 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('menu.settings.organization.templateManagementEdit'); - isEdit.value = true; - getTemplateInfo(); + getClassifyField(); } else { title.value = t('menu.settings.organization.templateManagementDetail'); - isEdit.value = false; } }); - const formRef = ref(); - // 获取模板参数 function getTemplateParams(): ActionTemplateManage { - const result: CustomField[] = templateFieldTableRef.value.getCustomFields(); - const { name, remark } = templateForm.value; + 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, - customFields: result, + enableThirdPart, + customFields: result as CustomField[], scopeId: appStore.currentOrgId, scene: route.query.type, }; @@ -145,13 +251,14 @@ const isContinueFlag = ref(false); + // 保存回调 async function save() { try { loading.value = true; const params = getTemplateParams(); - if (isEdit.value) { + if (isEdit.value && route.params.mode !== 'copy') { await updateOrganizeTemplateInfo(params); - Message.success(t('system.resourcePool.updateSuccess')); + Message.success(t('system.orgTemplate.updateSuccess')); } else { await createOrganizeTemplateInfo(params); Message.success(t('system.orgTemplate.addSuccess')); @@ -160,7 +267,7 @@ resetForm(); } else { await sleep(300); - router.push({ name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL }); + router.push({ name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT, query: route.query }); } } catch (error) { console.log(error); @@ -170,30 +277,57 @@ } // 保存 - async function saveHandler(isContinue = false) { + function saveHandler(isContinue = false) { isContinueFlag.value = isContinue; - formRef.value?.validate(async (errors: Record | undefined) => { - if (!errors) { - save(); - } else { - return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' }); + formRef.value?.validate().then((res) => { + if (!res) { + return save(); } + return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' }); }); } - const selectFiledToTem = ref([]); // 非预览模式模板已选择字段 // 是否预览模式 const isPreview = ref(true); // 默认非预览模式 function togglePreview() { isPreview.value = !isPreview.value; - if (!isPreview.value) { - selectFiledToTem.value = templateFieldTableRef.value.getSelectFiled(); - templateStore.setPreviewHandler(selectFiledToTem.value); - } } + // 计算当前级别title + const breadTitle = computed(() => { + const firstBreadTitle = cardList.find((item) => 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(); + }; + onMounted(() => { - templateFieldTableRef.value.setDefaultField(); + setBreadText(); + getClassifyField(); + if (!isEdit.value) { + selectData.value = totalTemplateField.value.filter((item) => item.internal); + } }); diff --git a/frontend/src/views/setting/organization/template/components/templateItem.vue b/frontend/src/views/setting/organization/template/components/templateItem.vue index 47f165bfae..2a20566773 100644 --- a/frontend/src/views/setting/organization/template/components/templateItem.vue +++ b/frontend/src/views/setting/organization/template/components/templateItem.vue @@ -17,14 +17,14 @@ {{ t('system.orgTemplate.TemplateManagement') }} - + {{ t('system.orgTemplate.workflowSetup') }} - + - @@ -36,15 +36,19 @@ diff --git a/frontend/src/views/setting/organization/template/components/templateManagementTable.vue b/frontend/src/views/setting/organization/template/components/templateManagementTable.vue index 83f9250e81..8cbcb3252d 100644 --- a/frontend/src/views/setting/organization/template/components/templateManagementTable.vue +++ b/frontend/src/views/setting/organization/template/components/templateManagementTable.vue @@ -5,6 +5,14 @@ {{ record.name }} {{ t('system.orgTemplate.isSystem') }} +