最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue使用docx和file-saver實現(xiàn)Word文檔導(dǎo)出功能

 更新時間:2026年01月21日 09:21:04   作者:51349592  
在實際的前端項目中,經(jīng)常需要將數(shù)據(jù)或頁面內(nèi)容導(dǎo)出為Word文檔,利用docx和file-saver庫,可以實現(xiàn)強(qiáng)大的文檔導(dǎo)出功能,所以本文介紹了如何使用docx和file-saver庫在前端項目中導(dǎo)出Word文檔,需要的朋友可以參考下

在實際的前端項目中,經(jīng)常需要將數(shù)據(jù)或頁面內(nèi)容導(dǎo)出為Word文檔。利用docxfile-saver庫,可以實現(xiàn)強(qiáng)大的文檔導(dǎo)出功能,滿足報表生成、數(shù)據(jù)導(dǎo)出、文檔打印等需求。

  • docx:純JavaScript的Word文檔生成庫,功能全面,無依賴
  • file-saver:簡化文件保存操作,兼容性好

安裝

# 安裝最新版本
npm install docx file-saver

# 或使用yarn
yarn add docx file-saver

# 或使用pnpm
pnpm add docx file-saver

版本兼容性

版本特點建議
docx 8.x傳統(tǒng)API,文檔豐富已有項目維護(hù)
docx 9.x新API,直接配置(本文基于此)新項目首選
file-saver 2.x標(biāo)準(zhǔn)API,兼容性好推薦使用

基礎(chǔ)示例

<template>
  <div>
    <button @click="exportSimpleDocument" :disabled="loading">
      {{ loading ? "生成中..." : "導(dǎo)出Word文檔" }}
    </button>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { Document, Packer, Paragraph, TextRun } from "docx";
import { saveAs } from "file-saver";

const loading = ref(false);

async function exportSimpleDocument() {
  loading.value = true;

  try {
    // 1. 創(chuàng)建文檔對象
    const doc = new Document({
      sections: [
        {
          children: [
            new Paragraph({
              children: [new TextRun("歡迎使用文檔導(dǎo)出功能")],
            }),
            new Paragraph({
              children: [new TextRun("這是一個簡單的示例文檔。")],
            }),
          ],
        },
      ],
    });

    // 2. 生成Blob
    const blob = await Packer.toBlob(doc);

    // 3. 保存文件
    saveAs(blob, "示例文檔.docx");
  } catch (error) {
    console.error("導(dǎo)出失敗:", error);
  } finally {
    loading.value = false;
  }
}
</script>

docx庫詳解

什么是docx?

docx 是一個純 JavaScript 庫,用于 創(chuàng)建和操作 Microsoft Word (.docx) 文件。它可以在 Node.js 和瀏覽器環(huán)境中運行,不需要安裝 Office 軟件。

主要特性

  • 完整的 Word 文檔功能支持
  • 純 JavaScript 實現(xiàn),無外部依賴
  • 支持 Node.js 和瀏覽器
  • 豐富的樣式和格式選項
  • 表格、圖像、列表等元素支持
  • TypeScript 友好,有完整的類型提示
## 瀏覽器環(huán)境注意事項

1. 文件大小限制:瀏覽器中生成大文檔可能導(dǎo)致內(nèi)存問題
2. 中文支持:必須顯式設(shè)置中文字體
3. 圖片處理:瀏覽器中需要使用不同的圖片加載方式

核心概念架構(gòu)

文檔結(jié)構(gòu)模型

文檔 (Document)
    ↓
分段 (Section) [可以有多個]
    ↓
各種元素 (Paragraph, Table, Image...)

基本工作流程

1. 創(chuàng)建文檔對象 (Document)
   ↓
2. 添加分段 (Sections)
   ↓
3. 向分段添加內(nèi)容元素
   (Paragraphs, Tables, Images...)
   ↓
4. 配置元素的樣式和屬性
   ↓
5. 使用 Packer 轉(zhuǎn)換為文件
   ↓
6. 保存或下載文件
// 導(dǎo)入必要的模塊
import { 
  Document,     // 文檔
  Paragraph,    // 段落
  TextRun,      // 文本塊
  Packer        // 打包器(用于生成文件)
} from "docx";

async function exportDocument() {
  try {
    // 1. 創(chuàng)建文檔對象
    const doc = new Document({
      // 2. 文檔內(nèi)容分段(可以包含多個分段)
      sections: [
        {
          properties: {}, // 分段屬性(頁面大小、邊距等)
          children: [
            // 3. 分段中的內(nèi)容元素
            new Paragraph({
              // 4. 段落
              children: [
                // 5. 段落中的文本塊
                new TextRun("Hello, World!"),
              ],
            }),
          ],
        },
      ],
    });
    // 6. 將文檔對象轉(zhuǎn)換為文件
    const buffer = await Packer.toBuffer(doc); // Node.js 環(huán)境
    const blob = await Packer.toBlob(doc); // 瀏覽器環(huán)境
  } catch (error) {
    console.error("導(dǎo)出失敗:", error);  // 錯誤提示
  }
}

Packer工具對比

docx 庫中,Packer 類是用于將文檔對象序列化為不同格式的工具。toBuffer()toBlob() 是最常用的兩個方法,它們用于不同的運行環(huán)境。

特性Packer.toBuffer()Packer.toBlob()
運行環(huán)境Node.js瀏覽器
返回值BufferBlob
存儲方式二進(jìn)制緩沖區(qū)二進(jìn)制大對象
文件操作使用 fs 模塊使用 FileSaver 等庫
內(nèi)存使用直接內(nèi)存操作Blob URL 引用
典型用途服務(wù)器端保存文件客戶端下載文件

