const TYPE_ORDER = [ '中医学', '中西汇通学', '肿瘤学', '针灸学', '心理学', '国学', '医学超级', '心理国学超级' ] const TYPE_ALIAS_MAP = { 国学心理学超级: '心理国学超级' } function toNumber(value) { const num = Number(value) return Number.isNaN(num) ? 0 : num } function formatAmount(value) { return toNumber(value).toFixed(2).replace(/\.00$/, '') } function normalizeTypeName(name) { return TYPE_ALIAS_MAP[name] || name } function getCurrentYearValue() { return String(new Date().getFullYear()) } function normalizeYearValue(value) { if (!value) { return getCurrentYearValue() } if (Object.prototype.toString.call(value) === '[object Date]' && !Number.isNaN(value.getTime())) { return String(value.getFullYear()) } const matched = String(value).trim().match(/^(\d{4})/) if (matched) { return matched[1] } return getCurrentYearValue() } function getPreviousYearValue(yearValue) { return String(Number(normalizeYearValue(yearValue)) - 1) } function getDynamicTypeKeys(...sources) { const keys = [] sources.forEach((source) => { Object.keys(source || {}).forEach((key) => { const normalizedKey = normalizeTypeName(key) if (!keys.includes(normalizedKey)) { keys.push(normalizedKey) } }) }) const orderedKeys = TYPE_ORDER.filter((key) => keys.includes(key)) const restKeys = keys.filter((key) => !TYPE_ORDER.includes(key)) return orderedKeys.concat(restKeys) } function getMappedCount(source, typeName, yearKey) { const originalKey = Object.keys(source || {}).find((key) => normalizeTypeName(key) === typeName) if (!originalKey) { return 0 } return toNumber(((source || {})[originalKey] || {})[yearKey]) } function getMappedValue(source, typeName) { const originalKey = Object.keys(source || {}).find((key) => normalizeTypeName(key) === typeName) if (!originalKey) { return 0 } return toNumber((source || {})[originalKey]) } function buildCountLines(totalLabel, totalValue, counts) { return [{ label: totalLabel, value: toNumber(totalValue), unit: '人' }].concat( getDynamicTypeKeys(counts).map((typeName) => ({ label: typeName, value: getMappedValue(counts, typeName), unit: '人' })) ) } function formatYearRate(current, previous) { const currentValue = toNumber(current) const previousValue = toNumber(previous) if (currentValue === 0) { return '0.00%' } return `${(((currentValue - previousValue) / currentValue) * 100).toFixed(2)}%` } function buildSummaryCards(currentData, previousData) { return [ { title: '办理统计', lines: buildCountLines('办理总人数', currentData.banTotalCount, currentData.banTypeTotalCounts) }, { title: '延期统计', lines: buildCountLines('延期总人数', currentData.yanTotalCount, currentData.yanTypeTotalCounts) }, { title: '金额统计', lines: [ { label: '总办理金额', value: formatAmount(currentData.banTotalPrice), unit: '元' }, { label: '总延期金额', value: formatAmount(currentData.yanTotalPrice), unit: '元' }, { label: '合计总金额', value: formatAmount(toNumber(currentData.banTotalPrice) + toNumber(currentData.yanTotalPrice)), unit: '元' } ] }, { title: '同比增长率', lines: [ { label: '办理总人数同比增长率', value: formatYearRate(currentData.banTotalCount, previousData.banTotalCount) }, { label: '延期总人数同比增长率', value: formatYearRate(currentData.yanTotalCount, previousData.yanTotalCount) }, { label: '办理金额同比增长率', value: formatYearRate(currentData.banTotalPrice, previousData.banTotalPrice) }, { label: '延期金额同比增长率', value: formatYearRate(currentData.yanTotalPrice, previousData.yanTotalPrice) } ] } ] } function buildHandleTableRows(currentData) { const typeKeys = getDynamicTypeKeys(currentData.banCounts, currentData.yanCounts) const rows = typeKeys.map((typeName) => { const ban3 = getMappedCount(currentData.banCounts, typeName, '3') const ban4 = getMappedCount(currentData.banCounts, typeName, '4') const banOther = getMappedCount(currentData.banCounts, typeName, '其他') const yan1 = getMappedCount(currentData.yanCounts, typeName, '1') const yan3 = getMappedCount(currentData.yanCounts, typeName, '3') const yan4 = getMappedCount(currentData.yanCounts, typeName, '4') const yanOther = getMappedCount(currentData.yanCounts, typeName, '其他') return { typeName, ban3, ban4, banOther, yan1, yan3, yan4, yanOther, total: ban3 + ban4 + banOther + yan1 + yan3 + yan4 + yanOther } }) const totalRow = rows.reduce((acc, item) => { acc.ban3 += item.ban3 acc.ban4 += item.ban4 acc.banOther += item.banOther acc.yan1 += item.yan1 acc.yan3 += item.yan3 acc.yan4 += item.yan4 acc.yanOther += item.yanOther acc.total += item.total return acc }, { typeName: '合计', ban3: 0, ban4: 0, banOther: 0, yan1: 0, yan3: 0, yan4: 0, yanOther: 0, total: 0 }) return rows.concat(totalRow) } function buildRatioTableRows(currentData) { return getDynamicTypeKeys(currentData.banTypeRatios).map((typeName) => { const originalKey = Object.keys(currentData.banTypeRatios || {}).find((key) => normalizeTypeName(key) === typeName) return { typeName, ratio: (currentData.banTypeRatios || {})[originalKey] || '0%' } }) } function buildMonthTableRows(currentData) { const rows = Array.from({ length: 12 }, (_, index) => { const month = String(index + 1) return { monthLabel: `${month}月`, banCount: toNumber((currentData.banMonthCounts || {})[month]), yanCount: toNumber((currentData.yanMonthCounts || {})[month]), amount: formatAmount((currentData.monthAmounts || {})[month]) } }) rows.push({ monthLabel: '合计', banCount: toNumber(currentData.banTotalCount), yanCount: toNumber(currentData.yanTotalCount), amount: formatAmount(currentData.banTotalPrice) }) return rows } export { getCurrentYearValue, getPreviousYearValue, normalizeYearValue } export function buildYearVipViewData(currentData = {}, previousData = {}) { return { summaryCards: buildSummaryCards(currentData, previousData), handleTableRows: buildHandleTableRows(currentData), ratioTableRows: buildRatioTableRows(currentData), monthTableRows: buildMonthTableRows(currentData) } }