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

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

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

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

  • docx:純JavaScript的Word文檔生成庫(kù),功能全面,無(wú)依賴
  • file-saver:簡(jiǎn)化文件保存操作,兼容性好

安裝

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

# 或使用yarn
yarn add docx file-saver

# 或使用pnpm
pnpm add docx file-saver

版本兼容性

版本特點(diǎn)建議
docx 8.x傳統(tǒng)API,文檔豐富已有項(xiàng)目維護(hù)
docx 9.x新API,直接配置(本文基于此)新項(xiàng)目首選
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)建文檔對(duì)象
    const doc = new Document({
      sections: [
        {
          children: [
            new Paragraph({
              children: [new TextRun("歡迎使用文檔導(dǎo)出功能")],
            }),
            new Paragraph({
              children: [new TextRun("這是一個(gè)簡(jiǎn)單的示例文檔。")],
            }),
          ],
        },
      ],
    });

    // 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庫(kù)詳解

什么是docx?

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

主要特性

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

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

核心概念架構(gòu)

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

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

基本工作流程

1. 創(chuàng)建文檔對(duì)象 (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)建文檔對(duì)象
    const doc = new Document({
      // 2. 文檔內(nèi)容分段(可以包含多個(gè)分段)
      sections: [
        {
          properties: {}, // 分段屬性(頁(yè)面大小、邊距等)
          children: [
            // 3. 分段中的內(nèi)容元素
            new Paragraph({
              // 4. 段落
              children: [
                // 5. 段落中的文本塊
                new TextRun("Hello, World!"),
              ],
            }),
          ],
        },
      ],
    });
    // 6. 將文檔對(duì)象轉(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);  // 錯(cuò)誤提示
  }
}

Packer工具對(duì)比

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

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

基礎(chǔ)組件

1. Document(文檔)

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

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

2. Section(分段)

文檔由多個(gè)分段組成,每個(gè)分段可以有不同的頁(yè)面設(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: []  // 這個(gè)分段中的內(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 中的一個(gè)段落。

const paragraph = new Paragraph({
  children: [ /* 文本塊或其他內(nèi)聯(lián)元素 */ ],
  alignment: "left",  // 對(duì)齊方式: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: "一級(jí)標(biāo)題",
    heading: HeadingLevel.HEADING_1,
  }),
  new Paragraph({
    text: "二級(jí)標(biāo)題",
    heading: HeadingLevel.HEADING_2,
  }),
  new Paragraph({
    text: "三級(jí)標(biāo)題",
    heading: HeadingLevel.HEADING_3,
  })
];

2). 列表 (Lists)

列表類型概覽

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

->無(wú)序列表:

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

->有序列表:

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

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

instance 屬性:

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

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

示例:

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

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

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

重要提示

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

4.TextRun(文本塊)

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

const textRun = new TextRun({
  text: "這是文本內(nèi)容",
  bold: true,        // 加粗
  italics: false,    // 斜體
  underline: {},     // 下劃線(空對(duì)象表示單下劃線)
  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 庫(kù)中管理文檔樣式的兩個(gè)核心配置項(xiàng),它們有不同的用途和層級(jí)關(guān)系。

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

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

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

常用樣式屬性

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

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

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

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

const doc = new Document({
  styles: {
    default: {
      // 文檔默認(rèn)字符樣式
      document: {
        run: {
          font: "宋體",      // 字體
          size: 24,         // 字號(hào)
          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: [
      // 自動(dòng)應(yīng)用 heading1 樣式
      new Paragraph({
        text: "文檔標(biāo)題",
        heading: "Heading1"
      }),
      
      // 自動(dòng)應(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: [
      // 自動(dòng)應(yīng)用 heading1 樣式
      new Paragraph({
        text: "文檔標(biāo)題",
        heading: "Heading1"
      }),
      
      // 自動(dòng)應(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", // 基于哪個(gè)樣式(可選)
        next: "Normal", // 按Enter后的樣式(可選)
        quickFormat: true, // 是否在快速樣式庫(kù)顯示
        // 字符樣式
        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", // 對(duì)齊:left, center, right, both
          border: {
            // 邊框
            top: { style: "single", size: 4, color: "000000" },
          },
          shading: {
            // 底紋
            fill: "F5F5F5",
          },
          pageBreakBefore: false, // 是否段前分頁(yè)
          keepLines: true, // 保持在同一頁(yè)
          keepNext: true, // 與下段同頁(yè)
        },
      },
    ],
  },
});

示例:

import { Document, Paragraph } from "docx";

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

3. 字符樣式 (characterStyles)

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

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("請(qǐng)及時(shí)查看最新消息")
        ]
      })
    ]
  }]
});

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

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

