import { Document, HeadingLevel, ImageRun, Packer, Paragraph, TableOfContents, TextRun, AlignmentType, PageBreak, BorderStyle, } from "docx" import type { DocumentationPage } from "@/lib/documentation/catalog" export type DocumentationRenderItem = { page: DocumentationPage screenshotPng?: Buffer error?: string } function groupBy(items: T[], getKey: (item: T) => string): Record { const map: Record = {} for (const item of items) { const key = getKey(item) if (!map[key]) map[key] = [] map[key].push(item) } return map } export async function renderDocumentationDocx(items: DocumentationRenderItem[]) { const now = new Date() const title = "Soul派对 - 应用功能文档" const subtitle = `生成时间:${now.toLocaleString("zh-CN", { hour12: false })}` const version = `文档版本:v1.0` const children: Paragraph[] = [] children.push(new Paragraph({ text: "" })) children.push(new Paragraph({ text: "" })) children.push( new Paragraph({ text: title, heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, }), ) children.push( new Paragraph({ children: [new TextRun({ text: subtitle, size: 24 })], alignment: AlignmentType.CENTER, }), ) children.push( new Paragraph({ children: [new TextRun({ text: version, size: 20, color: "666666" })], alignment: AlignmentType.CENTER, }), ) children.push(new Paragraph({ text: "" })) children.push(new Paragraph({ text: "" })) children.push( new Paragraph({ text: "文档概述", heading: HeadingLevel.HEADING_1, }), ) children.push( new Paragraph({ children: [ new TextRun({ text: "本文档自动生成,包含应用程序所有核心页面的功能说明与界面截图。文档按功能模块分组,便于快速查阅和理解应用结构。", }), ], }), ) children.push( new Paragraph({ children: [ new TextRun({ text: `共包含 ${items.length} 个页面,${Object.keys(groupBy(items, (i) => i.page.group)).length} 个功能模块。`, }), ], }), ) children.push(new Paragraph({ text: "" })) children.push( new Paragraph({ text: "目录", heading: HeadingLevel.HEADING_1, }), ) children.push( new TableOfContents("目录", { hyperlink: true, headingStyleRange: "1-3", }), ) children.push( new Paragraph({ children: [new PageBreak()], }), ) const grouped = groupBy(items, (i) => i.page.group) const groupNames = Object.keys(grouped) let pageNumber = 1 for (const groupName of groupNames) { children.push( new Paragraph({ text: groupName, heading: HeadingLevel.HEADING_1, border: { bottom: { color: "2DD4BF", size: 6, style: BorderStyle.SINGLE }, }, }), ) children.push(new Paragraph({ text: "" })) for (const item of grouped[groupName]) { const { page } = item children.push( new Paragraph({ text: `${pageNumber}. ${page.title}`, heading: HeadingLevel.HEADING_2, }), ) if (page.subtitle) { children.push( new Paragraph({ children: [new TextRun({ text: page.subtitle, italics: true, color: "666666" })], }), ) } children.push( new Paragraph({ children: [ new TextRun({ text: "页面路径:", bold: true }), new TextRun({ text: page.path, color: "2563EB" }), ], }), ) if (page.caption) { children.push(new Paragraph({ text: "" })) children.push( new Paragraph({ children: [new TextRun({ text: "功能说明:", bold: true })], }), ) children.push( new Paragraph({ children: [new TextRun({ text: page.caption })], }), ) } children.push(new Paragraph({ text: "" })) if (item.error) { children.push( new Paragraph({ children: [ new TextRun({ text: "截图状态:", bold: true }), new TextRun({ text: `失败 - ${item.error}`, color: "DC2626" }), ], }), ) } else if (item.screenshotPng) { children.push( new Paragraph({ children: [new TextRun({ text: "界面截图:", bold: true })], }), ) children.push(new Paragraph({ text: "" })) children.push( new Paragraph({ children: [ new ImageRun({ data: item.screenshotPng, transformation: { width: 320, height: 693 }, }), ], alignment: AlignmentType.CENTER, }), ) children.push( new Paragraph({ children: [new TextRun({ text: `图${pageNumber}: ${page.title}界面`, size: 20, color: "666666" })], alignment: AlignmentType.CENTER, }), ) } children.push(new Paragraph({ text: "" })) children.push(new Paragraph({ text: "" })) pageNumber++ } children.push( new Paragraph({ children: [new PageBreak()], }), ) } children.push( new Paragraph({ text: "附录", heading: HeadingLevel.HEADING_1, }), ) children.push( new Paragraph({ children: [new TextRun({ text: "技术说明", bold: true })], }), ) children.push( new Paragraph({ children: [ new TextRun({ text: "• 截图尺寸:430×932像素 (iPhone 14 Pro Max)", }), ], }), ) children.push( new Paragraph({ children: [ new TextRun({ text: "• 截图方式:Playwright自动化浏览器截图", }), ], }), ) children.push( new Paragraph({ children: [ new TextRun({ text: "• 文档格式:Microsoft Word (.docx)", }), ], }), ) children.push(new Paragraph({ text: "" })) children.push( new Paragraph({ children: [new TextRun({ text: "本文档由系统自动生成,如有问题请联系技术支持。", color: "666666", size: 20 })], alignment: AlignmentType.CENTER, }), ) const doc = new Document({ title: "Soul派对 - 应用功能文档", description: "自动生成的应用功能文档", creator: "Soul派对文档生成器", sections: [ { properties: {}, children, }, ], }) return await Packer.toBuffer(doc) }