- 通过xlsx依赖实现
- npm install --save xlsx
<template>
<div class="page">
<van-nav-bar title="附件预览EXCEL" left-arrow @click-left="onClickLeft" />
<div class="excel">
<div class="flex jc pdt-30 pdt-30" v-if="loading">
<van-loading type="spinner" />
</div>
<div class="sheet">
<span
v-for="(item, index) in tableList"
:key="index"
v-html="item.name"
:class="item.isSelected ? 'active' : ''"
@click="onClickSwitchTab(index)"
></span>
</div>
<div
class="table"
v-for="(item, index) in tableList"
:key="index"
v-html="item.content"
v-show="item.isSelected"
></div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import FileRequest from './request.js'
import * as XLSX from 'xlsx'
const emit = defineEmits(['onClose', 'onloadError'])
const loading = ref(true)
const fileUrl = ref('')
const activeName = ref(0)
const tableList = ref([])
function init(url) {
loading.value = true
tableList.value = []
fileUrl.value = url
getFileContent()
}
async function getFileContent() {
try {
let res = await FileRequest.get(fileUrl.value)
let binary = ''
let bytes = new Uint8Array(res)
let length = bytes.byteLength
for (let i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i])
}
let wb = XLSX.read(binary, { type: 'binary' })
let sheetNameList = wb.SheetNames
sheetNameList.forEach((item, index) => {
let ws = wb.Sheets[item]
let itemContent = ''
try {
itemContent = XLSX.utils.sheet_to_html(ws)
} catch (error) {}
tableList.value.push({
name: item,
isSelected: index == 0,
content: itemContent
})
console.log(tableList.value)
})
loading.value = false
} catch (error) {
emit('onloadError')
}
}
function onClickLeft() {
emit('onClose')
}
function onClickSwitchTab(i) {
tableList.value.forEach((item, index) => {
item.isSelected = index == i
})
}
defineExpose({ init })
</script>
<style lang="less">
.excel {
flex: 1;
overflow-x: scroll;
align-items: center;
padding: 24px;
background-color: #f8f8f8;
.sheet {
display: flex;
white-space: nowrap;
padding-bottom: 30px;
span {
display: block;
height: 72px;
line-height: 72px;
padding: 0 24px;
background-color: #fff;
font-size: 28px;
box-shadow: 0px 4px 8px 6px rgba(204, 204, 204, 0.34);
&.active {
background-color: #ff6633;
color: #fff;
}
}
}
.table {
table {
border-collapse: collapse !important;
background-color: #fff;
td {
word-break: keep-all;
white-space: nowrap;
border: 2px solid #000;
padding: 0px 16px;
font-size: 24px;
color: #666;
}
}
}
}
::v-deep(.van-nav-bar) {
height: 88px !important;
}
::v-deep(.van-nav-bar .van-nav-bar__title.van-ellipsis) {
font-size: 34px !important;
}
::v-deep(.van-nav-bar .van-icon) {
font-size: 44px;
color: rgb(72, 72, 81);
}
.components-file-preview {
height: 100%;
.excel {
height: 100vh;
margin-top: 95px;
.sheet {
display: flex;
white-space: nowrap;
padding-bottom: 30px;
box-shadow: 0px 4px 8px 16px rgba(204, 204, 204, 0.34);
span {
display: block;
height: 72px;
line-height: 72px;
padding: 0 24px;
background-color: #fff;
font-size: 28px;
// box-shadow: 0px 4px 8px 16px rgba(204, 204, 204, 0.34);
&.active {
background-color: #ff6d00;
color: #fff;
}
}
}
.table {
table {
border-collapse: collapse !important;
background-color: #fff;
td {
word-break: keep-all;
white-space: nowrap;
border: 2px solid #000;
padding: 0px 16px;
font-size: 24px;
color: #666;
}
}
}
}
}
.van-nav-bar {
position: fixed !important;
top: 0;
width: 100%;
}
.flex.jc {
// -webkit-box-pack: center;
justify-content: center;
}
.pdt-30 {
padding-top: 60px;
}
</style>
import { escape } from 'lodash-es'
class FileRequest {
static get(url, responseType = 'arraybuffer') {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = responseType
if (responseType == 'text') {
xhr.setRequestHeader('Content-Type', 'text/text;charset=utf-8')
}
xhr.onload = () => {
if (xhr.status == 200) {
if (responseType == 'text') {
let decodedData = decodeURIComponent(escape(xhr.responseText))
resolve(decodedData)
} else {
resolve(xhr.response)
}
} else {
reject()
}
}
xhr.error = () => {
reject()
}
xhr.send()
})
}
}
export default FileRequest
<template>
<div id="imagePreview">
<van-popup
class="components-file-preview"
v-model:show="show"
position="bottom"
get-container="body"
:lazy-render="false"
>
<template v-if="isError">
<Preview-error ref="" :msg="errMsg" @onClose="onClose"></Preview-error>
</template>
<template v-else>
<Preview-excel
ref="excel"
v-if="cusFileType == 'excel'"
@onClose="onClose"
@onloadError="onloadError"
>
</Preview-excel>
</template>
</van-popup>
</div>
</template>
<script setup>
import { ref, nextTick, getCurrentInstance } from 'vue'
import { CONSTS_FILE_TYPE_MAP } from './js/map'
import { showToast } from 'vant'
import PreviewExcel from './child/PreviewExcel.vue'
import PreviewError from './child/PreviewError.vue'
const { proxy } = getCurrentInstance()
const fileUrl = ref('')
const fileType = ref('')
const cusFileType = ref('')
const show = ref(false)
const errMsg = ref('暂不支持当前格式')
const isError = ref(false)
const images = ref([])
const showImage = ref(false)
const fileRef = ref(null)
function open(url, type) {
if (!url) return showToast('暂无文件')
fileUrl.value = url
fileType.value = type
getFileType()
}
function getUrlData(url) {
let str = url.split('?')[1]
let obj = {}
let paramsArr = str.split('&')
for (let i = 0, len = paramsArr.length; i < len; i++) {
let arr = paramsArr[i].split('=')
obj[arr[0]] = arr[1]
}
return obj
}
function getFileType() {
let type = fileType.value
if (!type) {
let index = fileUrl.value.lastIndexOf('.')
type = fileUrl.value.substr(index + 1)
const types = ['xls', 'xlsx', 'doc', 'docx', 'pdf', 'jpeg', 'png', 'jpg', 'txt']
if (!types.includes(type)) {
let urlInfo = getUrlData(fileUrl.value)
let suffix = urlInfo['response-content-disposition']
let index = suffix.lastIndexOf('.')
type = suffix.substr(index + 1)
}
}
cusFileType.value = CONSTS_FILE_TYPE_MAP[type]
fileRef.value = cusFileType.value
if (!cusFileType.value) {
isError.value = true
show.value = true
return
}
if (cusFileType.value == 'image') {
images.value = [`${fileUrl.value}`]
showImage.value = true
return
}
nextTick(() => {
console.log(fileRef.value)
proxy.$refs[fileRef.value].init(fileUrl.value)
show.value = true
})
}
function onClose() {
isError.value = false
errMsg.value = '暂不支持当前格式'
show.value = false
}
function onloadError() {
isError.value = true
errMsg.value = '文件加载失败'
}
defineExpose({
open
})
</script>
<style lang="less">
</style>