From 9a4a5f2339d94952b44e9c96d1d3f9c65b629fee Mon Sep 17 00:00:00 2001 From: danaisuiyuan Date: Mon, 11 May 2026 13:21:35 +0800 Subject: [PATCH] feat(dashboard): archive daily report from page data Generate the standalone daily report HTML from the dashboard data already loaded in the H5 page, keeping the archived page visually aligned with the mobile dashboard. Co-authored-by: Cursor --- .../src/features/boss-dashboard/archive.ts | 215 ++++++++++++++++++ .../boss-dashboard/pages/OperationsPages.tsx | 6 +- 2 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 dashboard-frontend/src/features/boss-dashboard/archive.ts diff --git a/dashboard-frontend/src/features/boss-dashboard/archive.ts b/dashboard-frontend/src/features/boss-dashboard/archive.ts new file mode 100644 index 0000000..3d52a56 --- /dev/null +++ b/dashboard-frontend/src/features/boss-dashboard/archive.ts @@ -0,0 +1,215 @@ +import { formatMetricValue, formatMoney, formatNumber } from '../../utils/format' +import type { DashboardOverview, MetricStatus, RankItem, RiskAlert, RiskLevel, SnapshotSlot, TodaySnapshot } from './types' + +const snapshotTitle: Record = { + '1015': '上午抢购快报', + '1455': '下午寄卖/转卖快报', +} + +const snapshotDescription: Record = { + '1015': '用户集中抢购上一天用户寄卖的商品,重点看成交、付款和采购用户是否达标。', + '1455': '用户把上午抢到的商品继续寄卖或转卖,重点看新增寄售供给和奖金变化是否正常。', +} + +const metricStatusText: Record = { + normal: '正常', + success: '达标', + warning: '关注', + danger: '异常', +} + +const riskLevelText: Record = { + red: '红色', + yellow: '黄色', + gray: '灰色', +} + +function escapeHtml(value: unknown): string { + return String(value ?? '') + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", ''') +} + +function serializeStaticData(data: DashboardOverview): string { + return JSON.stringify(data, null, 2).replaceAll('<', '\\u003c').replaceAll('>', '\\u003e') +} + +function renderMetricGrid(metrics: DashboardOverview['kpis']): string { + return metrics + .map( + (metric) => ` +
+ ${escapeHtml(metricStatusText[metric.status])} +

${escapeHtml(metric.title)}

+ ${escapeHtml(formatMetricValue(metric.value, metric.unit))} + ${metric.trendLabel ? `

${escapeHtml(metric.trendLabel)} ${escapeHtml(metric.trendValue ?? '')}%

` : ''} +
`, + ) + .join('') +} + +function renderSnapshots(snapshots: TodaySnapshot[]): string { + return snapshots + .map((snapshot) => { + const bonusChange = Number(snapshot.selfBonusChange) + Number(snapshot.shareBonusChange) + return ` +
+
+
+ ${escapeHtml(snapshot.slot)} +

${escapeHtml(snapshotTitle[snapshot.slot])}

+
+ ${escapeHtml(snapshot.status)} +
+

${escapeHtml(snapshotDescription[snapshot.slot])}

+

${escapeHtml(snapshot.message)}

+ ${snapshot.generatedAt ? `生成时间:${escapeHtml(snapshot.generatedAt)}` : ''} +
+ 用户${escapeHtml(formatNumber(snapshot.purchaseUsers))}人 + 订单${escapeHtml(formatNumber(snapshot.orderCount))}单 + 成交额${escapeHtml(formatMoney(snapshot.dealAmount))} + 已支付${escapeHtml(formatMoney(snapshot.paidAmount))} + 商品${escapeHtml(formatNumber(snapshot.newMerchandiseCount))}件 + 奖金${escapeHtml(formatMoney(bonusChange))} +
+
` + }) + .join('') +} + +function renderRanks(title: string, ranks: RankItem[]): string { + return ` +
+

${escapeHtml(title)}

+
+ ${ranks + .map( + (rank, index) => ` +
+ ${index + 1} + + ${escapeHtml(rank.name)} + ${escapeHtml(rank.description)} + + ${escapeHtml(formatMoney(rank.value))} +
`, + ) + .join('')} +
+
` +} + +function renderRisks(risks: RiskAlert[]): string { + return risks + .map( + (risk) => ` +
+
+ ${escapeHtml(riskLevelText[risk.level])} + ${escapeHtml(risk.type)} + +
+ ${escapeHtml(risk.title)} +

${escapeHtml(risk.description)}

+
`, + ) + .join('') +} + +export function buildDailyReportArchiveHtml(data: DashboardOverview): string { + const generatedAt = new Date().toLocaleString('zh-CN', { hour12: false }) + + return ` + + + + + 经营日报归档 - ${escapeHtml(data.businessDate)} + + + +
+
+

Daily Report Archive

+

经营日报归档 ${escapeHtml(data.businessDate)}

+

${escapeHtml(data.summary)}

+
+ 业务日期:${escapeHtml(data.businessDate)} + 数据生成:${escapeHtml(data.generatedAt)} + 归档生成:${escapeHtml(generatedAt)} +
+
+

核心指标

${renderMetricGrid(data.kpis)}
+

资金池摘要

${renderMetricGrid(data.fundPool)}
+

今日快报

${renderSnapshots(data.snapshots)}
+
+

最近趋势

+
+ ${data.trends + .map( + (trend) => ` +
+ ${escapeHtml(trend.date)} + 成交 ${escapeHtml(formatMoney(trend.amount))} + 订单 ${escapeHtml(formatNumber(trend.orders))} 单 + 奖金 ${escapeHtml(formatMoney(trend.bonus))} +
`, + ) + .join('')} +
+
+ ${renderRanks('高价值用户', data.userRanks)} + ${renderRanks('团队贡献排行', data.teamRanks)} + ${renderRanks('高货值未成交商品', data.productRanks)} +

风险预警

${renderRisks(data.risks)}
+ +
+ +` +} diff --git a/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx b/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx index 26b37e0..9247b64 100644 --- a/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx +++ b/dashboard-frontend/src/features/boss-dashboard/pages/OperationsPages.tsx @@ -3,7 +3,8 @@ import { useMemo, useState } from 'react' import { MiniTrendChart } from '../../../components/charts/MiniTrendChart' import { KpiCard } from '../../../components/kpi/KpiCard' import { formatMoney, formatNumber } from '../../../utils/format' -import { downloadDailyReportArchive, useDashboardOverview } from '../api' +import { useDashboardOverview } from '../api' +import { buildDailyReportArchiveHtml } from '../archive' import type { DashboardOverview, RiskLevel, SnapshotSlot, TodaySnapshot } from '../types' const snapshotStatusMeta = { @@ -187,7 +188,8 @@ export function DailyReportPage() { const handleArchive = async () => { try { setIsArchiving(true) - const blob = await downloadDailyReportArchive(data.businessDate) + const html = buildDailyReportArchiveHtml(data) + const blob = new Blob([html], { type: 'text/html;charset=utf-8' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url