314 lines
8.2 KiB
Markdown
314 lines
8.2 KiB
Markdown
---
|
|
title: Element Plus 上传器
|
|
date: 2026-01-29
|
|
---
|
|
|
|
# 一、上传图片
|
|
```vue
|
|
<template>
|
|
<el-upload
|
|
v-model:file-list="imageFileList"
|
|
list-type="picture-card"
|
|
:on-preview="handlePreview"
|
|
:limit="3"
|
|
:before-upload="beforeUpload"
|
|
:http-request="httpRequest"
|
|
:on-exceed="handleExceed"
|
|
:before-remove="beforeRemove"
|
|
:on-remove="handleRemove"
|
|
>
|
|
<el-icon><Plus /></el-icon>
|
|
<template #tip>
|
|
<div class="el-upload__tip">
|
|
请上传小于10M的图片
|
|
</div>
|
|
</template>
|
|
|
|
<el-dialog v-model="dialogVisible">
|
|
<el-image :src="dialogImageUrl" alt="Preview Image" />
|
|
</el-dialog>
|
|
</el-upload>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { Plus } from '@element-plus/icons-vue'
|
|
import { onMounted, ref } from 'vue'
|
|
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
|
|
import type { UploadRequestOptions } from 'element-plus'
|
|
import type { UploadProps, UploadUserFile, UploadRawFile } from 'element-plus'
|
|
import { getFileExtension, getFileHashName, validateImage } from '@/utils/file.ts'
|
|
import { uploadFileApi } from '@/apis/file.ts'
|
|
|
|
const imageFileList = ref<UploadUserFile[]>([])
|
|
const dialogImageUrl = ref('')
|
|
const dialogVisible = ref(false)
|
|
|
|
const baseUrl = ''
|
|
const apiImageList = [] as string[]
|
|
|
|
onMounted(async () => {
|
|
imageFileList.value = []
|
|
|
|
// 根据后端传来的List数据将信息添加文件列表中
|
|
apiImageList.forEach((item, index) => {
|
|
imageFileList.value.push({
|
|
name: index.toString(),
|
|
url: `${baseUrl}${item}`
|
|
})
|
|
})
|
|
})
|
|
|
|
const beforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
|
|
const maxFileMax = 1024 * 1024 * 10
|
|
const fileType = rawFile.type
|
|
const fileName = rawFile.name
|
|
const fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase()
|
|
|
|
if (!validateImage(fileType, fileExtension)) {
|
|
ElMessage.error('请上传图片')
|
|
return false
|
|
}
|
|
|
|
if (rawFile.size > maxFileMax) {
|
|
ElMessage.error('请上传10M以内的文件!')
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
const httpRequest: UploadProps['httpRequest'] = async (options: UploadRequestOptions): Promise<any> => {
|
|
const loading = ElLoading.service({
|
|
lock: true,
|
|
text: '文件上传中 请等待',
|
|
background: 'rgba(0, 0, 0, 0.7)'
|
|
})
|
|
|
|
try {
|
|
// 重命名文件
|
|
const filePath = `${baseUrl}/${getFileHashName(options.file.name)}.${getFileExtension(options.file.name)}`
|
|
|
|
const data = new FormData()
|
|
data.append('file', options.file)
|
|
data.append('filePath', filePath)
|
|
|
|
// 调用后端接口上传文件
|
|
await uploadFileApi(data)
|
|
apiImageList.push(filePath)
|
|
ElMessage.success('上传成功')
|
|
} catch (error) {
|
|
console.log(error)
|
|
ElMessage.error('上传失败 请联系管理员')
|
|
} finally {
|
|
loading.close()
|
|
}
|
|
}
|
|
|
|
const handleExceed: UploadProps['onExceed'] = () => {
|
|
ElMessage.warning('最多上传3张图片')
|
|
}
|
|
|
|
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
|
const index = imageFileList.value.findIndex((item) => item.uid === uploadFile.uid)
|
|
apiImageList.splice(index, 1)
|
|
}
|
|
|
|
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
|
dialogImageUrl.value = uploadFile.url!
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
const beforeRemove: UploadProps['beforeRemove'] = () => {
|
|
return ElMessageBox.confirm('确认删除该文件?').then(() => true, () => false)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
# 二、上传视频
|
|
```vue
|
|
<template>
|
|
<el-upload
|
|
v-model:file-list="videoFileList"
|
|
:limit="1"
|
|
:before-upload="beforeUpload"
|
|
:http-request="httpRequest"
|
|
:on-exceed="handleExceed"
|
|
:before-remove="beforeRemove"
|
|
:on-remove="handleRemove"
|
|
>
|
|
<el-button type="primary">点击上传</el-button>
|
|
<template #tip>
|
|
<div class="el-upload__tip">
|
|
请上传小于200M的视频
|
|
</div>
|
|
</template>
|
|
</el-upload>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { onMounted, ref } from 'vue'
|
|
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
|
|
import type { UploadRequestOptions } from 'element-plus'
|
|
import type { UploadProps, UploadUserFile, UploadRawFile } from 'element-plus'
|
|
import { getFileExtension, getFileHashName, validateVideo } from '@/utils/file.ts'
|
|
import { uploadFileApi } from '@/apis/file.ts'
|
|
|
|
const videoFileList = ref<UploadUserFile[]>([])
|
|
|
|
let baseUrl = ''
|
|
const apiVideoUrl = ''
|
|
|
|
onMounted(async () => {
|
|
videoFileList.value = []
|
|
if (apiVideoUrl) {
|
|
videoFileList.value.push({
|
|
name: 'video',
|
|
url: `${baseUrl}p/${apiVideoUrl}`
|
|
})
|
|
}
|
|
})
|
|
|
|
const beforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => {
|
|
const maxFileMax = 1024 * 1024 * 200
|
|
const fileType = rawFile.type
|
|
const fileName = rawFile.name
|
|
const fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase()
|
|
|
|
if (!validateVideo(fileType, fileExtension)) {
|
|
ElMessage.error('请上传视频')
|
|
return false
|
|
}
|
|
|
|
if (rawFile.size > maxFileMax) {
|
|
ElMessage.error('请上传200M以内的文件!')
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
const httpRequest: UploadProps['httpRequest'] = async (options: UploadRequestOptions): Promise<any> => {
|
|
const loading = ElLoading.service({
|
|
lock: true,
|
|
text: '文件上传中 请等待',
|
|
background: 'rgba(0, 0, 0, 0.7)'
|
|
})
|
|
|
|
try {
|
|
const filePath = `${baseUrl}/${getFileHashName(options.file.name)}.${getFileExtension(options.file.name)}`
|
|
|
|
const data = new FormData()
|
|
data.append('file', options.file)
|
|
data.append('filePath', filePath)
|
|
|
|
await uploadFileApi(data)
|
|
baseUrl = filePath
|
|
ElMessage.success('上传成功')
|
|
} catch (error) {
|
|
console.log(error)
|
|
ElMessage.error('上传失败 请联系管理员')
|
|
} finally {
|
|
loading.close()
|
|
}
|
|
}
|
|
|
|
const handleExceed: UploadProps['onExceed'] = () => {
|
|
ElMessage.warning('最多上传1个视频')
|
|
}
|
|
|
|
const beforeRemove: UploadProps['beforeRemove'] = () => {
|
|
return ElMessageBox.confirm('确认删除该文件?').then(() => true, () => false)
|
|
}
|
|
|
|
const handleRemove: UploadProps['onRemove'] = () => {
|
|
baseUrl = ''
|
|
}
|
|
</script>
|
|
```
|
|
|
|
# 三、工具类
|
|
```ts
|
|
import { hashSHA256 } from 'vue3-common/utils/cryptoUtil'
|
|
|
|
export const getFileHashName = (name: string) => {
|
|
return hashSHA256(name)
|
|
}
|
|
|
|
export const getFileExtension = (name: string) => {
|
|
return name.substring(name.lastIndexOf('.') + 1).toLowerCase()
|
|
}
|
|
|
|
export const validateExcel = (fileType: string, fileExtension: string): boolean => {
|
|
// 常见的 Excel MIME 类型
|
|
const excelMimeTypes = [
|
|
'application/vnd.ms-excel', // .xls
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
|
|
'application/vnd.ms-excel.sheet.macroEnabled.12', // .xlsm
|
|
'text/csv' // CSV
|
|
]
|
|
|
|
// 常见的 Excel 文件扩展名
|
|
const excelExtensions = ['xls', 'xlsx', 'xlsm', 'csv']
|
|
|
|
return excelMimeTypes.includes(fileType) || excelExtensions.includes(fileExtension.toLowerCase())
|
|
}
|
|
|
|
export const validateImage = (fileType: string, fileExtension: string): boolean => {
|
|
// 常见的图片 MIME 类型
|
|
const imageMimeTypes = [
|
|
'image/jpeg', // JPEG
|
|
'image/jpg', // JPG
|
|
'image/png', // PNG
|
|
'image/gif', // GIF
|
|
'image/bmp', // BMP
|
|
'image/webp', // WebP
|
|
'image/svg+xml' // SVG
|
|
]
|
|
|
|
// 常见的图片文件扩展名
|
|
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg']
|
|
|
|
return imageMimeTypes.includes(fileType) || imageExtensions.includes(fileExtension.toLowerCase())
|
|
}
|
|
|
|
export const validateVideo = (fileType: string, fileExtension: string): boolean => {
|
|
// 常见的视频 MIME 类型
|
|
const videoMimeTypes = [
|
|
'video/mp4', // MP4
|
|
'video/mpeg', // MPEG
|
|
'video/quicktime', // MOV
|
|
'video/x-msvideo', // AVI
|
|
'video/x-matroska', // MKV
|
|
'video/webm', // WebM
|
|
'video/3gpp', // 3GP
|
|
'video/3gpp2', // 3G2
|
|
'video/x-flv', // FLV
|
|
'video/mp2t', // TS (MPEG Transport Stream)
|
|
'application/x-mpegURL', // M3U8 (HLS)
|
|
'video/H264', // H264
|
|
'video/H265' // H265/HEVC
|
|
]
|
|
|
|
// 常见的视频文件扩展名
|
|
const videoExtensions = [
|
|
'mp4',
|
|
'mpeg',
|
|
'mpg',
|
|
'mov',
|
|
'avi',
|
|
'mkv',
|
|
'webm',
|
|
'3gp',
|
|
'3g2',
|
|
'flv',
|
|
'ts',
|
|
'm3u8',
|
|
'h264',
|
|
'h265',
|
|
'hevc'
|
|
]
|
|
|
|
return videoMimeTypes.includes(fileType) || videoExtensions.includes(fileExtension.toLowerCase())
|
|
}
|
|
```
|