Files
nuttyreading-master-html/src/views/modules/statisticsBusiness/vipStatistics/yearVipDataHelper.js
chenghuan bba73fdc7c feat(统计业务): 新增月度和年度VIP统计页面及数据处理功能
- 添加月度VIP统计页面,支持选择月份并展示相关统计信息
- 添加年度VIP统计页面,支持选择年份并展示年度统计数据
- 实现数据导出功能,支持下载月度和年度VIP统计报表
- 集成表格展示VIP用户的详细信息,包括办理时间、姓名、电话等
- 新增数据处理辅助函数,支持统计数据的格式化和计算
2026-03-16 16:15:28 +08:00

248 lines
6.6 KiB
JavaScript

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)
}
}