import type { DailyBriefingV2Payload, ExecutiveBriefingHero, ExecutiveBriefingSection, } from "@/pages/ai-steward/types"; import { formatTechnicalIdentifierLabel } from "@/lib/display"; import { getBriefingItemDisplaySource, getBriefingItemDisplaySubtitle, getBriefingItemDisplayTitle, groupBriefingItemsByProject, } from "./BriefingSectionCard"; export interface PrintableBriefingPayload { brief_date: string; mode: "snapshot" | "live"; hero: ExecutiveBriefingHero; sections: ExecutiveBriefingSection[]; export_title: string; viewer_scope?: DailyBriefingV2Payload["viewer_scope"]; personal_summary?: DailyBriefingV2Payload["personal_summary"]; personal_workload?: DailyBriefingV2Payload["personal_workload"]; } function escapeHtml(value: string | number | null | undefined): string { return String(value ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function toneLabel(tone: string): string { switch (tone) { case "critical": return "紧急"; case "warning": return "关注"; case "positive": return "正常"; case "info": return "提示"; default: return "状态"; } } function modeLabel(mode: PrintableBriefingPayload["mode"]): string { return mode === "live" ? "实时版" : "晨报快照"; } function formatTimestamp(value?: string | null): string { if (!value) return "-"; const date = new Date(value); if (Number.isNaN(date.getTime())) return String(value); return date.toLocaleString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", }); } function renderMetrics(metrics: ExecutiveBriefingSection["metrics"]): string { if (!metrics.length) { return '

当前没有额外指标。

'; } return `
${metrics .map( (metric) => `
${escapeHtml(metric.label)}
${escapeHtml(metric.value)}
` ) .join("")}
`; } function renderTargetLabel(sectionTitle: string, target?: { label?: string | null; type?: string } | null): string { const label = String(target?.label || "").trim(); if (label) return label; switch (target?.type) { case "project_board": return "进入项目"; case "workload_detail": return "查看负载"; case "user_profile": return "查看成员"; case "ai_steward_tab": return `打开${sectionTitle}`; default: return "查看详情"; } } function renderItemMeta(item: ExecutiveBriefingSection["items"][number]): Array<{ label: string; value: string }> { if (Array.isArray(item.meta) && item.meta.length) { return item.meta; } const fallback: Array<{ label: string; value: string }> = []; if (item.display_source) { fallback.push({ label: "来源", value: item.display_source }); } if (item.project_name) { fallback.push({ label: "项目", value: item.project_name }); } if (item.owner_name) { fallback.push({ label: "负责人", value: item.owner_name }); } if (item.occurred_at) { fallback.push({ label: "时间", value: item.occurred_at }); } return fallback; } function renderItemDetailLines(item: ExecutiveBriefingSection["items"][number]): string[] { if (Array.isArray(item.detail_lines) && item.detail_lines.length) { return item.detail_lines; } const detail = getBriefingItemDisplaySubtitle(item); return detail ? [detail] : []; } function renderItems(section: ExecutiveBriefingSection): string { const groups = groupBriefingItemsByProject(section.items); if (!section.items.length) { return '
当前区块暂无需要补充到报告的异常或跟进项。
'; } return `
${groups .map( (group) => `
${escapeHtml(group.title)}
${escapeHtml(group.categoryLabel)}
${escapeHtml(group.items.length)} 条
${group.items .map((item) => { const meta = renderItemMeta(item); const detailLines = renderItemDetailLines(item); return `
${escapeHtml(getBriefingItemDisplayTitle(item))} ${escapeHtml(getBriefingItemDisplaySource(item))}
${toneLabel(item.tone)}
${escapeHtml(getBriefingItemDisplaySubtitle(item))}
${meta .map( (meta) => `
${escapeHtml(meta.label)}
${escapeHtml(meta.value)}
` ) .join("")}
${item.technical_identifier ? `
${escapeHtml(formatTechnicalIdentifierLabel(item.technical_identifier))}
` : ""}
${detailLines .map((line) => `
${escapeHtml(line)}
`) .join("")}
处理动作:${escapeHtml(renderTargetLabel(section.title, item.target))}
` }) .join("")}
` ) .join("")}
`; } export function buildBriefingPrintHtml(payload: PrintableBriefingPayload, viewerName?: string): string { const hero = payload.hero; const summaryBlock = payload.personal_summary ? `

今日结论

${escapeHtml(payload.personal_summary.headline)}

` : ""; const workloadBlock = payload.personal_workload ? `
个人负载
${escapeHtml(payload.personal_workload.user_name)}
${escapeHtml(payload.personal_workload.workload_ratio)}%
已分配 ${escapeHtml(payload.personal_workload.assigned_hours)} 小时 / 周上限 ${escapeHtml(payload.personal_workload.max_weekly_hours)} 小时
` : ""; const hasWorkloadBlock = Boolean(payload.personal_workload); return ` ${escapeHtml(payload.export_title)}
${escapeHtml(modeLabel(payload.mode))}

${escapeHtml(payload.export_title)}

${escapeHtml(hero.title)}${viewerName ? ` · 查看人:${escapeHtml(viewerName)}` : ""}

查看日期:${escapeHtml(payload.brief_date)} 晨报快照:${escapeHtml(formatTimestamp(hero.baseline_generated_at))} 实时刷新:${escapeHtml(formatTimestamp(hero.live_generated_at))} 异常总数:${escapeHtml(hero.anomaly_count)}
需关注事项
${escapeHtml(hero.decision_count)}
异常总数
${escapeHtml(hero.anomaly_count)}
查看范围
${escapeHtml(payload.viewer_scope === "personal" ? "个人" : "全租户")}
${summaryBlock}
${payload.sections .map( (section) => `

${escapeHtml(section.title)}

${escapeHtml(section.summary)}

${renderMetrics(section.metrics)} ${renderItems(section)}
` ) .join("")}
${hasWorkloadBlock ? `` : ""}
`; } export function printBriefingReport(payload: PrintableBriefingPayload, viewerName?: string): void { const win = window.open("", "_blank"); if (!win) { return; } win.document.write(buildBriefingPrintHtml(payload, viewerName)); win.document.close(); win.focus(); win.print(); }