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))}
${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();
}