2025-07-19 02:39:56 +00:00
|
|
|
import { Document, Packer, Paragraph, ImageRun, TextRun, HeadingLevel } from "docx"
|
2025-07-19 02:34:18 +00:00
|
|
|
|
|
|
|
|
interface Screenshot {
|
|
|
|
|
id: string
|
|
|
|
|
name: string
|
|
|
|
|
url: string
|
|
|
|
|
dataUrl?: string
|
|
|
|
|
timestamp: Date
|
|
|
|
|
status: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface DocumentSettings {
|
|
|
|
|
title: string
|
|
|
|
|
author: string
|
|
|
|
|
description: string
|
|
|
|
|
includeTimestamp: boolean
|
|
|
|
|
includePageUrls: boolean
|
|
|
|
|
imageFormat: string
|
|
|
|
|
imageQuality: number
|
|
|
|
|
pageSize: string
|
|
|
|
|
orientation: string
|
|
|
|
|
}
|
2025-07-18 13:47:12 +00:00
|
|
|
|
2025-07-19 02:34:18 +00:00
|
|
|
export async function generateDocx(screenshots: Screenshot[], settings: DocumentSettings): Promise<ArrayBuffer> {
|
2025-07-18 13:47:12 +00:00
|
|
|
try {
|
2025-07-19 02:39:56 +00:00
|
|
|
const children: Paragraph[] = []
|
2025-07-18 13:47:12 +00:00
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 添加标题
|
|
|
|
|
children.push(
|
|
|
|
|
new Paragraph({
|
|
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: settings.title,
|
|
|
|
|
bold: true,
|
|
|
|
|
size: 32,
|
|
|
|
|
}),
|
2025-07-18 13:47:12 +00:00
|
|
|
],
|
2025-07-19 02:39:56 +00:00
|
|
|
heading: HeadingLevel.TITLE,
|
|
|
|
|
}),
|
|
|
|
|
)
|
2025-07-18 13:47:12 +00:00
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 添加描述
|
|
|
|
|
if (settings.description) {
|
|
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
|
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
2025-07-19 02:39:56 +00:00
|
|
|
text: settings.description,
|
|
|
|
|
size: 24,
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
}),
|
2025-07-19 02:39:56 +00:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加生成信息
|
|
|
|
|
if (settings.includeTimestamp) {
|
|
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
|
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
2025-07-19 02:39:56 +00:00
|
|
|
text: `生成时间: ${new Date().toLocaleString()}`,
|
|
|
|
|
size: 20,
|
|
|
|
|
italics: true,
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
}),
|
2025-07-19 02:39:56 +00:00
|
|
|
)
|
2025-07-18 13:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
2025-07-19 02:39:56 +00:00
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: `作者: ${settings.author}`,
|
|
|
|
|
size: 20,
|
|
|
|
|
italics: true,
|
|
|
|
|
}),
|
|
|
|
|
],
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 添加分隔符
|
|
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
2025-07-19 02:39:56 +00:00
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: "─".repeat(50),
|
|
|
|
|
size: 20,
|
|
|
|
|
}),
|
|
|
|
|
],
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 添加每个截图
|
|
|
|
|
for (const screenshot of screenshots) {
|
|
|
|
|
// 页面标题
|
|
|
|
|
children.push(
|
|
|
|
|
new Paragraph({
|
|
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: screenshot.name,
|
|
|
|
|
bold: true,
|
|
|
|
|
size: 28,
|
|
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
heading: HeadingLevel.HEADING_1,
|
|
|
|
|
}),
|
|
|
|
|
)
|
2025-07-18 13:47:12 +00:00
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 页面URL
|
|
|
|
|
if (settings.includePageUrls) {
|
|
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
2025-07-19 02:39:56 +00:00
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: `页面地址: ${screenshot.url}`,
|
|
|
|
|
size: 20,
|
|
|
|
|
color: "666666",
|
|
|
|
|
}),
|
|
|
|
|
],
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
)
|
2025-07-19 02:39:56 +00:00
|
|
|
}
|
2025-07-18 13:47:12 +00:00
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 截图时间
|
|
|
|
|
if (settings.includeTimestamp) {
|
|
|
|
|
children.push(
|
2025-07-19 02:34:18 +00:00
|
|
|
new Paragraph({
|
2025-07-19 02:39:56 +00:00
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: `截图时间: ${screenshot.timestamp.toLocaleString()}`,
|
|
|
|
|
size: 20,
|
|
|
|
|
color: "666666",
|
|
|
|
|
}),
|
|
|
|
|
],
|
2025-07-19 02:34:18 +00:00
|
|
|
}),
|
|
|
|
|
)
|
2025-07-19 02:39:56 +00:00
|
|
|
}
|
2025-07-19 02:34:18 +00:00
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 添加截图
|
|
|
|
|
if (screenshot.dataUrl) {
|
2025-07-18 13:47:12 +00:00
|
|
|
try {
|
2025-07-19 02:39:56 +00:00
|
|
|
// 将 dataUrl 转换为 ArrayBuffer
|
|
|
|
|
const base64Data = screenshot.dataUrl.split(",")[1]
|
|
|
|
|
const binaryString = atob(base64Data)
|
|
|
|
|
const bytes = new Uint8Array(binaryString.length)
|
|
|
|
|
for (let i = 0; i < binaryString.length; i++) {
|
|
|
|
|
bytes[i] = binaryString.charCodeAt(i)
|
2025-07-18 13:47:12 +00:00
|
|
|
}
|
2025-07-19 02:39:56 +00:00
|
|
|
|
|
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
2025-07-19 02:39:56 +00:00
|
|
|
children: [
|
|
|
|
|
new ImageRun({
|
|
|
|
|
data: bytes,
|
|
|
|
|
transformation: {
|
|
|
|
|
width: 600,
|
|
|
|
|
height: 400,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
],
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
2025-07-19 02:39:56 +00:00
|
|
|
)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("添加图片失败:", error)
|
|
|
|
|
children.push(
|
2025-07-18 13:47:12 +00:00
|
|
|
new Paragraph({
|
2025-07-19 02:39:56 +00:00
|
|
|
children: [
|
|
|
|
|
new TextRun({
|
|
|
|
|
text: "[图片加载失败]",
|
|
|
|
|
color: "FF0000",
|
|
|
|
|
italics: true,
|
|
|
|
|
}),
|
|
|
|
|
],
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 添加分隔符
|
|
|
|
|
children.push(
|
|
|
|
|
new Paragraph({
|
2025-07-18 13:47:12 +00:00
|
|
|
children: [
|
2025-07-19 02:39:56 +00:00
|
|
|
new TextRun({
|
|
|
|
|
text: "",
|
2025-07-18 13:47:12 +00:00
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
}),
|
2025-07-19 02:39:56 +00:00
|
|
|
)
|
|
|
|
|
}
|
2025-07-18 13:47:12 +00:00
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 创建文档
|
|
|
|
|
const doc = new Document({
|
|
|
|
|
sections: [
|
|
|
|
|
{
|
|
|
|
|
properties: {},
|
|
|
|
|
children: children,
|
|
|
|
|
},
|
|
|
|
|
],
|
2025-07-18 13:47:12 +00:00
|
|
|
})
|
|
|
|
|
|
2025-07-19 02:39:56 +00:00
|
|
|
// 生成文档
|
|
|
|
|
const buffer = await Packer.toBuffer(doc)
|
2025-07-19 02:34:18 +00:00
|
|
|
return buffer
|
2025-07-18 13:47:12 +00:00
|
|
|
} catch (error) {
|
2025-07-19 02:39:56 +00:00
|
|
|
console.error("生成文档失败:", error)
|
|
|
|
|
throw new Error(`文档生成失败: ${error instanceof Error ? error.message : "未知错误"}`)
|
2025-07-18 13:47:12 +00:00
|
|
|
}
|
|
|
|
|
}
|