- 添加月度VIP统计页面,支持选择月份并展示相关统计信息 - 添加年度VIP统计页面,支持选择年份并展示年度统计数据 - 实现数据导出功能,支持下载月度和年度VIP统计报表 - 集成表格展示VIP用户的详细信息,包括办理时间、姓名、电话等 - 新增数据处理辅助函数,支持统计数据的格式化和计算
248 lines
6.6 KiB
JavaScript
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)
|
|
}
|
|
}
|