基礎(chǔ)組件

1. Document(文檔)

文檔是最頂層的容器,相當(dāng)于一個Word文件。

const doc = new Document({
  creator: "作者名稱",      // 創(chuàng)建者
  title: "文檔標(biāo)題",        // 文檔標(biāo)題
  description: "文檔描述",  // 描述
  sections: []            // 包含的分段
});

2. Section(分段)

文檔由多個分段組成,每個分段可以有不同的頁面設(shè)置。

const section = {
  properties: {
    page: {
      size: {
        width: 12240,  // A4 寬度 (單位:twips)
        height: 15840, // A4 高度
      },
      margin: {
        top: 1440,     // 上邊距 2.54cm
        right: 1440,   // 右邊距
        bottom: 1440,  // 下邊距
        left: 1440,    // 左邊距
      }
    }
  },
  children: []  // 這個分段中的內(nèi)容
};

單位解釋docx使用twips作為Word文檔的標(biāo)準(zhǔn)單位系統(tǒng)。

單位說明換算關(guān)系
twipsWord標(biāo)準(zhǔn)單位1 twip = 1/20 pt
pt1 pt = 20 twips
inch英寸1 inch = 1440 twips
cm厘米1 cm ≈ 567 twips

常用預(yù)設(shè)值

  • 單倍行距:240 twips
  • 1.5倍行距:360 twips
  • 2倍行距:480 twips
  • A4寬度:12240 twips (21cm)
  • A4高度:15840 twips (29.7cm)

3. Paragraph(段落)

段落是文檔的基本內(nèi)容單元,相當(dāng)于 Word 中的一個段落。

const paragraph = new Paragraph({
  children: [ /* 文本塊或其他內(nèi)聯(lián)元素 */ ],
  alignment: "left",  // 對齊方式:left, center, right, both
  spacing: {         // 間距
    before: 200,     // 段前間距
    after: 200,      // 段后間距
    line: 240        // 行間距(240 = 單倍行距)
  },
  indent: {          // 縮進(jìn)
    firstLine: 720   // 首行縮進(jìn) 720twips = 0.5英寸
  }
});

1). 標(biāo)題 (Headings)

import { HeadingLevel } from "docx";

const headings = [
  new Paragraph({
    text: "一級標(biāo)題",
    heading: HeadingLevel.HEADING_1,
  }),
  new Paragraph({
    text: "二級標(biāo)題",
    heading: HeadingLevel.HEADING_2,
  }),
  new Paragraph({
    text: "三級標(biāo)題",
    heading: HeadingLevel.HEADING_3,
  })
];

2). 列表 (Lists)

列表類型概覽

列表類型描述配置方式示例
有序列表帶數(shù)字/字母編號numbering屬性1., 2., 3.
無序列表帶項目符號bullet屬性• 項目1
多級列表嵌套的列表組合使用1.1., 1.2.

->無序列表:

// 無序列表(項目符號)
const bulletList = [
  new Paragraph({
    text: "項目1",
    bullet: { level: 0 },  // level 表示縮進(jìn)級別
  }),
  new Paragraph({
    text: "項目2",
    bullet: { level: 0 },
  }),
];

->有序列表:

有序列表(編號列表)需要兩步配置:

  1. 定義編號格式(在 Documentnumbering.config 中)
  2. 應(yīng)用編號引用(在 Paragraphnumbering 屬性中)
const doc = new Document({
  numbering: {
    config: [
      // 配置1:數(shù)字編號 (1., 2., 3.)
      {
        reference: "decimal-list",	// 唯一引用標(biāo)識
        levels: [{
          level: 0,				  // 列表級別
          format: "decimal",      // 格式類型
          text: "%1.",            // 顯示模板
          alignment: "left",      // 對齊方式
          style: {
            paragraph: {
              indent: { 
                left: 720,       // 左縮進(jìn) (0.5英寸)
                hanging: 360     // 懸掛縮進(jìn) (0.25英寸)
              }
            }
          }
        }]
      },
      // 配置2:大寫字母編號 (A., B., C.)
      {
        reference: "upper-letter-list",
        levels: [{
          level: 0,
          format: "upperLetter",
          text: "%1.",
          alignment: "left"
        }]
      },
      // 配置3:羅馬數(shù)字編號 (I., II., III.)
      {
        reference: "roman-list",
        levels: [{
          level: 0,
          format: "upperRoman",
          text: "%1.",
          alignment: "left"
        }]
      }
    ]
  },
  sections: [{
    children: [
      // 使用不同格式的有序列表
      new Paragraph({
        text: "數(shù)字列表項",
        numbering: { 
          reference: "decimal-list",   // 編號引用名稱
          level: 0 				  	   // 縮進(jìn)級別
        }
      }),
      new Paragraph({
        text: "字母列表項",
        numbering: { reference: "upper-letter-list", level: 0 }
      }),
      new Paragraph({
        text: "羅馬數(shù)字列表項",
        numbering: { reference: "roman-list", level: 0 }
      })
    ]
  }]
});

instance 屬性:

在有序列表中可以使用instance創(chuàng)建獨立的編號序列

