因为环境是浏览器,所以没办法用fs读取文件,使用的是FileReader,这个API的详细数据可以查看MDN文档,这里使用onload钩子,在事件对象中获取我们所需要的文件
// template
<el-upload
action="#"
list-type="picture-card"
:limit="1"
:auto-upload="false"
accept=".docx"
:before-upload="beforeUpload"
:on-change="handleChange"
:http-request="customUpload"
v-model:file-list="fileList"
:class="{ hideUpload: hideUpload }"
>
</el-upload>
// script
const handleChange = (file, uploadFiles) => {
const reader = new FileReader()
reader.readAsArrayBuffer(file.raw)
reader.onload = function (event) {
console.log(event)
const fileData = event.target.result
}
fileList.value.push(file)
}
这里我们拿到了文件的二进制数据,接下来需要通过两个包来解析word中的数据
pizzipdocxtemplater
注意,template中,accept的值为.doc,可能会报错
Uncaught Error: InternalError: The filetype for this file could not be identified, is this file corrupted ?
const handleChange = (file, uploadFiles) => {
console.log("[mLog] file -->", file)
const reader = new FileReader()
reader.readAsArrayBuffer(file.raw)
reader.onload = function (event) {
const fileData = event.target.result
// 在这里可以对文件数据进行进一步处理
let zip = new Pizzip(fileData)
const doc = new Docxtemplater(zip)
console.log("[mLog] doc -->", doc)
}
fileList.value.push(file)
}
这时我们可以看控制台
搜索似乎没法查找未展开的对象中的字段,只能说一顿好找
可以看到这些对象,type有content/tag,这好像是判断内容是双标签,type就是tag,单标签也是content,但是单标签的posiztion属性是outsidetag,text元素是insidetag,双标签,标签头是start,标签尾是end
我们需要查找对应表格中的数据,因为数据也是键值对模式,所以只需要去掉所有的标签,即过滤出所有type属性为content,position属性为insidetag的元素,这样剩下的就是我们所需求数据的键值对了,在根据index查找对应位置的值即可
补充:
在根据index查找数据位置时,可以发现有些标点/字符也换单独占一个对象,并且需要合并单元格,导致index对应的位置不固定,具体原理没研究清楚,但想了个办法,如果是表格,就获取<w:tc>和</w:tc>,根据index和表格的单元格查询
贴上完整代码:
// 组件内
const handleChange = (file, uploadFiles) => {
try {
// console.log("[mLog] file -->", file)
const reader = new FileReader()
reader.onload = function (event) {
const fileData = event.target.result
// 在这里可以对文件数据进行进一步处理
let zip = new Pizzip(fileData)
const doc = new Docxtemplater(zip)
// console.log("[mLog] doc -->", doc)
const arr = doc.compiled["word/document.xml"].lexed
const newArr = getWordInfo(arr)
// 将最终结果存入本地,方便路由跳转到添加表单页拿数据
sessionStorage.setItem("wordInfo", JSON.stringify(newArr))
}
reader.readAsArrayBuffer(file.raw)
fileList.value.push(file)
} catch (e) {
ElMessage.error("文件识别出错")
}
}
// js文件
export const getWordInfo = (word) => {
// word = word.replace(/<(\w+?)[^>]*?>/g, '').replace(/<\/.*?>/g, '')
const obj = {}
word.forEach((item) => {
switch (item.value) {
case '信访件编号': obj.number = getValueByTC(word, item.lIndex)
break
case '信访人': obj.personName = getValueByTC(word, item.lIndex)
break
case '人数': obj.peopleCount = getValueByTC(word, item.lIndex)
break
case '证件号码': obj.personId = getValueByTC(word, item.lIndex)
break
case '电话号码': obj.personPhone = getValueByTC(word, item.lIndex)
break
case '登记单位': obj.registerDepartName = getValueByTC(word, item.lIndex)
break
case '初重信': obj.isRepeat = getValueByTC(word, item.lIndex)
break
case '信访人地址': obj.personAddress = getValueByTC(word, item.lIndex)
break
case '问题发生地': obj.address = getValueByTC(word, item.lIndex)
break
case '信访日期': obj.visitTime = getValueByTC(word, item.lIndex)
break
case '标题': obj.title = getValueByTC(word, item.lIndex)
break
case '信访内容':
obj.descriptionCategoryText = getValueByTC(word, item.lIndex, 2)
obj.description = getValueByTC(word, item.lIndex, 4)
break
case '经办人': obj.managePersonName = getValueByTC(word, item.lIndex)
break
case '经办日期': obj.manageTime = getValueByTC(word, item.lIndex)
break
default:
break;
}
});
return obj
}
// arr值全部数组
// start指当前键所在数组中的index
// num指值的单元格在当前键所在的单元格后,几格位置
const getValueByTC = (arr, start, num = 1) => {
let str = ''
for (let i = start + 1; i < arr.length; i++) {
let item = arr[i]
let value = item.value
if (value === '<w:tc>') {
num--
}
if (num <= 0) {
if (value === '</w:tc>') {
break;
}
if (item.position === "insidetag") {
str += value
}
}
}
console.log('[mLog] str -->', str)
return str;
}
如果是纯文字,暂时没有需求,也没尝试,以后有在补充