实现以下两点功能:
1.导出单条数据至word中,图片还需支持多张。
2.导出多条数据至zip压缩包中,并且有图片也需要导出至word中,支持多张。
废话不多说开整!
首先在mixins文件中创建一个 exportWordImage.js 代码如下:
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'
import JSZip from 'jszip'
export default {
data () {
return {
zipName: '导出word.zip',
zip: null,
download: [],
length: 0
}
},
watch: {
download (val) {
if (val.length === this.length) {
var word = this.zip.generate({ type: 'blob' })
saveAs(word, this.zipName)
this.zip = null
this.download = []
}
}
},
methods: {
/**
* 将base64格式的数据转为ArrayBuffer
* @param {Object} dataURL base64格式的数据
*/
base64DataURLToArrayBuffer (dataURL) {
const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/
// const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/
if (!base64Regex.test(dataURL)) {
return false
}
const stringBase64 = dataURL.replace(base64Regex, '')
let binaryString
if (typeof window !== 'undefined') {
binaryString = window.atob(stringBase64)
} else {
binaryString = Buffer.from(stringBase64, 'base64').toString('binary')
}
const len = binaryString.length
const bytes = new Uint8Array(len)
for (let i = 0; i < len; i++) {
const ascii = binaryString.charCodeAt(i)
bytes[i] = ascii
}
return bytes.buffer
},
create (res = '') {
if (res) {
this.zip = new JSZip(res)
} else {
this.zip = new JSZip()
}
},
// 导出多条数据执行这个方法
ExportWordLists (url, list) {
this.length = list.length
list.forEach((item) => {
var obj = item
this.ExportBriefDataDocxs(url, obj, item.imgData)
})
},
ExportBriefDataDocxs (tempDocxPath, data, fileName, imgSize) {
console.log('ExportBriefDataDocxs', tempDocxPath, data, fileName, imgSize)
var _this = this
// 这里要引入处理图片的插件
var ImageModule = require('docxtemplater-image-module-free')
var expressions = require('angular-expressions')
var assign = require('lodash/assign')
var last = require('lodash/last')
expressions.filters.lower = function (input) {
// This condition should be used to make sure that if your input is
// undefined, your output will be undefined as well and will not
// throw an error
if (!input) return input
// toLowerCase() 方法用于把字符串转换为小写。
return input.toLowerCase()
}
function angularParser (tag) {
tag = tag
.replace(/^\.$/, 'this')
.replace(/(’|‘)/g, '')
.replace(/(“|”)/g, '')
const expr = expressions.compile(tag)
return {
get: function (scope, context) {
let obj = {}
const index = last(context.scopePathItem)
const scopeList = context.scopeList
const num = context.num
for (let i = 0, len = num + 1; i < len; i++) {
obj = assign(obj, scopeList[i])
}
// word模板中使用 $index+1 创建递增序号
obj = assign(obj, { $index: index })
return expr(scope, obj)
}
}
}
JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
if (error) {
console.log(error)
}
expressions.filters.size = function (input, width, height) {
return {
data: input,
size: [width, height]
}
}
let opts = {}
opts = {
// 图像是否居中
centered: true
}
opts.fileType = 'docx'
opts.getImage = (chartId) => {
// 将base64的数据转为ArrayBuffer
return _this.base64DataURLToArrayBuffer(chartId)
}
opts.getSize = function (img, tagValue, tagName) {
// 自定义指定图像大小
if (tagName === 'signature') {
return [80, 40]
} else {
return [350, 250]
}
}
// 创建一个JSZip实例,内容为模板的内容
const zip = new JSZip(content)
// 创建并加载 Docxtemplater 实例对象
// 设置模板变量的值
let doc = new Docxtemplater() // eslint-disable-line
doc.attachModule(new ImageModule(opts))
doc.loadZip(zip)
doc.setOptions({ parser: angularParser })
doc.setData({ ...data })
try {
// 呈现文档,会将内部所有变量替换成值,
doc.render()
} catch (error) {
const e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties
}
console.log('err', { error: e })
// 当使用json记录时,此处抛出错误信息
throw error
}
// 生成一个代表Docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
const out = doc.getZip().generate({ type: 'ArrayBuffer' })
this.zip.file(data.docName || data.title + '.docx', out, { binary: true })
this.download.push(data.proposalId)
})
},
// 导出单条数据执行这个方法
ExportBriefDataDocx (tempDocxPath, data, fileName, imgSize) {
console.log('ExportBriefDataDocx', tempDocxPath, data, fileName, imgSize)
var _this = this
// 这里要引入处理图片的插件
var ImageModule = require('docxtemplater-image-module-free')
var expressions = require('angular-expressions')
var assign = require('lodash/assign')
var last = require('lodash/last')
expressions.filters.lower = function (input) {
// This condition should be used to make sure that if your input is
// undefined, your output will be undefined as well and will not
// throw an error
if (!input) return input
// toLowerCase() 方法用于把字符串转换为小写。
return input.toLowerCase()
}
function angularParser (tag) {
tag = tag
.replace(/^\.$/, 'this')
.replace(/(’|‘)/g, '')
.replace(/(“|”)/g, '')
const expr = expressions.compile(tag)
return {
get: function (scope, context) {
let obj = {}
const index = last(context.scopePathItem)
const scopeList = context.scopeList
const num = context.num
for (let i = 0, len = num + 1; i < len; i++) {
obj = assign(obj, scopeList[i])
}
// word模板中使用 $index+1 创建递增序号
obj = assign(obj, { $index: index })
return expr(scope, obj)
}
}
}
JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
if (error) {
console.log(error)
}
expressions.filters.size = function (input, width, height) {
return {
data: input,
size: [width, height]
}
}
let opts = {}
opts = {
// 图像是否居中
centered: true
}
opts.getImage = (chartId) => {
// 将base64的数据转为ArrayBuffer
return _this.base64DataURLToArrayBuffer(chartId)
}
opts.getSize = function (img, tagValue, tagName) {
// 自定义指定图像大小
if (tagName === 'signature') {
return [80, 40]
} else {
return [350, 250]
}
}
// 创建一个JSZip实例,内容为模板的内容
const zip = new PizZip(content)
// 创建并加载 Docxtemplater 实例对象
// 设置模板变量的值
let doc = new Docxtemplater() // eslint-disable-line
doc.attachModule(new ImageModule(opts))
doc.loadZip(zip)
doc.setOptions({ parser: angularParser })
doc.setData(data)
try {
// 呈现文档,会将内部所有变量替换成值,
doc.render()
} catch (error) {
const e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties
}
console.log('err', { error: e })
// 当使用json记录时,此处抛出错误信息
throw error
}
// 生成一个代表Docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
const out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
})
// 将目标文件对象保存为目标类型的文件,并命名
saveAs(out, data.title || fileName)
})
},
/**
* 将图片的url路径转为base64路径
* 可以用await等待Promise的异步返回
* @param {Object} imgUrl 图片路径
*/
getBase64Sync (imgUrl) {
return new Promise(function (resolve, reject) {
// 一定要设置为let,不然图片不显示
let image = new Image() // eslint-disable-line
// 图片地址
image.src = imgUrl
// 解决跨域问题
image.setAttribute('crossOrigin', '*') // 支持跨域图片
// image.onload为异步加载
image.onload = function () {
let canvas = document.createElement('canvas') // eslint-disable-line
canvas.width = image.width
canvas.height = image.height
let context = canvas.getContext('2d') // eslint-disable-line
context.drawImage(image, 0, 0, image.width, image.height)
// 图片后缀名
let ext = image.src.substring(image.src.lastIndexOf('.') + 1).toLowerCase() // eslint-disable-line
// 图片质量
let quality = 0.8 // eslint-disable-line
// 转成base64
let dataurl = canvas.toDataURL('image/' + ext, quality) // eslint-disable-line
// 返回
resolve(dataurl)
}
})
}
}
}
在vue文件中:
<el-button type="primary"
@click="exportClick">导出</el-button>
import exportWordImage from '@/mixins/exportWordImage'
exportClick() {
if (this.choose.length) {
this.$confirm('此操作将当前选中的数据导出word, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.create()
this.wordExport(this.choose.join(','))
}).catch(() => {
this.$message({
type: 'info',
message: '已取消导出'
})
})
} else {
this.$message({
message: '请至少选择一条数据',
type: 'warning'
})
}
},
async wordExport (id) {
const res = await this.$api.representativeMessage.representativemessageExportWord({ ids: id })
var { data } = res
console.log('data===>', data)
var idArray = id.split(',')
var numberOfIds = idArray.length
if (numberOfIds === 1) {
var wordArr = []
var item = {}
for (let i = 0; i < data.length; i++) { // eslint-disable-line
console.log('data===>', data[i])
item.title = data[i].title
item.imgData = data[i].imageVo
for (let i in item.imgData) { // eslint-disable-line
item.imgData[i].imgUrl = await this.getBase64Sync(item.imgData[i].fullUrl)
}
item.userName = data[i].userName ? data[i].userName : ''
item.userIdentity = data[i].userIdentity ? data[i].userIdentity : ''
item.mobile = data[i].mobile ? data[i].mobile : ''
item.address = data[i].address ? data[i].address : ''
item.messageMessage = data[i].messageMessage ? data[i].messageMessage : ''
item.schedule = data[i].schedule ? data[i].schedule : ''
item.resultFeedback = data[i].resultFeedback ? data[i].resultFeedback : ''
item.representativeResult = data[i].representativeResult ? data[i].representativeResult : ''
item.feedbackImage = data[i].feedbackImage
for (let i in item.feedbackImage) { // eslint-disable-line
item.feedbackImage[i].imgUrl = await this.getBase64Sync(item.feedbackImage[i].fullUrl)
}
}
wordArr.push({ ...item })
var contentObj = wordArr[0]
this.ExportBriefDataDocx('word/representativeMessage.docx', contentObj, '留言导出')
} else {
var wordArrs = []
data.map(eItem => {
var _item = {}
_item.title = eItem.title
_item.userName = eItem.userName
_item.userIdentity = eItem.userIdentity
_item.mobile = eItem.mobile
_item.address = eItem.address
_item.messageMessage = eItem.messageMessage
_item.schedule = eItem.schedule
_item.resultFeedback = eItem.resultFeedback
_item.representativeResult = eItem.representativeResult
_item.imgData = eItem.imageVo
_item.feedbackImage = eItem.feedbackImage
setTimeout(async () => {
for (let i in _item.imgData) { // eslint-disable-line
_item.imgData[i].imgUrl = await this.getBase64Sync(_item.imgData[i].fullUrl)
}
setTimeout(async () => {
for (let i in _item.feedbackImage) { // eslint-disable-line
_item.feedbackImage[i].imgUrl = await this.getBase64Sync(_item.feedbackImage[i].fullUrl)
}
}, 200)
wordArrs.push(_item)
}, 400)
})
setTimeout(() => {
console.log('wordArrs===>', wordArrs)
this.ExportWordLists('word/representativeMessage.docx', wordArrs)
}, 2000)
}
this.choose = []
this.selectObj = []
this.list()
}