import { Paragraph, TextRun } from "docx";

// 1. 在 Paragraph 中直接設(shè)置
const paragraph1 = new Paragraph({
  text: "這個(gè)段落有直接樣式",
  alignment: "center",           // 對(duì)齊方式
  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,                  // 字號(hào)
      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)點(diǎn):保持樣式一致性和便于批量修改。

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,          // 添加加粗
      }
      // 其他屬性自動(dòng)從 BaseStyle 繼承
    },
    // 多層繼承
    {
      id: "CriticalWarning",
      name: "嚴(yán)重警告",
      basedOn: "WarningStyle",  // 繼承 WarningStyle
      run: {
        highlight: "yellow",    // 添加高亮
        size: 28,               // 修改字號(hào)
      },
      paragraph: {
        shading: {              // 添加背景色
          fill: "FFEEEE"
        }
      }
    }
  ]
});

2. 樣式鏈 (next)

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

6. 混合使用所有方式

實(shí)際項(xiàng)目中,通常需要混合使用多種方式:

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: "公司報(bào)告樣式",
      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年度報(bào)告",
        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)先級(jí)示例:

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

示例:

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

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}月度工作報(bào)告`,
    description: `${data.year}年${data.month}月工作報(bào)告`,
    
    // 樣式配置
    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}月度工作報(bào)告`,
          heading: HeadingLevel.TITLE,
          alignment: AlignmentType.CENTER,
          spacing: { after: 400 }
        }),
        
        // 基本信息
        new Paragraph({
          children: [
            new TextRun({ text: "報(bào)告人:", 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("項(xiàng)目")],
                  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: "完成項(xiàng)目需求分析" },
    { description: "完成核心功能開發(fā)" },
    { description: "完成單元測(cè)試" }
  ],
  projects: [
    { name: "項(xiàng)目A", progress: 80, owner: "張三" },
    { name: "項(xiàng)目B", progress: 60, owner: "李四" },
    { name: "項(xiàng)目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",     // 危險(xiǎn)
    info: "#008080",       // 信息
    light: "#F5F5F5",      // 淺色
    dark: "#333333",       // 深色
  },
  
  // 字體配置
  fonts: {
    chinese: "宋體",
    heading: "黑體",
    code: "Consolas",
    english: "Arial",
  },
  
  // 字號(hào)配置(單位:磅的2倍)
  sizes: {
    title: 36,     // 18pt
    h1: 32,        // 16pt
    h2: 28,        // 14pt
    h3: 26,        // 13pt
    normal: 24,    // 12pt (小四)
    small: 21,     // 10.5pt (五號(hào))
    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.文檔生成策略(分塊處理)

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

場(chǎng)景需要程度說明
小文檔(< 100行)?完全不需要
中等文檔(100-500行)??可考慮,但不是必須
大型報(bào)表(500-2000行)???建議實(shí)現(xiàn)
大數(shù)據(jù)量(> 2000行)?????必須實(shí)現(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. 錯(cuò)誤處理與用戶體驗(yàn)

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

常見問題及解決方案

問題1:樣式不生效

檢查清單

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

問題2:中英文混合字體

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

問題3:編號(hào)不正確

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

解決方案

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

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

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

優(yōu)化策略

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

項(xiàng)目結(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           # 報(bào)表頁(yè)面
└── 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. 兼容性考慮
    • 指定完整字體鏈
    • 測(cè)試不同環(huán)境下的顯示效果
    • 提供樣式回退機(jī)制

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

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

相關(guān)文章

最新評(píng)論

海淀区| 孟州市| 瓮安县| 河东区| 巴塘县| 大足县| 花莲市| 平和县| 闽清县| 灌云县| 察隅县| 永宁县| 武汉市| 高密市| 奉贤区| 景洪市| 西平县| 深圳市| 建瓯市| 伊宁市| 金门县| 资源县| 台前县| 越西县| 公主岭市| 繁昌县| 遵化市| 利辛县| 海口市| 秦安县| 扶余县| 新巴尔虎左旗| 彩票| 凤台县| 漾濞| 长宁区| 行唐县| 古浪县| 昌乐县| 玛沁县| 合山市|