Ant Design Vue和VUE3下的upload组件使用以及文件预览
文章目录
用到技术:Ant Design Vue、VUE3、xlsx的文件预览功能(也可预览txt,csv)
一、多文件上传
1.需求
- 可以多文件上传
- 文件先上传到本地,点击开始上传再通过后端接口继续上传
- 上传后显示全部、正确和错误的数据信息
- 上传后可以下载
注意细节:
- 最开始文件上传到本地时,右边页面不加载,当点击上传后,在右边未获取数据之前,
a.上传组件无法再次上传;
b:右边数据均处于加载中状态;
c.文件下载处于不可编辑状态- 加载出数据后,
a.上传组件可再次上传;
b.文件预览中的全部、正确和错误数据均需显示出对应的数据序号
c.正确和错误的数据,均不显示‘比对结果’,仅全部数据显示- 文件下载接口注意文件类型为‘blob’
2.样例
3.代码
<template>
<a-row :gutter="8">
<a-col :span="8">
<a-card title="文件上传">
<a-upload-dragger
v-model:fileList="fileList"
name="files"
accept=".txt"
:multiple="true"
:action="action"
:before-upload="beforeUpload"
:disabled="upLoadDisabled"
@remove="handleRemove"
:showUploadList="{
showRemoveIcon: true
}"
>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">点击或将文件拖拽到这里上传, 支持扩展名:.txt</p>
</a-upload-dragger>
<div style="text-align: right">
<a-button
type="primary"
:disabled="fileList.length === 0"
:loading="uploading"
style="margin-top: 16px"
@click="handleUpload"
>
{{ uploading ? '上传中' : '开始上传' }}
</a-button>
</div>
</a-card>
</a-col>
<a-col :span="16">
<a-card>
<a-spin :spinning="upLoadSpinning" tip="数据加载中...">
<a-row :gutter="16">
<a-col :span="12">
<a-statistic title="正确/错误(个)" :value="trueNum" class="demo-class">
<template #suffix>
<span>/{{ falseNum }}</span>
</template>
</a-statistic>
</a-col>
<a-col :span="12">
<a-statistic title="完善度" :value="wcdPercent" style="margin-right: 50px" />
</a-col>
</a-row>
</a-spin>
</a-card>
<a-card title="文件预览" style="margin-top: 5px">
<a-spin :spinning="upLoadSpinning" tip="数据加载中...">
<a-tabs v-model:activeKey="activeKey" type="card" @change="tabChange">
<a-tab-pane key="0">
<template #tab>
<div>全部</div>
</template>
<div style="padding-bottom: 20px; padding-top: 20px">
<div v-if="tableData0.length > 0">
<a-table :columns="columns" :data-source="tableData0" bordered> </a-table>
</div>
<div v-else>
<div class="emptyStyle"></div>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="1">
<template #tab>
<div>正确</div>
</template>
<div style="padding-bottom: 20px; padding-top: 20px">
<div v-if="tableData1.length > 0">
<a-table :columns="columns1" :data-source="tableData1" bordered> </a-table>
</div>
<div v-else>
<div class="emptyStyle"></div>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab>
<div>错误</div>
</template>
<div style="padding-bottom: 20px; padding-top: 20px">
<div v-if="tableData2.length > 0">
<a-table :columns="columns1" :data-source="tableData2" bordered> </a-table>
</div>
<div v-else>
<div class="emptyStyle"></div>
</div>
</div>
</a-tab-pane>
<template #rightExtra>
<a-button @click="fileDownloadBtn" type="primary" :disabled="fileDownload">文件下载</a-button>
</template>
</a-tabs>
</a-spin>
</a-card>
</a-col>
</a-row>
</template>
<script setup>
import { message } from 'ant-design-vue'
import sysConfig from '@/config'
import uploadApi from '@/api/auth/uploadApi'
import * as XLSX from 'xlsx'
import { clone } from 'lodash-es'
import fileApi from '@/api/dev/fileApi'
const props = defineProps({
action: {
type: String,
default: '/biz/file/upload',
required: false
}
})
const action = sysConfig.API_URL + props.action
const fileList = ref([])
const beforeUpload = (file) => {
fileList.value = [...(fileList.value || []), file]
return false
}
const handleRemove = (file) => {
const index = fileList.value.indexOf(file)
const newFileList = fileList.value.slice()
newFileList.splice(index, 1)
fileList.value = newFileList
}
const handleUpload = () => {
// 可预览
upLoadSpinning.value = true
upLoadDisabled.value = true
fileDownload.value = true
uploading.value = true
const fileData = new FormData()
fileList.value.forEach((item) => {
fileData.append('files', item.originFileObj)
})
uploadApi
.upload(fileData)
.then((res) => {
message.success(`文件上传成功`)
previewFile()
trueNum.value = res.trueNum
falseNum.value = res.falseNum
wcdPercent.value = res.wcdPercent
})
.catch(() => {
message.error(`文件上传失败`)
})
.finally(() => {
//开始上传按钮
uploading.value = false
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
//可下载
fileDownload.value = false
})
}
const activeKey = ref('0')
const uploading = ref(false)
const upLoadSpinning = ref(true)
const upLoadDisabled = ref(true)
const tableData = ref([])
const tableData0 = ref([])
const tableData1 = ref([])
const tableData2 = ref([])
const columns = ref({})
const columns1 = ref({})
const previewFile = () => {
uploadApi
.download()
.then((res) => {
const reader = new FileReader()
reader.readAsBinaryString(res.data)
reader.onload = (ev) => {
try {
const data = ev.target.result
const wb = XLSX.read(data, {
type: 'binary',
cellText: false,
cellDates: true
})
const outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]])
let tableheader = outdata[0]
columns.value = [
{
title: '序号',
dataIndex: '序号',
key: '序号'
}
]
columns1.value = [
{
title: '序号',
dataIndex: '序号',
key: '序号'
}
]
for (let val in tableheader) {
if (val !== '__EMPTY') {
columns.value.push({
title: val,
dataIndex: val,
key: val
})
if (val !== '比对结果') {
columns1.value.push({
title: val,
dataIndex: val,
key: val
})
}
}
}
tableData.value = clone(outdata)
tabChange(activeKey.value)
} catch (e) {
return false
}
}
fileDownload.value = false
})
.catch((error) => {
console.log(error)
})
}
const tabChange = (key) => {
if (key === '0') {
const tableAll = tableData.value
tableAll.forEach((v, i) => {
v['序号'] = v.__EMPTY + 1
v = { ...v, key: i + 1 }
})
tableData0.value = tableAll
} else if (key === '1') {
const tableTrue = tableData.value.filter((item) => item['比对结果'] === 1)
tableTrue.forEach((v, i) => {
v['序号'] = i + 1
})
tableData1.value = tableTrue
} else if (key === '2') {
const tableFalse = tableData.value.filter((item) => item['比对结果'] === 0)
tableFalse.forEach((v, i) => {
v['序号'] = i + 1
})
tableData2.value = tableFalse
}
}
const fileDownload = ref(true)
const trueNum = ref(0)
const falseNum = ref(0)
const wcdPercent = ref(0)
const fileDownloadBtn = () => {
upLoadSpinning.value = true
upLoadDisabled.value = true
fileDownload.value = true
uploadApi
.download()
.then((res) => {
message.success('文件下载成功')
const blob = new Blob([res.data], { type: 'application/octet-stream;charset=UTF-8' })
const $link = document.createElement('a')
$link.href = URL.createObjectURL(blob)
// $link.download = 'xxx.xlsx'
$link.download = 'xxx.csv'
$link.click()
document.body.appendChild($link)
document.body.removeChild($link) // 下载完成移除元素
window.URL.revokeObjectURL($link.href) // 释放掉blob对象
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
//可下载
fileDownload.value = false
})
.catch((error) => {
console.log(error)
})
}
onMounted(() => {
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
})
</script>
<style scoped>
.emptyStyle {
height: 200px;
background-image: url(../../assets/images/empty1.png);
background-position: center;
background-repeat: no-repeat;
background-size: 200px 200px;
cursor: default;
}
</style>
二、单文件上传
1. 需求
- 单个文件上传
- 文件直接上传,直接调用后端接口
- 上传后显示全部、正确和错误的数据信息
- 上传后可以下载
注意细节:
- 文件上传未完成时,不可以再次上传,不可以下载,右边需要处于加载状态
- 文件再次上传后,原文件被覆盖,需要清除之前的fileList
- 文件下载接口注意类型为blob
2. 样例
3.代码
<template>
<a-row :gutter="8">
<a-col :span="8">
<a-card title="文件上传">
<a-upload-dragger
v-model:fileList="fileList"
name="files"
accept=".txt"
:multiple="false"
:action="action"
@change="handleChange"
:disabled="upLoadDisabled"
:showUploadList="{
showRemoveIcon: false
}"
>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">点击或将文件拖拽到这里上传, 支持扩展名:.txt</p>
<p class="ant-upload-hint">仅支持单个文件上传</p>
<template #removeIcon><StarOutlined></StarOutlined></template>
</a-upload-dragger>
</a-card>
</a-col>
<a-col :span="16">
<a-card>
<a-spin :spinning="upLoadSpinning" tip="文件加载中...">
<a-row :gutter="16">
<a-col :span="12">
<a-statistic title="正确/错误(个)" :value="trueNum" class="demo-class">
<template #suffix>
<span>/{{ falseNum }}</span>
</template>
</a-statistic>
</a-col>
<a-col :span="12">
<a-statistic title="完善度" :value="wcdPercent" style="margin-right: 50px" />
</a-col>
</a-row>
</a-spin>
</a-card>
<a-card title="文件预览" style="margin-top: 5px">
<a-spin :spinning="upLoadSpinning" tip="文件加载中...">
<a-tabs v-model:activeKey="activeKey" type="card" @change="tabChange">
<a-tab-pane key="0">
<template #tab>
<div>全部</div>
</template>
<div style="padding-bottom: 20px; padding-top: 20px">
<div v-if="tableData0.length > 0">
<a-table :columns="columns" :data-source="tableData0" bordered> </a-table>
</div>
<div v-else>
<div class="emptyStyle"></div>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="1">
<template #tab>
<div>正确</div>
</template>
<div style="padding-bottom: 20px; padding-top: 20px">
<div v-if="tableData1.length > 0">
<a-table :columns="columns1" :data-source="tableData1" bordered> </a-table>
</div>
<div v-else>
<div class="emptyStyle"></div>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab>
<div>错误</div>
</template>
<div style="padding-bottom: 20px; padding-top: 20px">
<div v-if="tableData2.length > 0">
<a-table :columns="columns1" :data-source="tableData2" bordered> </a-table>
</div>
<div v-else>
<div class="emptyStyle"></div>
</div>
</div>
</a-tab-pane>
<template #rightExtra>
<a-button @click="fileDownloadBtn" type="primary" :disabled="fileDownload">文件下载</a-button>
</template>
</a-tabs>
</a-spin>
</a-card>
</a-col>
</a-row>
</template>
<script setup>
import { message } from 'ant-design-vue'
import sysConfig from '@/config'
import uploadApi from '@/api/auth/uploadApi'
import * as XLSX from 'xlsx'
import { clone } from 'lodash-es'
const props = defineProps({
action: {
type: String,
default: '/biz/file/upload',
required: false
}
})
const action = sysConfig.API_URL + props.action
const fileList = ref([])
const handleChange = (info) => {
if (fileList.value.length > 1) {
fileList.value.shift()
}
upLoadSpinning.value = true
upLoadDisabled.value = true
fileDownload.value = true
const status = info.file.status
if (status === 'done') {
if (info.file.response.code === 200) {
message.success(`${info.file.name}上传成功`)
trueNum.value = info.file.response.data.trueNum
falseNum.value = info.file.response.data.falseNum
wcdPercent.value = info.file.response.data.wcdPercent
//文件上传禁选择
upLoadDisabled.value = false
}
//可预览
upLoadSpinning.value = false
//可下载
fileDownload.value = false
previewFile(1)
} else if (status === 'error') {
message.error(`${info.file.name}上传失败`)
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
//可下载
fileDownload.value = false
} else if (status === 'removed') {
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
//可下载
fileDownload.value = false
}
}
const activeKey = ref('0')
const upLoadSpinning = ref(true)
const upLoadDisabled = ref(true)
const tableData = ref([])
const tableData0 = ref([])
const tableData1 = ref([])
const tableData2 = ref([])
const columns = ref({})
const columns1 = ref({})
const previewFile = () => {
uploadApi
.download()
.then((res) => {
const reader = new FileReader()
reader.readAsBinaryString(res.data)
reader.onload = (ev) => {
try {
const data = ev.target.result
const wb = XLSX.read(data, {
type: 'binary',
cellText: false,
cellDates: true
})
const outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]])
let tableheader = outdata[0]
columns.value = [
{
title: '序号',
dataIndex: '序号',
key: '序号'
}
]
columns1.value = [
{
title: '序号',
dataIndex: '序号',
key: '序号'
}
]
for (let val in tableheader) {
if (val !== '__EMPTY') {
columns.value.push({
title: val,
dataIndex: val,
key: val
})
if (val !== '比对结果') {
columns1.value.push({
title: val,
dataIndex: val,
key: val
})
}
}
}
tableData.value = clone(outdata)
tabChange(activeKey.value)
} catch (e) {
return false
}
}
fileDownload.value = false
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
})
.catch((error) => {
console.log(error)
})
}
const tabChange = (key) => {
console.log(key, 'key')
if (key === '0') {
const tableAll = tableData.value
tableAll.forEach((v, i) => {
v['序号'] = v.__EMPTY + 1
v = { ...v, key: i + 1 }
})
tableData0.value = tableAll
} else if (key === '1') {
const tableTrue = tableData.value.filter((item) => item['比对结果'] === 1)
tableTrue.forEach((v, i) => {
v['序号'] = i + 1
})
tableData1.value = tableTrue
} else if (key === '2') {
const tableFalse = tableData.value.filter((item) => item['比对结果'] === 0)
tableFalse.forEach((v, i) => {
v['序号'] = i + 1
})
tableData2.value = tableFalse
}
}
const fileDownload = ref(true)
const trueNum = ref(0)
const falseNum = ref(0)
const wcdPercent = ref(0)
const fileDownloadBtn = () => {
upLoadSpinning.value = true
upLoadDisabled.value = true
fileDownload.value = true
uploadApi
.download()
.then((res) => {
message.success('文件下载成功')
const blob = new Blob([res.data], { type: 'application/octet-stream;charset=UTF-8' })
const $link = document.createElement('a')
$link.href = URL.createObjectURL(blob)
// $link.download = 'xxx.xlsx'
$link.download = 'xxx.csv'
$link.click()
document.body.appendChild($link)
document.body.removeChild($link) // 下载完成移除元素
window.URL.revokeObjectURL($link.href) // 释放掉blob对象
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
//可下载
fileDownload.value = false
})
.catch((error) => {
console.log(error)
})
}
onMounted(() => {
//文件上传禁选择
upLoadDisabled.value = false
//可预览
upLoadSpinning.value = false
})
</script>
<style scoped>
.emptyStyle {
height: 200px;
background-image: url(../../assets/images/empty1.png);
background-position: center;
background-repeat: no-repeat;
background-size: 200px 200px;
cursor: default;
}
</style>
二、多文件上传产生的时间超时问题
当上传文件太多时,需要延长请求时间,以减少报错情况
文件路径:src/config/index.js
修改该文件中的TIMEOUT
以延长请求时间
三、文件系统名称更改
1. 修改文件index.html
文件路径:index.html
,修改这两处,即可修改刷新系统时候,浏览器显示的系统名称
2. 修改文件index.js
文件路径:src/config/index.js
,修改此处即可修改登陆页面系统名称
3. 修改数据库存储的系统名称
四、系统登陆后的首页更改
原始首页为个人首页,若想修改为自己想要的页面,并且面包屑也不显示已去除的个人首页时,需修改文件util.js
文件路径:src/views/auth/login/util.js