一個 reference + level 定義一個編號格式
┌─────────────────────────────────────┐
│ reference: "demo-list", level: 0      │
│ format: "decimal", text: "%1."      │
└─────────────────────────────────────┘
    │
    ├─ instance: 1 (實例1)
    │     ├─ 段落1 → 顯示 "1."
    │     ├─ 段落2 → 顯示 "2."
    │     └─ 段落3 → 顯示 "3."
    │
    ├─ instance: 2 (實例2)  
    │     ├─ 段落1 → 顯示 "1." ← 重新開始!
    │     ├─ 段落2 → 顯示 "2."
    │     └─ 段落3 → 顯示 "3."
    │
    └─ instance: 3 (實例3)
          ├─ 段落1 → 顯示 "1." ← 又重新開始!
          └─ 段落2 → 顯示 "2."

示例:

const doc = new Document({
  // 定義編號格式
  numbering: {
    config: [
      {
        reference: "demo-list",
        levels: [{ level: 0, format: "decimal", text: "%1." }],
      },
    ],
  },
  sections: [
    {
      properties: {},
      children: [
        // 實例1
        new Paragraph({
          text: "實例1-第一項",
          numbering: { reference: "demo-list", level: 0, instance: 1 },
        }),

        new Paragraph({
          text: "實例1-第二項",
          numbering: { reference: "demo-list", level: 0, instance: 1 },
        }),
        // 實例2(重新開始編號)
        new Paragraph({
          text: "實例2-第一項",
          numbering: { reference: "demo-list", level: 0, instance: 2 },
        }),

        new Paragraph({
          text: "實例2-第二項",
          numbering: { reference: "demo-list", level: 0, instance: 2 }, // 繼續(xù)實例2
        }),
      ],
    },
  ],
});
// 結(jié)果:1., 2., 1., 2.

