You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RuoYi-Vue/ruoyi-ui-next/src/components/ui/FileUpload/index.vue

184 lines
5.2 KiB

<template>
<div class="upload-file">
<el-upload
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="uploadRef"
>
<el-button type="primary" size="small">选取文件</el-button>
<template #tip>
<div class="el-upload__tip" v-if="showTip">
<template v-if="fileSize"> <b class="danger-text">{{ fileSize }}MB</b> </template>
<template v-if="fileType.length"> <b class="danger-text">{{ fileType.join('/') }}</b> </template>
的文件
</div>
</template>
</el-upload>
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="file in fileList">
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
<el-icon><Document /></el-icon> {{ getFileName(file.name) }}
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(fileList.indexOf(file))" type="danger">删除</el-link>
</div>
</li>
</transition-group>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { Document } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { getToken } from '@/utils/auth'
import type { UploadFileItem } from '@/types'
interface Props {
modelValue?: string | UploadFileItem | UploadFileItem[]
limit?: number
fileSize?: number
fileType?: string[]
isShowTip?: boolean
}
const props = withDefaults(defineProps<Props>(), {
modelValue: '',
limit: 5,
fileSize: 5,
fileType: () => ['doc', 'xls', 'ppt', 'txt', 'pdf'],
isShowTip: true
})
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
const baseUrl = import.meta.env.VITE_APP_BASE_API || ''
const uploadFileUrl = `${import.meta.env.VITE_APP_BASE_API}/common/upload`
const headers = {
Authorization: `Bearer ${getToken()}`
}
const fileList = ref<UploadFileItem[]>([])
const showTip = computed(() => props.isShowTip && (props.fileType.length > 0 || props.fileSize > 0))
function handleBeforeUpload(file: File): boolean {
// Check file type
if (props.fileType.length > 0) {
let fileExtension = ''
const lastDotIndex = file.name.lastIndexOf('.')
if (lastDotIndex > -1) {
fileExtension = file.name.slice(lastDotIndex + 1)
}
const isTypeOk = props.fileType.some(type => {
if (file.type.indexOf(type) > -1) return true
if (fileExtension && fileExtension.indexOf(type) > -1) return true
return false
})
if (!isTypeOk) {
ElMessage.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`)
return false
}
}
// Check file size
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize
if (!isLt) {
ElMessage.error(`上传文件大小不能超过 ${props.fileSize} MB!`)
return false
}
}
return true
}
function handleExceed() {
ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
}
function handleUploadError() {
ElMessage.error('上传失败, 请重试')
}
function handleUploadSuccess(res: { fileName: string }) {
ElMessage.success('上传成功')
fileList.value.push({ name: res.fileName, url: res.fileName })
emit('update:modelValue', listToString(fileList.value))
}
function handleDelete(index: number) {
fileList.value.splice(index, 1)
emit('update:modelValue', listToString(fileList.value))
}
function getFileName(name: string): string {
const lastSlashIndex = name.lastIndexOf('/')
if (lastSlashIndex > -1) {
return name.slice(lastSlashIndex + 1).toLowerCase()
}
return name.toLowerCase()
}
function listToString(list: UploadFileItem[], separator = ','): string {
const urls = list.map(item => item.url)
return urls.join(separator)
}
watch(
() => props.modelValue,
(val) => {
if (val) {
let temp = 1
const list = Array.isArray(val) ? val : String(val).split(',')
fileList.value = list.map(item => {
if (typeof item === 'string') {
return { name: item, url: item, uid: new Date().getTime() + temp++ }
}
return { ...item, uid: (item as UploadFileItem).uid || new Date().getTime() + temp++ } as UploadFileItem
})
} else {
fileList.value = []
}
},
{ deep: true, immediate: true }
)
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
.danger-text {
color: #f56c6c;
}
</style>