Vue2转Word方法(html-docx-js库)
前言
本文主要讲解使用Js中Dom实现将Vue中部分代码转换成html。文末有示例完整Vue2源代码.
应用场景:
Vue2文件导出成Word(先转成Html、再转成Word)
步骤一:获取当前内容
bash
const container = this.$el.querySelector('.stats-container').cloneNode(true)
先是用querySelector 截取当前Dom 元素的Class 类来获取内容,再用cloneNode来实现克隆当前元素内容。
步骤二:构建完整Html
获取到的内容就可以作为html 中body 体。将固定的html 模板、样式、body 体内容写入到变量中。
注:可以使用new Blob([htmlContent,{type:"text/html;charset=utf-8}])实现对转换后的html文件导出。
这两部分的Vue2 源代码代码示例如下(运行代码需要下载fileSaver库,如不想下载可修改成模拟a标签click事件实现导出):
bash
<template>
<div class="vue-html-example">
<el-card>
<div slot="header" class="clearfix">
<span>数据导出示例</span>
<el-button style="float: right; padding: 3px 0" type="text" @click="handleExportHtml">
导出HTML
</el-button>
</div>
<div class="export-container">
<h2 class="doc-title">{{ reportTitle }}</h2>
<p class="doc-desc">
本报告生成于 {{ currentDate }},包含最新的项目人员状态及分工详情。
所有数据均为实时获取,确保信息的准确性与时效性。
</p>
<table class="doc-table" border="1">
<thead>
<tr>
<th>姓名</th>
<th>职位</th>
<th>状态</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in tableData" :key="index">
<td>{{ item.name }}</td>
<td>{{ item.role }}</td>
<td>
<span :style="{ color: item.status === '在职' ? 'green' : 'red' }">
{{ item.status }}
</span>
</td>
<td>{{ item.remark || '-' }}</td>
</tr>
</tbody>
</table>
<div class="doc-footer">
<p>生成日期:{{ currentDate }}</p>
<p>制表人:系统管理员</p>
</div>
</div>
</el-card>
</div>
</template>
<script>
import { saveAs } from 'file-saver'
export default {
name: 'Vue2HtmlExportExample',
data() {
return {
reportTitle: '2026年度人员统计报告',
currentDate: new Date().toLocaleDateString(),
tableData: [
{ name: '张三', role: '项目经理', status: '在职', remark: '负责整体进度' },
{ name: '李四', role: '前端开发', status: '在职', remark: '负责页面实现' },
]
}
},
methods: {
handleExportHtml() {
try {
const originalElement = this.$el.querySelector('.export-container')
if (!originalElement) {
this.$message.warning('未找到导出内容区域')
return
}
const clonedNode = originalElement.cloneNode(true)
const htmlContent = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>${this.reportTitle}</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; margin: 20px; color: #333; }
.doc-title { font-size: 24px; font-weight: bold; text-align: center; margin: 20px 0; border-bottom: 2px solid #409eff; padding-bottom: 10px; }
.doc-desc { line-height: 1.6; margin-bottom: 20px; text-indent: 2em; }
.doc-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.doc-table th { background-color: #f5f7fa; font-weight: bold; }
.doc-table th, .doc-table td { border: 1px solid #000; padding: 12px 8px; text-align: center; }
.doc-footer { margin-top: 30px; text-align: right; font-size: 14px; color: #666; border-top: 1px dashed #ccc; padding-top: 10px; }
</style>
</head>
<body>
<div style="max-width: 800px; margin: 0 auto; background: #fff; padding: 20px; box-shadow: 0 0 10px rgba(0,0,0,0.1);">
${clonedNode.innerHTML}
</div>
</body>
</html>
`
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' })
saveAs(blob, `${this.reportTitle}.html`)
this.$message.success('导出 HTML 成功!')
} catch (error) {
console.error('导出失败:', error)
this.$message.error('导出失败,请查看控制台')
}
}
}
}
</script>
<style scoped>
.vue-html-example {
padding: 20px;
}
.export-container {
border: 1px dashed #dcdfe6;
padding: 20px;
margin-bottom: 20px;
background: #fff;
}
.doc-title { text-align: center; color: #333; margin: 20px 0; }
.doc-desc { text-indent: 2em; line-height: 1.8; }
.doc-table { width: 100%; border-collapse: collapse; margin-top: 20px; }
.doc-table th, .doc-table td { border: 1px solid #ccc; padding: 10px; text-align: center; }
.doc-table th { background-color: #f5f7fa; }
.doc-footer { margin-top: 30px; text-align: right; }
</style>
步骤三:使用html-docx-js-typescript(简写hdj)库实现html-word转换
cpp
import { asBlob } from 'html-docx-js-typescript'
const blob = await asBlob(htmlContent, { orientation: 'portrait' })
这个步骤比较简单,因为本身hdj库它是根据html+css样式来动态控制word文件内容的,而不需要对样式布局手动的转换。所以只需要导入hdj库并且把做好的html文件放到asBlob方法中即可。那么弊端有很明显,就是它内部有很多问题是不可定制化的,就比方说对css样式做转义并没有做的那么完善。而且更新不及时,不过应用于简单的页面转word对样式没有高保真、精细化处理那还是比较适合的。
如果想看asBlob方法内容可参考这:
ps:如果有高要求,最好还是使用docx.js来实现转换导出word。因为docx这个库本身是不可直接将html转换成word的,需要手动定制化将html转换成word可接受的结构。费时但是好用,这里就不赘述了。Vue2-Html-Word导出完整示例代码如下:
cpp
<template>
<div class="vue-html-example">
<el-card>
<div slot="header" class="clearfix">
<span>数据导出示例</span>
<div style="float: right;">
<el-button type="text" @click="handleExportHtml">导出HTML</el-button>
<el-button type="text" @click="handleExportWord">导出Word</el-button>
</div>
</div>
<div class="export-container">
<h2 class="doc-title">{{ reportTitle }}</h2>
<p class="doc-desc">
本报告生成于 {{ currentDate }},包含最新的项目人员状态及分工详情。
所有数据均为实时获取,确保信息的准确性与时效性。
</p>
<table class="doc-table" border="1">
<thead>
<tr>
<th>姓名</th>
<th>职位</th>
<th>状态</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in tableData" :key="index">
<td>{{ item.name }}</td>
<td>{{ item.role }}</td>
<td>
<span :style="{ color: item.status === '在职' ? 'green' : 'red' }">
{{ item.status }}
</span>
</td>
<td>{{ item.remark || '-' }}</td>
</tr>
</tbody>
</table>
<div class="doc-footer">
<p>生成日期:{{ currentDate }}</p>
<p>制表人:系统管理员</p>
</div>
</div>
</el-card>
</div>
</template>
<script>
import { saveAs } from 'file-saver'
import { asBlob } from 'html-docx-js-typescript'
export default {
name: 'Vue2HtmlExportExample',
data() {
return {
reportTitle: '2026年度人员统计报告',
currentDate: new Date().toLocaleDateString(),
tableData: [
{ name: '张三', role: '项目经理', status: '在职', remark: '负责整体进度' },
{ name: '李四', role: '前端开发', status: '在职', remark: '负责页面实现' },
]
}
},
methods: {
getHtmlContent() {
const originalElement = this.$el.querySelector('.export-container')
if (!originalElement) {
this.$message.warning('未找到导出内容区域')
return null
}
const clonedNode = originalElement.cloneNode(true)
return `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>${this.reportTitle}</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; margin: 20px; color: #333; }
.doc-title { font-size: 24px; font-weight: bold; text-align: center; margin: 20px 0; border-bottom: 2px solid #409eff; padding-bottom: 10px; }
.doc-desc { line-height: 1.6; margin-bottom: 20px; text-indent: 2em; }
.doc-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.doc-table th { background-color: #f5f7fa; font-weight: bold; }
.doc-table th, .doc-table td { border: 1px solid #000; padding: 12px 8px; text-align: center; }
.doc-footer { margin-top: 30px; text-align: right; font-size: 14px; color: #666; border-top: 1px dashed #ccc; padding-top: 10px; }
</style>
</head>
<body>
<div style="max-width: 800px; margin: 0 auto; background: #fff; padding: 20px; box-shadow: 0 0 10px rgba(0,0,0,0.1);">
${clonedNode.innerHTML}
</div>
</body>
</html>
`
},
handleExportHtml() {
try {
const htmlContent = this.getHtmlContent()
if (!htmlContent) return
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' })
saveAs(blob, `${this.reportTitle}.html`)
this.$message.success('导出 HTML 成功!')
} catch (error) {
console.error('导出HTML失败:', error)
this.$message.error('导出HTML失败')
}
},
handleExportWord() {
try {
const htmlContent = this.getHtmlContent()
if (!htmlContent) return
Promise.resolve(asBlob(htmlContent)).then(blob => {
saveAs(blob, `${this.reportTitle}.docx`)
this.$message.success('导出 Word 成功!')
})
} catch (error) {
console.error('导出Word失败:', error)
this.$message.error('导出Word失败')
}
}
}
}
</script>
<style scoped>
.vue-html-example {
padding: 20px;
}
.export-container {
border: 1px dashed #dcdfe6;
padding: 20px;
margin-bottom: 20px;
background: #fff;
}
.doc-title { text-align: center; color: #333; margin: 20px 0; }
.doc-desc { text-indent: 2em; line-height: 1.8; }
.doc-table { width: 100%; border-collapse: collapse; margin-top: 20px; }
.doc-table th, .doc-table td { border: 1px solid #ccc; padding: 10px; text-align: center; }
.doc-table th { background-color: #f5f7fa; }
.doc-footer { margin-top: 30px; text-align: right; }
</style>