| | |
| | | computeLeaveDurationDisplay, |
| | | computeOvertimeHoursDisplay, |
| | | } from "../../../_utils/approvalModuleApplyExtras.js"; |
| | | import { resolveInstanceFormPayload } from "../../../_utils/approvalModuleListSearch.js"; |
| | | import { |
| | | businessStatusTagType, |
| | | businessStatusText, |
| | |
| | | taskStatusTagType, |
| | | taskStatusText, |
| | | } from "../../../_utils/approveListUtils.js"; |
| | | import { parseApprovalFormConfig } from "../../../_utils/approvalFormField.js"; |
| | | |
| | | const props = defineProps({ |
| | | row: { type: Object, default: () => ({}) }, |
| | |
| | | const statusTagType = status => |
| | | isBusinessModule.value ? businessStatusTagType(status) : instanceStatusTagType(status); |
| | | |
| | | const displayFields = computed(() => |
| | | resolveInstanceDisplayFields(props.row?.formConfig) |
| | | ); |
| | | const displayFields = computed(() => resolveInstanceDisplayFields(props.row)); |
| | | |
| | | const moduleExtraRows = computed(() => { |
| | | const rows = []; |
| | | const cfg = parseApprovalFormConfig(props.row?.formConfig); |
| | | const payload = {}; |
| | | (cfg.fields || []).forEach(f => { |
| | | if (f?.key) payload[f.key] = f.value ?? ""; |
| | | const { fields, formPayload } = resolveInstanceFormPayload(props.row); |
| | | const payload = { ...formPayload }; |
| | | (fields || []).forEach(f => { |
| | | if (f?.key && payload[f.key] == null) payload[f.key] = f.value ?? ""; |
| | | }); |
| | | if (props.moduleKey === APPROVAL_MODULE_KEYS.LEAVE) { |
| | | const balance = payload.leaveBalanceDays; |
| | | if (balance != null && balance !== "") { |
| | | rows.push({ label: "假期余额", value: `${balance} 天` }); |
| | | } |
| | | const days = computeLeaveDurationDisplay(cfg.fields, payload); |
| | | const days = computeLeaveDurationDisplay(fields, payload); |
| | | if (days) rows.push({ label: "请假时长", value: `${days} 天` }); |
| | | } |
| | | if (props.moduleKey === APPROVAL_MODULE_KEYS.OVERTIME) { |
| | | const hours = computeOvertimeHoursDisplay(cfg.fields, payload); |
| | | const hours = computeOvertimeHoursDisplay(fields, payload); |
| | | if (hours) rows.push({ label: "加班时长", value: `${hours} 小时` }); |
| | | } |
| | | if (props.moduleKey === APPROVAL_MODULE_KEYS.TRANSFER) { |
| | |
| | | <!-- |
| | | OA / 报销管理 / 费用报销 |
| | | 路由:/pages/oa/ReimburseManage/cost-reimburse/index |
| | | OA / 报销管理 / 费用报销(审批实例列表) |
| | | --> |
| | | <template> |
| | | <OaListPage v-if="config" |
| | | :page-key="pageKey" |
| | | :page-config="config" /> |
| | | <ApprovalInstanceListPage :module-key="APPROVAL_MODULE_KEYS.COST_REIMBURSE" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | /** OA - 报销管理 - 费用报销 */ |
| | | import OaListPage from "../../_components/OaListPage.vue"; |
| | | import { useOaPage } from "../../_utils/useOaPage.js"; |
| | | |
| | | const pageKey = "ReimburseManage/cost-reimburse"; |
| | | const { config } = useOaPage(pageKey); |
| | | import ApprovalInstanceListPage from "../../_components/ApprovalInstanceListPage.vue"; |
| | | import { APPROVAL_MODULE_KEYS } from "../../_utils/approvalModuleRegistry.js"; |
| | | </script> |
| | |
| | | <!-- |
| | | OA / 报销管理 / 差旅报销 |
| | | 路由:/pages/oa/ReimburseManage/travel-reimburse/index |
| | | OA / 报销管理 / 差旅报销(审批实例列表) |
| | | --> |
| | | <template> |
| | | <OaListPage v-if="config" |
| | | :page-key="pageKey" |
| | | :page-config="config" /> |
| | | <ApprovalInstanceListPage :module-key="APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE" /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | /** OA - 报销管理 - 差旅报销 */ |
| | | import OaListPage from "../../_components/OaListPage.vue"; |
| | | import { useOaPage } from "../../_utils/useOaPage.js"; |
| | | |
| | | const pageKey = "ReimburseManage/travel-reimburse"; |
| | | const { config } = useOaPage(pageKey); |
| | | import ApprovalInstanceListPage from "../../_components/ApprovalInstanceListPage.vue"; |
| | | import { APPROVAL_MODULE_KEYS } from "../../_utils/approvalModuleRegistry.js"; |
| | | </script> |
| | |
| | | import { fetchApprovalTemplateTypes } from "../_utils/approvalTemplateType.js"; |
| | | import { |
| | | getApprovalModuleConfig, |
| | | resolveModuleBusinessType, |
| | | getModuleListBusinessType, |
| | | } from "../_utils/approvalModuleRegistry.js"; |
| | | import { |
| | | buildModuleListDto, |
| | |
| | | }; |
| | | |
| | | const initBusinessType = async () => { |
| | | const fixed = getModuleListBusinessType(props.moduleKey); |
| | | businessType.value = fixed != null && fixed !== "" ? fixed : ""; |
| | | try { |
| | | typeOptions.value = await fetchApprovalTemplateTypes(); |
| | | const resolved = resolveModuleBusinessType(props.moduleKey, typeOptions.value); |
| | | businessType.value = |
| | | resolved != null && resolved !== "" |
| | | ? resolved |
| | | : moduleConfig.value?.approvalType ?? ""; |
| | | } catch { |
| | | businessType.value = moduleConfig.value?.approvalType ?? ""; |
| | | typeOptions.value = []; |
| | | } |
| | | }; |
| | | |
| | |
| | | datetime: "YYYY-MM-DD HH:mm:ss", |
| | | }; |
| | | |
| | | /** 解析 formConfig JSON */ |
| | | /** 解析 formConfig JSON(含嵌套 formPayload,与 Web parseInstanceFormConfig 一致) */ |
| | | export function parseApprovalFormConfig(raw) { |
| | | if (!raw) return { prompt: "", fields: [] }; |
| | | if (!raw) return { prompt: "", fields: [], formPayload: {} }; |
| | | try { |
| | | const obj = typeof raw === "string" ? JSON.parse(raw) : raw; |
| | | const payload = obj?.formPayload; |
| | | return { |
| | | prompt: obj?.prompt || "", |
| | | fields: Array.isArray(obj?.fields) ? obj.fields : [], |
| | | prompt: obj?.prompt || obj?.summaryPlaceholder || "", |
| | | summaryPlaceholder: obj?.summaryPlaceholder || "", |
| | | approvalType: obj?.approvalType || "", |
| | | fields: Array.isArray(obj?.fields) |
| | | ? obj.fields |
| | | : Array.isArray(obj?.formFields) |
| | | ? obj.formFields |
| | | : [], |
| | | formPayload: payload && typeof payload === "object" ? payload : {}, |
| | | }; |
| | | } catch { |
| | | return { prompt: "", fields: [] }; |
| | | return { prompt: "", fields: [], formPayload: {} }; |
| | | } |
| | | } |
| | | |
| | |
| | | valueMap[field.key] = val; |
| | | } |
| | | }); |
| | | Object.assign(valueMap, instance.formPayload || {}); |
| | | const baseFields = template.fields.length ? template.fields : instance.fields; |
| | | return { |
| | | prompt: instance.prompt || template.prompt, |
| | |
| | | getApprovalModuleConfig, |
| | | getModuleMatchingBusinessTypes, |
| | | } from "./approvalModuleRegistry.js"; |
| | | import { parseApprovalFormConfig } from "./approvalFormField.js"; |
| | | import { |
| | | parseApprovalFormConfig, |
| | | parseDatetimerangeValue, |
| | | } from "./approvalFormField.js"; |
| | | import { matchBusinessTypeValue } from "./approvalTemplateType.js"; |
| | | |
| | | /** 与 Web leave-apply LEAVE_TYPE_OPTIONS 一致 */ |
| | |
| | | return payload; |
| | | } |
| | | |
| | | function guessFieldTypeFromValue(val) { |
| | | if (Array.isArray(val)) return "datetimerange"; |
| | | if (typeof val === "number") return "number"; |
| | | return "text"; |
| | | } |
| | | |
| | | /** 解析实例 formConfig / formPayload(与 Web resolveInstanceFormFields 对齐) */ |
| | | export function resolveInstanceFormPayload(row) { |
| | | const cfg = parseApprovalFormConfig(row?.formConfig); |
| | | const fields = (row?.formFieldDefs?.length ? row.formFieldDefs : cfg.fields) || []; |
| | | let fields = (row?.formFieldDefs?.length ? row.formFieldDefs : cfg.fields) || []; |
| | | const formPayload = { |
| | | ...(fields.length ? buildFormPayloadFromFields(fields) : {}), |
| | | ...cfg.formPayload, |
| | | ...(row?.formPayload || {}), |
| | | }; |
| | | if (!fields.length && Object.keys(formPayload).length) { |
| | | fields = Object.keys(formPayload) |
| | | .filter(k => k && k !== "summary") |
| | | .map(k => ({ |
| | | key: k, |
| | | label: k, |
| | | type: guessFieldTypeFromValue(formPayload[k]), |
| | | required: false, |
| | | options: [], |
| | | })); |
| | | } |
| | | fields = fields.map(field => ({ |
| | | ...field, |
| | | value: |
| | | formPayload[field.key] ?? field.value ?? field.defaultValue ?? "", |
| | | })); |
| | | return { fields, formPayload }; |
| | | } |
| | | |
| | | /** 已知下拉字段 value → 展示文案(模板未带 options 时兜底) */ |
| | | export function formatKnownSelectLabel(prop, val) { |
| | | if (val === undefined || val === null || val === "") return ""; |
| | | const maps = { |
| | | leaveType: LEAVE_TYPE_OPTIONS, |
| | | overtimeType: OVERTIME_TYPE_OPTIONS, |
| | | handoverStatus: HANDOVER_STATUS_OPTIONS, |
| | | handoverType: HANDOVER_TYPE_OPTIONS, |
| | | }; |
| | | const options = maps[prop]; |
| | | if (!options) return ""; |
| | | const hit = options.find(o => String(o.value) === String(val)); |
| | | return hit?.label || ""; |
| | | } |
| | | |
| | | export function getRowPayloadValue(row, keys) { |
| | |
| | | if (formPayload[k] != null && formPayload[k] !== "") return formPayload[k]; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | const DATETIME_RANGE_KEYS = [ |
| | | "dateRange", |
| | | "leaveTime", |
| | | "overtimeTime", |
| | | "timeRange", |
| | | ]; |
| | | |
| | | function pickDatetimerangeRaw(formPayload, fields = []) { |
| | | for (const key of DATETIME_RANGE_KEYS) { |
| | | const v = formPayload?.[key]; |
| | | if (v != null && v !== "") return v; |
| | | const field = (fields || []).find(f => f?.key === key); |
| | | const fv = field?.value ?? field?.defaultValue; |
| | | if (fv != null && fv !== "") return fv; |
| | | } |
| | | const rangeField = (fields || []).find( |
| | | f => String(f?.type || "").toLowerCase() === "datetimerange" |
| | | ); |
| | | if (rangeField?.key) { |
| | | const v = |
| | | formPayload?.[rangeField.key] ?? rangeField.value ?? rangeField.defaultValue; |
| | | if (v != null && v !== "") return v; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | function splitRangeValue(val) { |
| | | if (val === undefined || val === null || val === "") { |
| | | return { start: "", end: "" }; |
| | | } |
| | | if (Array.isArray(val)) { |
| | | return { start: val[0] || "", end: val[1] || "" }; |
| | | } |
| | | return parseDatetimerangeValue(val); |
| | | } |
| | | |
| | | /** |
| | | * 列表列 prop 与 formPayload 对齐(请假 startTime/endTime 来自 dateRange) |
| | | */ |
| | | export function resolveListFieldRawValue(prop, row, fields = [], formPayload = {}) { |
| | | const payload = formPayload || {}; |
| | | const direct = payload[prop] ?? row?.[prop]; |
| | | |
| | | if (prop === "startTime" || prop === "endTime") { |
| | | if (direct != null && direct !== "") return direct; |
| | | const altStart = |
| | | payload.start ?? payload.startDate ?? payload.beginTime ?? row?.startTime; |
| | | const altEnd = |
| | | payload.end ?? payload.endDate ?? payload.finishTime ?? row?.endTime; |
| | | if (prop === "startTime" && altStart) return altStart; |
| | | if (prop === "endTime" && altEnd) return altEnd; |
| | | const { start, end } = splitRangeValue(pickDatetimerangeRaw(payload, fields)); |
| | | return prop === "startTime" ? start : end; |
| | | } |
| | | |
| | | if (prop === "overtimeDate") { |
| | | const d = payload.overtimeDate ?? payload.date ?? direct; |
| | | if (d != null && d !== "") return Array.isArray(d) ? d[0] || "" : d; |
| | | const { start } = splitRangeValue(pickDatetimerangeRaw(payload, fields)); |
| | | return start || ""; |
| | | } |
| | | |
| | | if (direct != null && direct !== "") return direct; |
| | | const hit = (fields || []).find(f => f?.key === prop); |
| | | return hit?.value ?? hit?.defaultValue ?? ""; |
| | | } |
| | | |
| | | function pickDateRange(searchForm) { |
| | |
| | | return { applicantKeyword: "", leaveType: "" }; |
| | | case APPROVAL_MODULE_KEYS.OVERTIME: |
| | | return { applicantKeyword: "", overtimeType: "" }; |
| | | case APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE: |
| | | case APPROVAL_MODULE_KEYS.COST_REIMBURSE: |
| | | case APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS: |
| | | return { applicantKeyword: "" }; |
| | | default: |
| | | return {}; |
| | | } |
| | |
| | | matchApplicantKeyword(row, sf.applicantKeyword) && |
| | | matchSelectValue(row, ["overtimeType", "加班类型"], sf.overtimeType) |
| | | ); |
| | | case APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE: |
| | | case APPROVAL_MODULE_KEYS.COST_REIMBURSE: |
| | | case APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS: |
| | | return list.filter(row => matchApplicantKeyword(row, sf.applicantKeyword)); |
| | | default: |
| | | return list; |
| | | } |
| | |
| | | }, |
| | | ], |
| | | }; |
| | | case APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE: |
| | | case APPROVAL_MODULE_KEYS.COST_REIMBURSE: |
| | | case APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS: |
| | | return { |
| | | fields: [ |
| | | { |
| | | key: "applicantKeyword", |
| | | type: "input", |
| | | label: "申请人", |
| | | placeholder: "姓名或编号", |
| | | }, |
| | | ], |
| | | }; |
| | | default: |
| | | return { fields: [] }; |
| | | } |
| | |
| | | WORK_HANDOVER: "work_handover", |
| | | LEAVE: "leave", |
| | | OVERTIME: "overtime", |
| | | TRAVEL_REIMBURSE: "travel_reimburse", |
| | | COST_REIMBURSE: "cost_reimburse", |
| | | ENTERPRISE_NEWS: "enterprise_news", |
| | | }; |
| | | |
| | | /** 审批实例 listPage businessType(与后端约定一致) */ |
| | | export const APPROVAL_BUSINESS_TYPE = { |
| | | [APPROVAL_MODULE_KEYS.REGULAR]: 10, |
| | | [APPROVAL_MODULE_KEYS.TRANSFER]: 11, |
| | | [APPROVAL_MODULE_KEYS.WORK_HANDOVER]: 13, |
| | | [APPROVAL_MODULE_KEYS.LEAVE]: 14, |
| | | [APPROVAL_MODULE_KEYS.OVERTIME]: 15, |
| | | [APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE]: 16, |
| | | [APPROVAL_MODULE_KEYS.COST_REIMBURSE]: 17, |
| | | [APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS]: 18, |
| | | }; |
| | | |
| | | export const APPROVAL_MODULE_REGISTRY = { |
| | | [APPROVAL_MODULE_KEYS.REGULAR]: { |
| | | label: "转正申请", |
| | | approvalType: "regular", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.REGULAR], |
| | | typeLabels: ["转正", "转正申请"], |
| | | listFields: [ |
| | | { label: "入职日期", prop: "entryDate" }, |
| | |
| | | [APPROVAL_MODULE_KEYS.TRANSFER]: { |
| | | label: "调岗申请", |
| | | approvalType: "transfer", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.TRANSFER], |
| | | typeLabels: ["调岗", "调动", "调岗申请", "调动申请"], |
| | | listFields: [ |
| | | { label: "原岗位", prop: "fromPost" }, |
| | |
| | | [APPROVAL_MODULE_KEYS.WORK_HANDOVER]: { |
| | | label: "工作交接", |
| | | approvalType: "work_handover", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.WORK_HANDOVER], |
| | | typeLabels: ["工作交接", "交接", "工作交接审批"], |
| | | listFields: [ |
| | | { label: "交接人", prop: "handoverTo" }, |
| | |
| | | [APPROVAL_MODULE_KEYS.LEAVE]: { |
| | | label: "请假申请", |
| | | approvalType: "leave", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.LEAVE], |
| | | typeLabels: ["请假", "请假申请", "请假审批"], |
| | | listFields: [ |
| | | { label: "请假类型", prop: "leaveType" }, |
| | |
| | | [APPROVAL_MODULE_KEYS.OVERTIME]: { |
| | | label: "加班申请", |
| | | approvalType: "overtime", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.OVERTIME], |
| | | typeLabels: ["加班", "加班申请", "加班审批"], |
| | | listFields: [ |
| | | { label: "加班日期", prop: "overtimeDate" }, |
| | | { label: "时长(小时)", prop: "hours" }, |
| | | ], |
| | | }, |
| | | [APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE]: { |
| | | label: "差旅报销", |
| | | approvalType: "travel_reimburse", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE], |
| | | typeLabels: ["差旅", "差旅报销", "出差报销"], |
| | | listFields: [], |
| | | }, |
| | | [APPROVAL_MODULE_KEYS.COST_REIMBURSE]: { |
| | | label: "费用报销", |
| | | approvalType: "cost_reimburse", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.COST_REIMBURSE], |
| | | typeLabels: ["费用", "费用报销"], |
| | | listFields: [], |
| | | }, |
| | | [APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS]: { |
| | | label: "企业新闻", |
| | | approvalType: "enterprise_news", |
| | | businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS], |
| | | typeLabels: ["企业新闻", "新闻", "新闻发布"], |
| | | listFields: [], |
| | | }, |
| | | }; |
| | | |
| | |
| | | const cfg = getApprovalModuleConfig(moduleKey); |
| | | if (!cfg) return ""; |
| | | if (cfg.businessType != null && cfg.businessType !== "") return cfg.businessType; |
| | | return cfg.approvalType || ""; |
| | | return APPROVAL_BUSINESS_TYPE[moduleKey] ?? ""; |
| | | } |
| | | |
| | | function matchBiz(a, b) { |
| | |
| | | export function resolveModuleBusinessType(moduleKey, typeOptions = []) { |
| | | const cfg = getApprovalModuleConfig(moduleKey); |
| | | if (!cfg) return null; |
| | | if (cfg.businessType != null && cfg.businessType !== "") return cfg.businessType; |
| | | |
| | | const fixed = getModuleListBusinessType(moduleKey); |
| | | if (fixed != null && fixed !== "") return fixed; |
| | | |
| | | const labels = [cfg.label, ...(cfg.typeLabels || [])].filter(Boolean); |
| | | const hitByLabel = (typeOptions || []).find(opt => { |
| | |
| | | if (hitByValue?.value != null && hitByValue.value !== "") return hitByValue.value; |
| | | } |
| | | |
| | | return cfg.approvalType || null; |
| | | return null; |
| | | } |
| | | |
| | | export function getModuleMatchingBusinessTypes(moduleKey, typeOptions = []) { |
| | | const cfg = getApprovalModuleConfig(moduleKey); |
| | | if (!cfg) return []; |
| | | |
| | | const fixed = getModuleListBusinessType(moduleKey); |
| | | if (fixed != null && fixed !== "") return [fixed]; |
| | | |
| | | const values = new Set(); |
| | | const primary = resolveModuleBusinessType(moduleKey, typeOptions); |
| | | if (primary != null && primary !== "") values.add(primary); |
| | | if (cfg.approvalType) values.add(cfg.approvalType); |
| | | |
| | | const labels = [cfg.label, ...(cfg.typeLabels || [])].filter(Boolean); |
| | | for (const opt of typeOptions || []) { |
| | |
| | | return [...values]; |
| | | } |
| | | |
| | | /** 列表页 moduleKey 与路由 pageKey 对照 */ |
| | | export const PAGE_KEY_TO_MODULE = { |
| | | "HrManage/regular-apply": APPROVAL_MODULE_KEYS.REGULAR, |
| | | "HrManage/transfer-apply": APPROVAL_MODULE_KEYS.TRANSFER, |
| | | "HrManage/work-handover": APPROVAL_MODULE_KEYS.WORK_HANDOVER, |
| | | "AttendManage/leave-apply": APPROVAL_MODULE_KEYS.LEAVE, |
| | | "AttendManage/overtime-apply": APPROVAL_MODULE_KEYS.OVERTIME, |
| | | "ReimburseManage/travel-reimburse": APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE, |
| | | "ReimburseManage/cost-reimburse": APPROVAL_MODULE_KEYS.COST_REIMBURSE, |
| | | "EnterpriseNews/news-manage": APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS, |
| | | }; |
| | | |
| | | export function getModuleKeyFromPageKey(pageKey) { |
| | |
| | | isSelectField, |
| | | mergeFormConfigForEdit, |
| | | } from "./approvalFormField.js"; |
| | | import { |
| | | formatKnownSelectLabel, |
| | | resolveInstanceFormPayload, |
| | | resolveListFieldRawValue, |
| | | } from "./approvalModuleListSearch.js"; |
| | | |
| | | export const DETAIL_STORAGE_KEY = "oa_approve_instance_detail_row"; |
| | | |
| | |
| | | return parseTime(val, "{y}-{m}-{d} {h}:{i}:{s}") || String(val); |
| | | } |
| | | |
| | | /** 解析实例 formConfig 为只读展示字段 */ |
| | | export function resolveInstanceDisplayFields(formConfig) { |
| | | const merged = mergeFormConfigForEdit("", formConfig); |
| | | /** 解析实例为只读展示字段(合并 formPayload,支持传整行或仅 formConfig) */ |
| | | export function resolveInstanceDisplayFields(formConfigOrRow) { |
| | | const row = |
| | | formConfigOrRow && |
| | | typeof formConfigOrRow === "object" && |
| | | (formConfigOrRow.formConfig != null || |
| | | formConfigOrRow.formPayload != null || |
| | | formConfigOrRow.formFieldDefs != null) |
| | | ? formConfigOrRow |
| | | : { formConfig: formConfigOrRow }; |
| | | const { fields } = resolveInstanceFormPayload(row); |
| | | if (fields.length) return fields.filter(f => f?.key); |
| | | const merged = mergeFormConfigForEdit("", row.formConfig); |
| | | return (merged.fields || []).filter(f => f?.key); |
| | | } |
| | | |
| | |
| | | const val = field.value ?? field.defaultValue; |
| | | if (val === undefined || val === null || val === "") return "-"; |
| | | if (isSelectField(field)) { |
| | | return getFieldOptionLabel(field, val) || String(val); |
| | | const fromOptions = getFieldOptionLabel(field, val); |
| | | if (fromOptions && fromOptions !== "-") return fromOptions; |
| | | const known = formatKnownSelectLabel(field.key, val); |
| | | if (known) return known; |
| | | return String(val); |
| | | } |
| | | const known = formatKnownSelectLabel(field?.key, val); |
| | | if (known) return known; |
| | | const shown = formatFieldDisplayValue(field, val); |
| | | return shown || String(val); |
| | | } |
| | | |
| | | const DATETIME_LIST_PROPS = new Set([ |
| | | "startTime", |
| | | "endTime", |
| | | "overtimeDate", |
| | | "applyTime", |
| | | ]); |
| | | |
| | | function formatListFieldDisplay(prop, val, field) { |
| | | if (val === undefined || val === null || val === "") return "-"; |
| | | if (DATETIME_LIST_PROPS.has(prop)) { |
| | | const shown = formatDateTime(val); |
| | | if (shown && shown !== "-") return shown; |
| | | } |
| | | if (field?.type === "datetimerange") { |
| | | const shown = formatFieldDisplayValue(field, val); |
| | | if (shown) return shown; |
| | | } |
| | | if (field) return displayFieldValue({ ...field, value: val }); |
| | | const known = formatKnownSelectLabel(prop, val); |
| | | if (known) return known; |
| | | return String(val); |
| | | } |
| | | |
| | | /** 审批记录 result:approved | rejected | pending */ |
| | |
| | | }; |
| | | } |
| | | |
| | | /** 从 formConfig 提取列表展示字段(label + value) */ |
| | | export function buildFormDisplayRows(formConfig, listFields = []) { |
| | | const fields = resolveInstanceDisplayFields(formConfig); |
| | | /** 从实例行提取列表展示字段(label + value,含 formPayload) */ |
| | | export function buildFormDisplayRows(row, listFields = []) { |
| | | const { fields, formPayload } = resolveInstanceFormPayload(row); |
| | | const fieldByKey = new Map((fields || []).map(f => [f.key, f])); |
| | | const rows = []; |
| | | const propKeys = (listFields || []).map(f => f.prop).filter(Boolean); |
| | | const defs = listFields || []; |
| | | |
| | | if (propKeys.length) { |
| | | propKeys.forEach(prop => { |
| | | const hit = fields.find(f => f.key === prop); |
| | | if (hit) { |
| | | rows.push({ label: hit.label, value: displayFieldValue(hit) }); |
| | | } |
| | | if (defs.length) { |
| | | defs.forEach(def => { |
| | | if (!def?.prop) return; |
| | | const prop = def.prop; |
| | | const hit = fieldByKey.get(prop); |
| | | const raw = resolveListFieldRawValue(prop, row, fields, formPayload); |
| | | rows.push({ |
| | | label: def.label || hit?.label || prop, |
| | | value: formatListFieldDisplay(prop, raw, hit), |
| | | }); |
| | | }); |
| | | } else { |
| | | fields.slice(0, 3).forEach(f => { |
| | |
| | | /** 列表行增强(保留原始字段供详情/编辑) */ |
| | | export function mapInstanceListRow(row, listFields = []) { |
| | | if (!row) return {}; |
| | | const displayRows = buildFormDisplayRows(row.formConfig, listFields); |
| | | const displayRows = buildFormDisplayRows(row, listFields); |
| | | const extra = {}; |
| | | const formFields = resolveInstanceDisplayFields(row.formConfig); |
| | | const { fields, formPayload } = resolveInstanceFormPayload(row); |
| | | (listFields || []).forEach(def => { |
| | | if (!def?.prop) return; |
| | | const hit = formFields.find(f => f.key === def.prop); |
| | | extra[def.prop] = hit ? displayFieldValue(hit) : "-"; |
| | | }); |
| | | const formPayload = {}; |
| | | formFields.forEach(f => { |
| | | if (f?.key) formPayload[f.key] = f.value ?? f.defaultValue ?? ""; |
| | | const hit = fields.find(f => f.key === def.prop); |
| | | const raw = resolveListFieldRawValue(def.prop, row, fields, formPayload); |
| | | extra[def.prop] = formatListFieldDisplay(def.prop, raw, hit); |
| | | }); |
| | | return { |
| | | ...row, |
| | |
| | | createTime: formatDateTime(row.applyTime || row.createTime), |
| | | displayRows, |
| | | formPayload, |
| | | formFieldDefs: fields, |
| | | ...extra, |
| | | }; |
| | | } |