src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
@@ -81,7 +81,7 @@ filterable > <el-option v-for="o in getOptions(field)" v-for="o in getOptions(field, { moduleKey: props.moduleKey })" :key="String(o.value)" :label="o.label" :value="o.value" @@ -103,13 +103,14 @@ fields: { type: Array, default: () => [] }, formPayload: { type: Object, default: () => ({}) }, readonly: { type: Boolean, default: false }, moduleKey: { type: String, default: "" }, }); const { loading: optionSourceLoading, ensureForFields, getOptions, getDisplayLabel } = useSelectOptionSources(); async function loadOptionCaches() { await ensureForFields(props.fields); await ensureForFields(props.fields, { moduleKey: props.moduleKey }); } onMounted(() => { @@ -127,7 +128,7 @@ function displayValue(field) { const val = props.formPayload?.[field.key]; if (field.type === "select" && field.optionSource && field.optionSource !== "static") { return getDisplayLabel(field, val); return getDisplayLabel(field, val, { moduleKey: props.moduleKey }); } return formatFieldDisplayValue(field, val); } src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue
@@ -24,6 +24,7 @@ v-model:attachments="form.storageBlobDTOs" :template-attachments="form.templateAttachments" :user-options="userOptions" :module-key="moduleKey" :show-template-name="!isEdit" :allow-change-template="false" :flow-attachments-only="flowAttachmentsOnly" @@ -56,6 +57,7 @@ fields: { type: Array, default: () => [] }, activeTemplate: { type: Object, default: null }, userOptions: { type: Array, default: () => [] }, moduleKey: { type: String, default: "" }, isEdit: { type: Boolean, default: false }, saving: { type: Boolean, default: false }, formRef: { type: Object, default: null }, src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue
@@ -4,7 +4,7 @@ 用法: <ApprovalTemplateBindDialog v-model:visible="visible" module-key="regular" :module-key="APPROVAL_MODULE_KEYS.REGULAR" @confirm="onTemplateBound" /> --> src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateFormSection.vue
@@ -15,6 +15,7 @@ v-if="!hideFormFields && !flowAttachmentsOnly && !flowOnly" :fields="fields" :form-payload="formPayload" :module-key="moduleKey" /> <el-form-item label="审批流程" required> @@ -87,6 +88,7 @@ uploadLimit: { type: Number, default: 10 }, /** 为 true 时可编辑模板预置的审批人(仅审批模板管理页使用) */ flowEditable: { type: Boolean, default: false }, moduleKey: { type: String, default: "" }, }); const emit = defineEmits(["update:flowNodes", "update:attachments", "change-template"]); src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
@@ -1,30 +1,42 @@ import { deptTreeSelect, userListNoPageByTenantId } from "@/api/system/user.js"; import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js"; /** 下拉选项来源(写入 formConfig,提交页按来源拉取数据) */ export const SELECT_OPTION_SOURCE = { STATIC: "static", USER: "user", DEPT: "dept", EMPLOYEE: "employee", }; export const SELECT_OPTION_SOURCE_OPTIONS = [ { value: SELECT_OPTION_SOURCE.STATIC, label: "手动配置", desc: "在模板中自定义选项文本与值" }, { value: SELECT_OPTION_SOURCE.USER, label: "人员列表", desc: "从系统用户中选择,值为用户 ID" }, { value: SELECT_OPTION_SOURCE.DEPT, label: "部门列表", desc: "从组织架构中选择,值为部门 ID" }, { value: SELECT_OPTION_SOURCE.EMPLOYEE, label: "员工列表", desc: "从在职员工中选择,值为员工 ID" }, ]; const EMPLOYEE_APPLICANT_MODULE_KEYS = new Set(["regular"]); export function selectOptionSourceLabel(source) { return SELECT_OPTION_SOURCE_OPTIONS.find((x) => x.value === source)?.label || "—"; } export function isDynamicOptionSource(source) { return source === SELECT_OPTION_SOURCE.USER || source === SELECT_OPTION_SOURCE.DEPT; return ( source === SELECT_OPTION_SOURCE.USER || source === SELECT_OPTION_SOURCE.DEPT || source === SELECT_OPTION_SOURCE.EMPLOYEE ); } function unwrapArray(payload) { if (Array.isArray(payload)) return payload; if (Array.isArray(payload?.records)) return payload.records; if (payload?.data && Array.isArray(payload.data)) return payload.data; if (Array.isArray(payload?.data?.records)) return payload.data.records; if (payload?.rows && Array.isArray(payload.rows)) return payload.rows; if (Array.isArray(payload?.data?.rows)) return payload.data.rows; return []; } @@ -39,6 +51,15 @@ const value = u.userId ?? u.id; return { label: u.nickName || u.userName || `用户${value}`, value, }; } /** 在职员工 → 下拉 option */ export function mapEmployeeToSelectOption(u) { const value = u.id ?? u.staffOnJobId ?? u.staffId; return { label: u.staffName || u.name || u.userName || `员工${value}`, value, }; } @@ -70,21 +91,41 @@ } /** 按字段配置解析下拉 options(需传入已加载的缓存) */ export function resolveFieldSelectOptions(field, caches = {}) { function isApplicantField(field) { const key = String(field?.key || "").toLowerCase(); const label = String(field?.label || ""); return ( key === "applicant" || key === "applicantname" || label.includes("申请人") ); } export function resolveFieldSelectOptions(field, caches = {}, context = {}) { const source = field?.optionSource || SELECT_OPTION_SOURCE.STATIC; if ( source === SELECT_OPTION_SOURCE.USER && EMPLOYEE_APPLICANT_MODULE_KEYS.has(context.moduleKey) && isApplicantField(field) ) { return (caches.employees || []).map(mapEmployeeToSelectOption); } if (source === SELECT_OPTION_SOURCE.USER) { return (caches.users || []).map(mapUserToSelectOption); } if (source === SELECT_OPTION_SOURCE.DEPT) { return caches.deptOptions || []; } if (source === SELECT_OPTION_SOURCE.EMPLOYEE) { return (caches.employees || []).map(mapEmployeeToSelectOption); } return (field?.options || []).filter((o) => o.value !== "" && o.value != null); } /** 根据已解析的 options 反查展示文本 */ export function resolveSelectDisplayLabel(field, val, caches = {}) { export function resolveSelectDisplayLabel(field, val, caches = {}, context = {}) { if (val == null || val === "") return "—"; const options = resolveFieldSelectOptions(field, caches); const options = resolveFieldSelectOptions(field, caches, context); const hit = options.find((o) => String(o.value) === String(val)); return hit?.label || String(val); } @@ -93,9 +134,10 @@ export async function fetchSelectOptionCaches(sources = []) { const needUser = sources.includes(SELECT_OPTION_SOURCE.USER); const needDept = sources.includes(SELECT_OPTION_SOURCE.DEPT); const caches = { users: [], deptOptions: [] }; const needEmployee = sources.includes(SELECT_OPTION_SOURCE.EMPLOYEE); const caches = { users: [], deptOptions: [], employees: [] }; if (!needUser && !needDept) return caches; if (!needUser && !needDept && !needEmployee) return caches; const tasks = []; if (needUser) { @@ -123,18 +165,37 @@ }) ); } if (needEmployee) { tasks.push( staffOnJobListPage({ current: -1, size: -1, staffState: 1 }) .then((res) => { caches.employees = unwrapArray(res); }) .catch(() => { caches.employees = []; }) ); } await Promise.all(tasks); return caches; } /** 从字段列表收集需要预加载的动态来源 */ export function collectOptionSourcesFromFields(fields) { export function collectOptionSourcesFromFields(fields, context = {}) { const set = new Set(); (fields || []).forEach((f) => { if (f?.type === "select" && isDynamicOptionSource(f.optionSource)) { if ( EMPLOYEE_APPLICANT_MODULE_KEYS.has(context.moduleKey) && f.optionSource === SELECT_OPTION_SOURCE.USER && isApplicantField(f) ) { set.add(SELECT_OPTION_SOURCE.EMPLOYEE); } else { set.add(f.optionSource); } } }); return [...set]; } src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
@@ -14,25 +14,26 @@ deptOptions: [], }); async function ensureForFields(fields) { const sources = collectOptionSourcesFromFields(fields); async function ensureForFields(fields, context = {}) { const sources = collectOptionSourcesFromFields(fields, context); if (!sources.length) return; loading.value = true; try { const next = await fetchSelectOptionCaches(sources); caches.users = next.users; caches.deptOptions = next.deptOptions; caches.employees = next.employees || []; } finally { loading.value = false; } } function getOptions(field) { return resolveFieldSelectOptions(field, caches); function getOptions(field, context = {}) { return resolveFieldSelectOptions(field, caches, context); } function getDisplayLabel(field, val) { return resolveSelectDisplayLabel(field, val, caches); function getDisplayLabel(field, val, context = {}) { return resolveSelectDisplayLabel(field, val, caches, context); } return { src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
@@ -48,6 +48,7 @@ :fields="submitFormFields" :active-template="activeTemplate" :user-options="flowUserOptions" :module-key="APPROVAL_MODULE_KEYS.REGULAR" :is-edit="isSubmitEdit" :saving="submitSaving" :form-ref="submitFormRef" src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
@@ -58,6 +58,7 @@ :fields="submitFormFields" :active-template="activeTemplate" :user-options="flowUserOptions" :module-key="APPROVAL_MODULE_KEYS.TRANSFER" :is-edit="isSubmitEdit" :saving="submitSaving" :form-ref="submitFormRef" src/views/officeProcessAutomation/HrManage/work-handover/index.vue
@@ -58,6 +58,7 @@ :fields="submitFormFields" :active-template="activeTemplate" :user-options="flowUserOptions" :module-key="APPROVAL_MODULE_KEYS.WORK_HANDOVER" :is-edit="isSubmitEdit" :saving="submitSaving" :form-ref="submitFormRef"