所用的插件 html-docx 和 file-saver
项目中所用到的 生成运动会秩序册 核心代码如下
<!--
* @Author: gaopengli
* @Date: 2021-09-09 15:01:26
* @LastEditTime: 2021-10-13 19:59:06
* @LastModifiedBy: gaopengli
* @LastEditors: gaopengli
* @Description: 描述
* @FilePath: \frontend-admin\src\pages\eventManage\dataNote\components\dialog.book.vue
-->
<template>
<div>
<div id="oredrBookContent" class="order-book">
<div>
<div id="html-docx-config">
<div id="first-page" titlePg="true">
<h1 style="text-align: center; margin-bottom: 80px">
{{ bookInfo.matchName }}{{ bookInfo.item }}
</h1>
<div
style="font-size: 80px; margin-bottom: 90px; text-align: center"
>
秩
</div>
<div
style="font-size: 80px; margin-bottom: 90px; text-align: center"
>
序
</div>
<div
style="font-size: 80px; margin-bottom: 90px; text-align: center"
>
册
</div>
<div>
<div
style="text-align: center; font-size: 25px; font-weight: bold"
>
{{ bookInfo.startTime }}至{{ bookInfo.endTime }}
</div>
<div
style="
text-align: center;
font-size: 25px;
margin-top: 30px;
font-weight: bold;
"
>
{{ bookInfo.holdPlace }}
</div>
</div>
</div>
<div class="change-line"></div>
</div>
<div id="rule-page">
<h2 style="text-align: center">
{{ bookInfo.matchName }}{{ bookInfo.item }}项目竞赛规程
</h2>
<div v-html="bookInfo.ruleInfo"></div>
<div class="change-line"></div>
</div>
<div id="notice-page">
<h2 style="text-align: center">
{{ bookInfo.matchName }}{{ bookInfo.item }}项目补充通知
</h2>
<div v-html="bookInfo.notice"></div>
<div class="change-line"></div>
</div>
<div id="unit-page">
<h2 style="text-align: center">各代表队名单</h2>
<!-- <div>
<span>运动员iiii:</span>
<span
v-for="(item, index) in 7"
:key="`item_${index}`"
style="padding-left: 15px"
>
<span>运动员 </span>
<span>运动员,运动员 </span>
<br v-if="index % 3 === 2" />
</span>
</div> -->
<div
style="font-size: 22px; line-height: 30px; margin-top: 20px"
v-for="(unitItem, index) in bookInfo.unitList"
:key="`unit_${index}`"
>
<h3 style="text-align: center">{{ unitItem.unit }}</h3>
<div
v-for="(staff, index) in unitItem.supportStaffMap"
:key="`staff_${index}`"
>
<span>{{ index }}:</span
><span
v-for="(item, index) in staff"
:key="`item_${index}`"
style="padding-left: 5px"
>{{ item }}</span
>
</div>
<div style="font-size: 22px; line-height: 30px">运动员:</div>
<div
v-for="(athlete, index) in unitItem.athleteMap"
:key="`athlete_${index}`"
>
<div>
<span>{{ index }}:</span>
</div>
<div>
<span
v-for="(item, index) in athlete"
:key="`item_${index}`"
style="padding-left: 15px"
>
<span>{{ item.peopleName }} </span>
<span>{{ item.birthYear }},{{ item.age }} </span>
<br v-if="index % 3 === 2" />
</span>
</div>
</div>
<!--<div style="clear: both"></div> -->
<!-- </div> -->
</div>
<div class="change-line"></div>
</div>
<div id="end-page">
<h2 style="text-align: center">大会活动日程表</h2>
<div v-html="bookInfo.matchSchedule"></div>
</div>
</div>
</div>
<el-button type="primary" @click="confirm">确定</el-button>
</div>
</template>
<script>
import apis from '@/apis'
import $ from 'jquery'
import HtmlDocx from 'html-docx-js/dist/html-docx'
import saveAs from 'file-saver'
export default {
data() {
return {
matchName: '厦门马拉松',
projectName: '摔跤',
leader: '王 成',
coach: '何 利 林泽禹 宋虎成 陈 杨',
worker: '林 畅',
matchGroupId: '',
styleList: [],
bookInfo: {}
}
},
methods: {
/**
* @description: 获取秩序册内容
* @param {*}
* @return {*}
*/
getBookContent(data) {
this.matchGroupId = data.matchGroupId
this.$http
.post(
`${apis._api_MatchDocumentController_getOrderVolumesContent}?matchGroupId=${this.matchGroupId}`
)
.then(res => {
if (res.data.code === 200) {
let data = res.data.data
console.log(data)
this.bookInfo = data
this.$nextTick(() => {
this.confirm()
})
}
})
},
confirm() {
// 这段代码用来处理style标签的样式
this.styleList = []
let element = document.getElementsByClassName('style')
if (element.length) {
for (var i = 0; i < element.length; i++) {
this.styleList.push(element[i].innerText.replace(/\s{2,}/g, ''))
}
}
this.HtmlToDocx({
exportElement: '#oredrBookContent', // 需要转换为word的html标签
exportFileName: `${this.bookInfo.matchName}${this.bookInfo.item}项目秩序册.docx`, // 转换之后word文档的文件名称
StringStyle: this.styleList.join(''), // css样式以字符窜的形式插入进去
margins: {
top: 1440,
right: 1440,
bottom: 1440,
left: 1440,
header: 720,
footer: 720,
gutter: 0
} // word的边距配置
})
},
/**
* @description: html转成word
* @param {*} params
* @return {*}
*/
HtmlToDocx(params) {
var exportElement = params.exportElement
var exportFileName = params.exportFileName
var StringStyle = params.StringStyle
? params.StringStyle
: '#main, #main1 {height: 400px;width: 630px } .list {background: blue;padding: 10px } .list .page {background: yellow;padding: 5px;height: 20px; } .img {padding: 50px; } table {align-text: center;width: 100%;vertical-align: middle;border-top: 1px solid #ddd;border-left: 1px solid #ddd; } table td, table th {width: 20%;height: 40px;border-right: 1px solid #ddd;border-bottom: 1px solid #ddd; } table p {text-indent: 0 !important;text-align: center; }'
var fistPageContent = $('#html-docx-config').html()
var config = this.getDocumentConfig()
console.log(fistPageContent, config)
var Id = '#temporaryExportElement'
$("<div id='temporaryExportElement'></div>")
.html($(exportElement).html())
.appendTo(document.body)
this.convertImagesToBase64()
var content =
"<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head></head><style>" +
(StringStyle ? StringStyle.replace(/(\s{2,}|\n)/g, '') : '') +
'</style><body>' +
$(Id)
.html()
.replace(/\s{2,}/g, '') +
'</body></html>'
$('#temporaryExportElement').remove()
let blobData = HtmlDocx.asBlob(content)
// let timestamp = Date.parse(new Date())
// 将blob转为file
let files = new window.File(
[blobData],
`${this.bookInfo.matchName}${this.bookInfo.item}项目秩序册.docx`,
{
type: blobData.type
}
)
console.log(files)
this.uploadFiles(files)
saveAs(HtmlDocx.asBlob(content), exportFileName)
},
/**
* @description: 上传生成的文件
* @param {*} files
* @return {*}
*/
uploadFiles(files) {
this.$http
.post(apis._api_file_uploadNoRename, {
files: files,
model: 'SSZH',
IsFormData: true
})
.then(res => {
let data = res.data.data
if (res.data.code === 200) {
console.log(res)
this.creatBook(data[0].path)
}
})
},
/**
* @description: 生成秩序册
* @param {*} filePath 文档地址
* @return {*}
*/
creatBook(filePath) {
this.$http
.post(apis._api_MatchDocumentController_createOrderVolumes, {
matchGroupId: this.matchGroupId,
path: filePath
})
.then(res => {
if (res.data.code === 200) {
$('.page-break').remove() //防止重复换页
this.$emit('onComplete')
console.log(res)
}
})
},
/**
* @description: 将图片转为base64格式
* @param {*}
* @return {*}
*/
convertImagesToBase64() {
var regularImages = document.querySelectorAll('img')
var canvas = document.createElement('canvas')
var ctx = canvas.getContext('2d')
;[].forEach.call(regularImages, function (img) {
var imgElement = new Image()
//设置图片跨域访问
img.setAttribute('crossOrigin', 'anonymous')
ctx.clearRect(0, 0, canvas.width, canvas.height)
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
var dataURL = canvas.toDataURL()
imgElement.setAttribute('src', dataURL)
})
canvas.remove()
},
// 获取文档中word文档配置信息
getDocumentConfig() {
// 处理页眉
var header = $('#page-header')
$('#page-header').attr({ data: header.html() }).html('')
// 处理首页
var firstPageContent = ''
if ($('#first-page').length && $('#page-content').length) {
firstPageContent =
"<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head></head><body>" +
$($('#first-page'))
.html()
.replace(/\s{2,}/g, '') +
'</body></html>'
$('#first-page').html('')
}
// 处理目录
var content = $('#page-content')
// 处理换行
$(
'<br clear=all style="page-break-before:always" mce_style="page-break-before:always" class="page-break">'
).insertAfter($('.change-line'))
var footer = $('#page-footer')
return {
header: {
text: header.attr('data') || '',
align: header.attr('textAlign') || 'right'
},
footer: footer.length ? true : false,
content: content.length,
firstPage: firstPageContent,
titlePg:
$('#first-page').length && $('#first-page').attr('titlePg') === 'true'
? true
: false
}
}
}
}
</script>