Bootstrap

Ant Design Vue和VUE3下的upload组件使用以及文件预览

在这里插入图片描述

Ant Design Vue和VUE3下的upload组件使用以及文件预览

用到技术:Ant Design Vue、VUE3、xlsx的文件预览功能(也可预览txt,csv)

一、多文件上传

1.需求

  1. 可以多文件上传
  2. 文件先上传到本地,点击开始上传再通过后端接口继续上传
  3. 上传后显示全部、正确和错误的数据信息
  4. 上传后可以下载

注意细节:

  1. 最开始文件上传到本地时,右边页面不加载,当点击上传后,在右边未获取数据之前,
    a.上传组件无法再次上传;
    b:右边数据均处于加载中状态;
    c.文件下载处于不可编辑状态
  2. 加载出数据后,
    a.上传组件可再次上传;
    b.文件预览中的全部、正确和错误数据均需显示出对应的数据序号
    c.正确和错误的数据,均不显示‘比对结果’,仅全部数据显示
  3. 文件下载接口注意文件类型为‘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. 需求

  1. 单个文件上传
  2. 文件直接上传,直接调用后端接口
  3. 上传后显示全部、正确和错误的数据信息
  4. 上传后可以下载

注意细节:

  1. 文件上传未完成时,不可以再次上传,不可以下载,右边需要处于加载状态
  2. 文件再次上传后,原文件被覆盖,需要清除之前的fileList
  3. 文件下载接口注意类型为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
在这里插入图片描述

;