重要提示

  • 每個instance值創(chuàng)建一個獨立的編號序列;
  • 相同的instance值共享編號計數(shù);
  • 不同的instance值各自從1開始編號;
  • 在docx 9.x版本中,不指定instance時行為不確定,可能不會自動繼續(xù)使用前一個實例,因此建議明確指定;`

4.TextRun(文本塊)

文本塊是段落中的具體文本內(nèi)容,可以設(shè)置樣式。

const textRun = new TextRun({
  text: "這是文本內(nèi)容",
  bold: true,        // 加粗
  italics: false,    // 斜體
  underline: {},     // 下劃線(空對象表示單下劃線)
  color: "FF0000",   // 顏色(十六進(jìn)制,不帶#)
  font: "宋體",      // 字體
  size: 24,          // 字體大?。?4 = 12pt)
  highlight: "yellow" // 高亮
});

5. Tables (表格)

import { Table, TableRow, TableCell } from "docx";

const table = new Table({
  rows: [
    // 表頭行
    new TableRow({
      children: [
        new TableCell({
          children: [new Paragraph("姓名")],
          width: { size: 30, type: "pct" },  // 寬度占30%
        }),
        new TableCell({
          children: [new Paragraph("年齡")],
          width: { size: 20, type: "pct" },
        }),
      ],
    }),
    // 數(shù)據(jù)行
    new TableRow({
      children: [
        new TableCell({
          children: [new Paragraph("張三")],
        }),
        new TableCell({
          children: [new Paragraph("25")],
        }),
      ],
    }),
  ],
  width: { size: 100, type: "pct" },  // 表格寬度占100%
});

6. Images(圖像)

// Node.js環(huán)境
import fs from "fs";

const image = new ImageRun({
  data: fs.readFileSync("path/to/image.png"),
  transformation: {
    width: 200,
    height: 200,
  },
});

// 瀏覽器環(huán)境
const image = new ImageRun({
  data: await fetch('image.png').then(res => res.arrayBuffer()),
  transformation: {
    width: 200,
    height: 200,
  },
});

樣式設(shè)置

stylesparagraphStylesdocx 庫中管理文檔樣式的兩個核心配置項,它們有不同的用途和層級關(guān)系。

方式配置位置作用范圍優(yōu)先級適用場景
全局默認(rèn)樣式Document.styles.default整個文檔設(shè)置文檔基礎(chǔ)樣式
自定義段落樣式Document.styles.paragraphStyles特定段落創(chuàng)建可重用樣式
字符樣式Document.styles.characterStyles文本片段文本級樣式復(fù)用
表格樣式Document.styles.tableStyles表格表格統(tǒng)一樣式
內(nèi)聯(lián)樣式Paragraph, TextRun 參數(shù)單個元素特定元素樣式
樣式繼承鏈basedOn, next樣式間-樣式復(fù)用和關(guān)聯(lián)

樣式層級結(jié)構(gòu):

Document (文檔)
├── styles (文檔樣式)
│   ├── default (默認(rèn)樣式)
│   ├── characterStyles (字符樣式)
│   ├── paragraphStyles (段落樣式)
│   └── tableStyles (表格樣式)
└── sections (內(nèi)容分段)
    └── Paragraph (段落)
        ├── style (引用paragraphStyles中的樣式)
        └── children (內(nèi)容)

常用樣式屬性

屬性說明示例值
font字體“宋體”, “微軟雅黑”
size字號24 (12pt), 28 (14pt)
bold加粗true / false
italics斜體true / false
color顏色“FF0000” (紅色)
underline下劃線{} (單線)
highlight高亮“yellow”, “green”

1. 全局默認(rèn)樣式 (styles.default)

這是最基礎(chǔ)的樣式配置方式,為整個文檔設(shè)置默認(rèn)樣式。

配置結(jié)構(gòu):

const doc = new Document({
  styles: {
    default: {
      // 文檔默認(rèn)字符樣式
      document: {
        run: {
          font: "宋體",      // 字體
          size: 24,         // 字號
          color: "000000",  // 顏色
        },
        paragraph: {
          spacing: { line: 360 },  // 行距
        }
      }, 
      // 標(biāo)題樣式
      heading1: {
        run: {
          font: "宋體",
          size: 32,
          bold: true,
        },
        paragraph: {
          spacing: { before: 240, after: 120 },
        }
      },
      heading2: {
        run: {
          font: "宋體",
          size: 28,
          bold: true,
        }
      },
      heading3: {
        run: {
          font: "宋體",
          size: 26,
          bold: true,
        }
      },
      // 標(biāo)題樣式
      title: {
        run: {
          font: "宋體",
          size: 36,
          bold: true,
        }
      },
      // 列表樣式
      listParagraph: {
        run: {
          font: "宋體",
          size: 24,
        }
      }
    }
  },
  sections: [{
    children: [
      // 自動應(yīng)用 heading1 樣式
      new Paragraph({
        text: "文檔標(biāo)題",
        heading: "Heading1"
      }),
      
      // 自動應(yīng)用全局默認(rèn)樣式
      new Paragraph({
        children: [
          new TextRun("正文內(nèi)容")
        ]
      })
    ]
  }]
});

示例:

import { Document, Paragraph, TextRun, Packer } from "docx";

const doc = new Document({
  styles: {
    default: {
      // 設(shè)置文檔全局樣式
      document: {
        run: {
          font: "宋體",
          size: 24,        // 小四
          color: "333333",
        },
        paragraph: {
          spacing: { 
            line: 360,     // 1.5倍行距
            after: 100     // 段落間距
          },
        }
      },
      // 標(biāo)題樣式
      heading1: {
        run: {
          font: "黑體",
          size: 32,
          color: "000080", // 深藍(lán)色
          bold: true,
        },
        paragraph: {
          spacing: { before: 240, after: 120 },
          alignment: "left",
        }
      },
      heading2: {
        run: {
          font: "黑體",
          size: 28,
          color: "000080",
          bold: true,
        }
      }
    }
  },
  
  sections: [{
    children: [
      // 自動應(yīng)用 heading1 樣式
      new Paragraph({
        text: "文檔標(biāo)題",
        heading: "Heading1"
      }),
      
      // 自動應(yīng)用全局默認(rèn)樣式
      new Paragraph({
        children: [
          new TextRun("正文內(nèi)容")
        ]
      })
    ]
  }]
});

2. 自定義段落樣式 (paragraphStyles)

這是最常用和最靈活的方式,用于定義可重用的段落樣式。

配置結(jié)構(gòu):

const doc = new Document({
  styles: {
    default:{},
    paragraphStyles: [
      {
        id: "樣式ID", // 樣式ID,必填,用于引用
        name: "樣式名稱", // 樣式名稱,在Word中顯示的名稱
        basedOn: "Normal", // 基于哪個樣式(可選)
        next: "Normal", // 按Enter后的樣式(可選)
        quickFormat: true, // 是否在快速樣式庫顯示
        // 字符樣式
        run: {
          font: "宋體",
          size: 24,
          color: "000000",
          bold: false,
          italics: false,
          underline: {},
        },
        // 段落樣式
        paragraph: {
          spacing: {
            line: 360, // 行間距
            before: 0, // 段前間距
            after: 100, // 段后間距
          },
          indent: {
            firstLine: 720, // 首行縮進(jìn)
            left: 0, // 左縮進(jìn)
            right: 0, // 右縮進(jìn)
          },
          alignment: "left", // 對齊:left, center, right, both
          border: {
            // 邊框
            top: { style: "single", size: 4, color: "000000" },
          },
          shading: {
            // 底紋
            fill: "F5F5F5",
          },
          pageBreakBefore: false, // 是否段前分頁
          keepLines: true, // 保持在同一頁
          keepNext: true, // 與下段同頁
        },
      },
    ],
  },
});

示例:

import { Document, Paragraph } from "docx";

const doc = new Document({
  styles: {
    paragraphStyles: [
      {
        id: "ReportTitle",
        name: "報告標(biāo)題",
        run: {
          font: "黑體",
          size: 36,
          color: "000080",
          bold: true,
        },
        paragraph: {
          alignment: "center",
          spacing: { before: 400, after: 300 },
        },
      },
      {
        id: "Important",
        name: "重點強(qiáng)調(diào)",
        run: {
          color: "FF0000",
          bold: true,
          highlight: "yellow",
        },
        paragraph: {
          shading: { fill: "FFF8DC" },
        },
      },
    ],
  },
  sections: [
    {
      children: [
        new Paragraph({
          text: "年度技術(shù)報告",
          style: "ReportTitle",
        }),
        new Paragraph({
          text: "?? 注意:此文檔為機(jī)密文件",
          style: "Important",
        }),
      ],
    },
  ],
});

3. 字符樣式 (characterStyles)

用于定義可重用的文本片段樣式,適用于多個段落中的相同文本樣式。

const doc = new Document({
  styles: {
    default:{},
    paragraphStyles:[],
    characterStyles: [
      {
        id: "RedBold",
        name: "紅字加粗",
        run: {
          color: "FF0000",
          bold: true,
        }
      },
      {
        id: "Highlight",
        name: "高亮",
        run: {
          highlight: "yellow",
          bold: true,
        }
      },
      {
        id: "LinkStyle",
        name: "鏈接樣式",
        run: {
          color: "0000FF",
          underline: {},
        }
      }
    ]
  },
  sections: [{
    children: [
      new Paragraph({
        children: [
          new TextRun({
            text: "重要通知:",
            style: "RedBold"  // 應(yīng)用字符樣式
          }),
          new TextRun("請及時查看最新消息")
        ]
      })
    ]
  }]
});

4. 內(nèi)聯(lián)樣式(直接設(shè)置)

最高優(yōu)先級的方式,直接在創(chuàng)建元素時設(shè)置樣式。

import { Paragraph, TextRun } from "docx";

// 1. 在 Paragraph 中直接設(shè)置
const paragraph1 = new Paragraph({
  text: "這個段落有直接樣式",
  alignment: "center",           // 對齊方式
  spacing: { 
    line: 400,                   // 行間距
    before: 200,                 // 段前
    after: 200                   // 段后
  },
  indent: {
    firstLine: 720,              // 首行縮進(jìn)
  },
  border: {
    bottom: {                    // 下邊框
      style: "single",
      size: 4,
      color: "000000"
    }
  },
  shading: {                     // 背景色
    fill: "F0F0F0"
  }
});

// 2. 在 TextRun 中直接設(shè)置
const paragraph2 = new Paragraph({
  children: [
    new TextRun({
      text: "這段文本",
      font: "黑體",               // 字體
      size: 28,                  // 字號
      bold: true,                // 加粗
      italics: false,            // 斜體
      underline: {},             // 下劃線
      color: "FF0000",           // 顏色
      highlight: "yellow",       // 高亮
      shading: {                 // 文字底紋
        fill: "FFCCCC"
      },
      strike: true,              // 刪除線
      doubleStrike: false,       // 雙刪除線
      allCaps: false,            // 全部大寫
      smallCaps: false           // 小型大寫
    })
  ]
});

5. 樣式繼承和關(guān)聯(lián)

1. 基于現(xiàn)有樣式 (basedOn)

優(yōu)點:保持樣式一致性和便于批量修改。

const doc = new Document({
  paragraphStyles: [
    // 基礎(chǔ)樣式
    {
      id: "BaseStyle",
      name: "基礎(chǔ)樣式",
      run: {
        font: "宋體",
        size: 24,
        color: "333333",
      },
      paragraph: {
        spacing: { line: 360, after: 100 },
      }
    },
    // 繼承基礎(chǔ)樣式,只修改需要的屬性
    {
      id: "WarningStyle",
      name: "警告樣式",
      basedOn: "BaseStyle",  // 繼承 BaseStyle
      run: {
        color: "FF0000",     // 只修改顏色
        bold: true,          // 添加加粗
      }
      // 其他屬性自動從 BaseStyle 繼承
    },
    // 多層繼承
    {
      id: "CriticalWarning",
      name: "嚴(yán)重警告",
      basedOn: "WarningStyle",  // 繼承 WarningStyle
      run: {
        highlight: "yellow",    // 添加高亮
        size: 28,               // 修改字號
      },
      paragraph: {
        shading: {              // 添加背景色
          fill: "FFEEEE"
        }
      }
    }
  ]
});

2. 樣式鏈 (next)

const doc = new Document({
  paragraphStyles: [
    {
      id: "TitleStyle",
      name: "標(biāo)題樣式",
      basedOn: "Title",
      next: "ChapterIntro",  // 按Enter后自動使用 ChapterIntro
      // ...
    },
    {
      id: "ChapterIntro",
      name: "章節(jié)引言",
      basedOn: "Normal",
      next: "Normal",        // 再按Enter回到正文
      // ...
    }
  ],
  sections: [{
    children: [
      new Paragraph({
        text: "文檔標(biāo)題",
        style: "TitleStyle"
      }),
      // 按Enter后,下一段落會自動使用 ChapterIntro 樣式
    ]
  }]
});

6. 混合使用所有方式

實際項目中,通常需要混合使用多種方式:

const doc = new Document({
  // 1. 全局默認(rèn)樣式
  styles: {
    default: {
      document: {
        run: {
          font: "宋體",
          size: 24,
          color: "333333",
        },
        paragraph: {
          spacing: { line: 360 },
        }
      }
    },
    // 字符樣式
    characterStyles: [
      {
        id: "Keyword",
        name: "關(guān)鍵詞",
        run: {
          bold: true,
          color: "0000FF",
        }
      }
    ]
  },
  // 2. 自定義段落樣式
  paragraphStyles: [
    {
      id: "CompanyReport",
      name: "公司報告樣式",
      basedOn: "Normal",
      run: {
        font: "微軟雅黑",
        size: 21,
        color: "000000",
      },
      paragraph: {
        spacing: { line: 315, after: 80 },
        indent: { firstLine: 420 },
      }
    }
  ],
  sections: [{
    children: [
      // 3. 應(yīng)用自定義段落樣式
      new Paragraph({
        text: "2024年度報告",
        style: "CompanyReport"
      }),
      // 4. 內(nèi)聯(lián)樣式 + 字符樣式
      new Paragraph({
        children: [
          new TextRun({
            text: "關(guān)鍵詞:",
            style: "Keyword"  // 字符樣式
          }),
          new TextRun({
            text: "數(shù)字化轉(zhuǎn)型",
            font: "黑體",     // 內(nèi)聯(lián)樣式
            size: 26,
            bold: true,
            color: "FF0000"
          })
        ],
        spacing: { line: 400 }  // 段落內(nèi)聯(lián)樣式
      })
    ]
  }]
});

樣式優(yōu)先級示例:

const doc = new Document({
  // 1. 全局默認(rèn)樣式(最低優(yōu)先級)
  styles: {
    default: {
      document: {
        run: {
          font: "宋體",      // 會被覆蓋
          size: 24,          // 會被覆蓋
          color: "000000",   // 會被覆蓋
        }
      }
    }
  },
  // 2. 段落樣式(中等優(yōu)先級)
  paragraphStyles: [
    {
      id: "MyParaStyle",
      name: "段落樣式",
      run: {
        font: "黑體",        // 會被TextRun覆蓋
        size: 28,            // 會被TextRun覆蓋
        color: "0000FF",     // 會被TextRun覆蓋
      }
    }
  ],
  sections: [{
    children: [
      // 3. 段落直接樣式 + 引用段落樣式
      new Paragraph({
        text: "示例文本",
        style: "MyParaStyle",  // 應(yīng)用段落樣式
        alignment: "center",   // 段落直接樣式
        spacing: { line: 400 }, // 段落直接樣式
        
        // 4. TextRun 內(nèi)聯(lián)樣式(最高優(yōu)先級)
        children: [
          new TextRun({
            text: "部分文字",
            font: "楷體",      // 覆蓋段落樣式
            size: 32,          // 覆蓋段落樣式
            color: "FF0000",   // 覆蓋段落樣式
            bold: true,        // 新增屬性
          }),
          new TextRun("其他文字")  // 使用段落樣式
        ]
      })
    ]
  }]
});
// 最終效果:
// - "部分文字": 楷體、32號、紅色、加粗
// - "其他文字": 黑體、28號、藍(lán)色
// - 整個段落: 居中、1.67倍行距

示例:

創(chuàng)建一份月度工作報告:

import {
  Document,
  Packer,
  Paragraph,
  TextRun,
  HeadingLevel,
  Table,
  TableRow,
  TableCell,
  AlignmentType
} from "docx";

async function createMonthlyReport(data) {
  // 1. 創(chuàng)建文檔
  const doc = new Document({
    creator: data.creator,
    title: `${data.month}月度工作報告`,
    description: `${data.year}年${data.month}月工作報告`,
    
    // 樣式配置
    styles: {
      default: {
        document: {
          run: {
            font: "微軟雅黑",
            size: 24,
            color: "333333",
          }
        }
      }
    },
      
    sections: [{
      properties: {
        page: {
          size: { width: 12240, height: 15840 },  // A4
          margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }
        }
      },
      children: [
        // 標(biāo)題
        new Paragraph({
          text: `${data.month}月度工作報告`,
          heading: HeadingLevel.TITLE,
          alignment: AlignmentType.CENTER,
          spacing: { after: 400 }
        }),
        
        // 基本信息
        new Paragraph({
          children: [
            new TextRun({ text: "報告人:", bold: true }),
            new TextRun(data.creator)
          ],
          spacing: { after: 100 }
        }),
        
        // 工作完成情況
        new Paragraph({
          text: "一、工作完成情況",
          heading: HeadingLevel.HEADING_1,
          spacing: { before: 200, after: 100 }
        }),
        
        // 任務(wù)列表
        ...data.tasks.map(task => 
          new Paragraph({
            text: `? ${task.description}`,
            bullet: { level: 0 },
            spacing: { after: 40 }
          })
        ),
        
        // 數(shù)據(jù)表格
        new Table({
          rows: [
            // 表頭
            new TableRow({
              children: [
                new TableCell({ 
                  children: [new Paragraph("項目")],
                  shading: { fill: "F0F0F0" }
                }),
                new TableCell({ 
                  children: [new Paragraph("進(jìn)度")],
                  shading: { fill: "F0F0F0" }
                }),
                new TableCell({ 
                  children: [new Paragraph("負(fù)責(zé)人")],
                  shading: { fill: "F0F0F0" }
                })
              ]
            }),
            // 數(shù)據(jù)行
            ...data.projects.map(project => 
              new TableRow({
                children: [
                  new TableCell({ children: [new Paragraph(project.name)] }),
                  new TableCell({ 
                    children: [new Paragraph({
                      children: [
                        new TextRun({
                          text: `${project.progress}%`,
                          color: project.progress >= 80 ? "008000" : 
                                 project.progress >= 50 ? "FFA500" : "FF0000"
                        })
                      ]
                    })]
                  }),
                  new TableCell({ children: [new Paragraph(project.owner)] })
                ]
              })
            )
          ],
          width: { size: 100, type: "pct" }
        })
      ]
    }]
  });
  
  // 生成文件
  const buffer = await Packer.toBuffer(doc);
  return buffer;
}

// 使用示例
const reportData = {
  creator: "張三",
  month: "1月",
  year: "2024",
  tasks: [
    { description: "完成項目需求分析" },
    { description: "完成核心功能開發(fā)" },
    { description: "完成單元測試" }
  ],
  projects: [
    { name: "項目A", progress: 80, owner: "張三" },
    { name: "項目B", progress: 60, owner: "李四" },
    { name: "項目C", progress: 95, owner: "王五" }
  ]
};

const docBuffer = await createMonthlyReport(reportData);

優(yōu)化建議:

1. 樣式配置工具

使用工具函數(shù)統(tǒng)一管理樣式:

// styles/config.js
export const STYLE_CONFIG = {
  // 顏色配置
  colors: {
    primary: "#000080",    // 主色
    secondary: "#800000",  // 輔色
    success: "#008000",    // 成功
    warning: "#FFA500",    // 警告
    danger: "#FF0000",     // 危險
    info: "#008080",       // 信息
    light: "#F5F5F5",      // 淺色
    dark: "#333333",       // 深色
  },
  
  // 字體配置
  fonts: {
    chinese: "宋體",
    heading: "黑體",
    code: "Consolas",
    english: "Arial",
  },
  
  // 字號配置(單位:磅的2倍)
  sizes: {
    title: 36,     // 18pt
    h1: 32,        // 16pt
    h2: 28,        // 14pt
    h3: 26,        // 13pt
    normal: 24,    // 12pt (小四)
    small: 21,     // 10.5pt (五號)
    tiny: 18,      // 9pt (小五)
  },
  
  // 間距配置(單位:twips)
  spacing: {
    line: {
      single: 240,     // 單倍
      oneHalf: 360,    // 1.5倍
      double: 480,     // 2倍
    },
    paragraph: {
      before: 200,
      after: 100,
    }
  }
};

// 樣式工廠函數(shù)
export function createStyle(id, name, overrides = {}) {
  const baseStyle = {
    id,
    name,
    basedOn: "Normal",
    next: "Normal",
    quickFormat: true,
    run: {
      font: STYLE_CONFIG.fonts.chinese,
      size: STYLE_CONFIG.sizes.normal,
      color: STYLE_CONFIG.colors.dark.replace("#", ""),
    },
    paragraph: {
      spacing: {
        line: STYLE_CONFIG.spacing.line.oneHalf,
        before: 0,
        after: STYLE_CONFIG.spacing.paragraph.after,
      }
    },
    ...overrides
  };
  
  return baseStyle;
}

// 預(yù)設(shè)樣式
export const PRESET_STYLES = {
  // 標(biāo)題樣式
  title: (options = {}) => createStyle("TitleStyle", "標(biāo)題", {
    basedOn: "Title",
    run: {
      font: STYLE_CONFIG.fonts.heading,
      size: STYLE_CONFIG.sizes.title,
      color: STYLE_CONFIG.colors.primary.replace("#", ""),
      bold: true,
    },
    paragraph: {
      alignment: "center",
      spacing: { before: 400, after: 300 },
    },
    ...options
  }),
  
  // 警告樣式
  warning: (options = {}) => createStyle("WarningStyle", "警告", {
    run: {
      color: STYLE_CONFIG.colors.danger.replace("#", ""),
      bold: true,
      highlight: "yellow",
    },
    paragraph: {
      shading: { fill: "FFF8DC" },
      indent: { left: 360, right: 360 },
    },
    ...options
  }),
  
  // 代碼樣式
  code: (options = {}) => createStyle("CodeStyle", "代碼", {
    run: {
      font: STYLE_CONFIG.fonts.code,
      size: STYLE_CONFIG.sizes.small,
      color: STYLE_CONFIG.colors.success.replace("#", ""),
    },
    paragraph: {
      shading: { fill: "F5F5F5" },
      indent: { left: 720 },
      spacing: { line: 280 },
    },
    ...options
  })
};

使用工具函數(shù):

import { Document } from "docx";
import { PRESET_STYLES, createStyle } from "./styles/config";

const doc = new Document({
  paragraphStyles: [
    // 使用預(yù)設(shè)樣式
    PRESET_STYLES.title(),
    // 使用工廠函數(shù)創(chuàng)建自定義樣式
    createStyle("CustomStyle", "自定義樣式", {
      run: {
        font: "楷體",
        size: 26,
        italics: true,
      },
      paragraph: {
        alignment: "right",
        shading: { fill: "F0F8FF" }
      }
    }),
    // 修改預(yù)設(shè)樣式
    PRESET_STYLES.warning({
      id: "CriticalWarning",
      name: "嚴(yán)重警告",
      run: {
        size: 28,
        underline: {},
      }
    })
  ],
  // 全局默認(rèn)樣式
  styles: {
    default: {
      document: {
        run: {
          font: "宋體",
          size: 24,
        },
        paragraph: {
          spacing: { line: 360 },
        }
      }
    }
  }
});

2.文檔生成策略(分塊處理)

分塊處理的目的:避免在瀏覽器中生成大型文檔時出現(xiàn)內(nèi)存溢出、界面卡死、用戶無響應(yīng)等問題。

場景需要程度說明
小文檔(< 100行)?完全不需要
中等文檔(100-500行)??可考慮,但不是必須
大型報表(500-2000行)???建議實現(xiàn)
大數(shù)據(jù)量(> 2000行)?????必須實現(xiàn)
// 漸進(jìn)式生成大型文檔
async function generateLargeDocument(data, chunkSize = 50) {
  const sections = [];
  
  // 分段處理避免內(nèi)存溢出
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    const section = await createSection(chunk);
    sections.push(section);
  }
  
  return new Document({ sections });
}

3. 瀏覽器環(huán)境優(yōu)化

// 添加進(jìn)度反饋的大型文檔生成
async function exportDocumentWithProgress(data, onProgress) {
  const totalSteps = data.length;
  let completed = 0;
  
  const sections = [];
  for (const item of data) {
    const section = await createSection(item);
    sections.push(section);
    
    completed++;
    if (onProgress) {
      onProgress(Math.round((completed / totalSteps) * 100));
    }
  }
  
  const doc = new Document({ sections });
  const blob = await Packer.toBlob(doc);
  saveAs(blob, "document.docx");
}

4. 錯誤處理與用戶體驗

// 完整的錯誤處理示例
async function safeExportDocument(data) {
  try {
    // 驗證數(shù)據(jù)
    if (!data || data.length === 0) {
      throw new Error("沒有數(shù)據(jù)可導(dǎo)出");
    }
    
    // 檢查數(shù)據(jù)大?。g覽器內(nèi)存限制)
    if (data.length > 1000) {
      console.warn("數(shù)據(jù)量較大,建議分批次導(dǎo)出");
      // 可以提示用戶或自動分批
    }
    
    // 生成文檔
    const doc = await createDocument(data);
    const blob = await Packer.toBlob(doc);
    
    // 檢查Blob大小
    if (blob.size > 10 * 1024 * 1024) { // 10MB
      console.warn("文檔較大,下載可能需要時間");
    }
    
    // 保存文件
    saveAs(blob, `導(dǎo)出文檔_${new Date().toISOString().slice(0, 10)}.docx`);
    
    return { success: true, size: blob.size };
    
  } catch (error) {
    console.error("文檔導(dǎo)出失敗:", error);
    
    // 用戶友好的錯誤提示
    const errorMessage = {
      "沒有數(shù)據(jù)可導(dǎo)出": "請先添加數(shù)據(jù)再導(dǎo)出",
      "NetworkError": "網(wǎng)絡(luò)連接失敗,請檢查網(wǎng)絡(luò)",
      "QuotaExceededError": "文檔太大,請減少數(shù)據(jù)量"
    }[error.name] || "文檔導(dǎo)出失敗,請重試";
    
    alert(errorMessage);
    return { success: false, error: error.message };
  }
}

常見問題及解決方案

問題1:樣式不生效

檢查清單

  1. 樣式ID是否正確引用
  2. 樣式優(yōu)先級是否正確(內(nèi)聯(lián)樣式 > 段落樣式 > 全局樣式)
  3. 樣式配置語法是否正確
  4. 是否拼寫錯誤

問題2:中英文混合字體

// 正確的字體設(shè)置
{
  id: "MixedFont",
  name: "混合字體",
  run: {
    // 分別設(shè)置不同字符集的字體
    font: {
      ascii: "Times New Roman",  // 英文字體
      eastAsia: "宋體",          // 中文字體
      cs: "宋體",                // 復(fù)雜字符
      hint: "eastAsia",          // 提示使用東亞字體
    }
  }
}

問題3:編號不正確

現(xiàn)象:有序列表編號混亂

解決方案

  1. 明確指定instance:不要依賴默認(rèn)行為
  2. 避免中斷實例:相同instance的段落放在一起
  3. 使用不同reference:完全獨立的列表使用不同reference

問題4:瀏覽器內(nèi)存不足

現(xiàn)象:大文檔生成失敗

優(yōu)化策略

  1. 分批次生成:將大數(shù)據(jù)分塊處理
  2. 壓縮圖片:減少圖片大小
  3. 簡化樣式:避免過度復(fù)雜的樣式嵌套
  4. 提供進(jìn)度反饋:讓用戶了解生成進(jìn)度

項目結(jié)構(gòu)建議

project/
├── src/
│   ├── components/
│   │   └── DocExporter.vue          # 導(dǎo)出組件
│   ├── utils/
│   │   ├── doc-generator/
│   │   │   ├── config/              # 樣式配置
│   │   │   │   ├── styles.js        # 樣式定義
│   │   │   │   └── templates.js     # 文檔模板
│   │   │   ├── builders/            # 組件構(gòu)建器
│   │   │   │   ├── table-builder.js
│   │   │   │   ├── list-builder.js
│   │   │   │   └── style-builder.js
│   │   │   └── index.js             # 主入口
│   │   └── file-utils.js            # 文件工具
│   └── views/
│       └── ReportView.vue           # 報表頁面
└── package.json

總結(jié):

  1. 分層配置
    • 使用 styles.default 設(shè)置全局基礎(chǔ)樣式
    • 使用 paragraphStyles 定義可重用段落樣式
    • 使用內(nèi)聯(lián)樣式處理特殊情況
  2. 樣式規(guī)劃
    • 先規(guī)劃樣式體系,再開始編碼
    • 創(chuàng)建樣式變量,方便維護(hù)
    • 使用樣式繼承減少重復(fù)
  3. 性能優(yōu)化
    • 避免過多內(nèi)聯(lián)樣式
    • 復(fù)用樣式定義
    • 使用樣式工廠函數(shù)
  4. 兼容性考慮
    • 指定完整字體鏈
    • 測試不同環(huán)境下的顯示效果
    • 提供樣式回退機(jī)制

現(xiàn)在你可以在Vue.js項目中輕松實現(xiàn)強(qiáng)大的Word文檔導(dǎo)出功能了!

以上就是Vue使用docx和file-saver實現(xiàn)Word文檔導(dǎo)出功能的詳細(xì)內(nèi)容,更多關(guān)于Vue docx和file-saver實現(xiàn)Word導(dǎo)出的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

习水县| 栾城县| 宁阳县| 潞城市| 天峻县| 巴楚县| 涿州市| 全椒县| 余姚市| 通州区| 朝阳市| 岳阳县| 秦安县| 宁晋县| 资中县| 会东县| 丘北县| 从江县| 龙里县| 宜州市| 乐昌市| 宁远县| 舞钢市| 卫辉市| 康乐县| 金昌市| 双峰县| 呼图壁县| 连州市| 安远县| 屏南县| 泽州县| 荆门市| 商洛市| 青川县| 都昌县| 高阳县| 苍南县| 台北县| 新绛县| 乐业县|