效果图
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4f3cda1434ca4cb09a047526d673f963.png#pic_center)
一、新建公共组件 upload.vue 文件
<template>
<div class="blank">
<n-spin :show="show">
<div class="file-list">
<template v-for="v in uploadList" v-if="uploadList?.length">
<div class="item" :style="imageCard">
<i class="iconfont icon-colourspdf special" v-if="v.fileSuffix === 'pdf'"></i>
<i
class="iconfont icon-coloursword special"
v-else-if="v.fileSuffix === 'docx'"
></i>
<i
class="iconfont icon-coloursexcel special"
v-else-if="v.fileSuffix === 'xlsx'"
></i>
<template v-else>
<img :src="findUrl() + v.url || ''" />
<!-- <img :src="findUrl() + v.url || ''" v-if="listType === 'image-card'" /> -->
<!-- <n-image
v-else
:style="imageCard"
:src="findUrl() + v.url"
placeholder="加载中请稍后"
:fallback-src="findUrl() + v.url"
/> -->
</template>
<div class="action" v-if="actionShow">
<i class="iconfont icon-chakan" @click="handlePreview(v)"></i>
<i class="iconfont icon-cuowu" @click="onRemove(v)"></i>
</div>
</div>
</template>
<n-upload
v-if="maxCount > uploadList.length"
:multiple="multiple"
:on-change="onChange"
:on-before-upload="beforeUpload"
:list-type="listType"
>
<slot>
<div
class="default-style"
v-if="listType === 'image-card'"
:style="imageCard"
>
上传文件
</div>
<n-button v-else>上传文件</n-button>
</slot>
</n-upload>
</div>
</n-spin>
<span class="tips">{{ tips }}</span>
</div>
<Modal
:modalInfo="modalInfo"
:closeEsc="true"
@modalCallback="close"
:visible="showModal"
:isFullBtn="true"
:fullStyle="{ top: '14px' }"
>
<img :src="previewImageUrl" style="width: 100%; height: 95%" />
<template #footer>
<n-button @click="close">关 闭</n-button>
</template>
</Modal>
</template>
<script lang="ts" setup>
import { reactive, toRefs, watch } from 'vue'
import { UploadFileInfo } from 'naive-ui'
import { getUpload } from '@/api/public'
import { deepClone, findUrl } from '@/utils'
import Modal from '@/components/Modal/index.vue'
const emits = defineEmits<{
(e: 'uploadCallback', value: object): void
}>()
const defaultProps = defineProps({
fileList: {
type: Array,
default: () => []
},
maxCount: {
type: Number,
default: 5
},
accept: {
type: String,
default: 'jpg,png,jpeg,word'
},
maxSize: {
type: Number,
default: 2
},
defaultUpload: {
type: Boolean,
default: false
},
action: {
type: String,
default: ''
},
listType: {
type: String,
default: 'image-card',
validator: (value: string) => {
return ['text', 'image', 'image-card'].includes(value)
}
},
tips: {
type: String,
default: '支持扩展名:jpg、pdf、png、word,不超过50M'
},
multiple: {
type: Boolean,
default: false
},
imageCard: {
type: Object,
default: {
height: '96px',
width: '96px'
}
},
actionShow: {
type: Boolean,
default: true
}
})
const state = reactive({
showModal: false,
show: false,
previewImageUrl: '',
uploadList: <any[]>[],
modalInfo: {
title: '',
width: 800,
footerCenter: true,
height: 600
}
})
watch(
() => defaultProps.fileList,
(newVal: any) => {
state.uploadList = newVal
},
{
immediate: true,
deep: true
}
)
const beforeUpload = ({ file }: any) => {
console.log(file, 'file')
const suffix = file.name?.split('.').pop().toLowerCase()
console.log('file', file, suffix)
const suffixList = defaultProps.accept.split(',')
const flagFileType = !!suffixList.includes(suffix)
const flagSize =
file.file.size / 1024 / 1024 < parseInt(defaultProps.maxSize.toString())
!flagFileType &&
window['$confirmMessage'](
'error',
`您只能上传格式为${suffixList.join(', ')}的文件!`,
4000
)
!flagSize &&
window['$confirmMessage'](
'error',
`文件大小最大只能为${defaultProps.maxSize}M!`,
4000
)
return flagFileType && flagSize
}
const backUpload = () => {
const IdList = deepClone(state.uploadList)
emits('uploadCallback', {
list: state.uploadList,
IdList: IdList.map((item: any) => item.id)
})
}
const onRemove = (file: any) => {
console.log(file, 'onRemove')
state.uploadList = state.uploadList.filter((item: any) => item.id !== file.id)
backUpload()
}
const close = () => {
state.showModal = false
}
const onError = (file: UploadFileInfo) => {
window['$confirmMessage']('error', `服务器错误!请稍后在试`, 4000)
console.log(state.uploadList, file, 'state.uploadList')
}
const handlePreview = (file: any) => {
console.log(file, 'file')
const { url, fileSuffix } = file
if (fileSuffix === 'pdf' || fileSuffix === 'docx' || fileSuffix === 'xlsx') {
window.open(findUrl() + url)
return
}
state.previewImageUrl = url as string
state.showModal = true
}
const onChange = async (file: any) => {
if (file?.file?.status === 'pending') {
const params = { file: file?.file?.file }
state.show = true
console.log(params, 'params')
const res = await getUpload(params)
if (res.code === 0) {
state.uploadList.push({
id: res.data.id,
url: res.data.url,
fileSuffix: file?.file?.name.split('.')[1]
})
}
console.log(res, state.uploadList, file, 'formData')
state.show = false
backUpload()
}
}
const { showModal, previewImageUrl, uploadList, show, modalInfo } = toRefs(state)
</script>
<style lang="scss" scoped>
::v-deep(.n-form-item-blank) {
display: block !important;
}
::v-deep(.n-upload-file) {
display: none !important;
}
::v-deep(.n-upload) {
display: inline-block;
width: 96px;
margin-top: 3px;
}
.file-list {
display: flex;
align-items: center;
flex-wrap: wrap;
.item {
width: 96px;
height: 96px;
margin-right: 10px;
border: 1px dashed rgb(224, 224, 230);
display: flex;
justify-content: center;
align-items: center;
border-radius: 2px;
position: relative;
margin-top: 3px;
.action {
position: absolute;
top: 0;
right: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
box-sizing: border-box;
width: 100%;
height: 100%;
i {
cursor: pointer;
font-size: 20px;
display: none;
}
.icon-cuowu {
font-size: 22px;
}
}
.special {
font-size: 98px;
}
&:hover {
.action {
background-color: rgba(0, 0, 0, 0.5);
i {
display: block;
color: white;
}
}
}
img {
width: 97%;
height: 97%;
}
}
.item2 {
width: 40px;
height: 40px;
}
}
.default-style {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #666666;
}
.blank {
display: flex;
flex-direction: column;
.tips {
margin-top: 10px;
color: #666666;
}
}
</style>
2、使用该组件
import Upload from '@/components/Upload/index.vue'
<upload
@uploadCallback="() => uploadCallback()"
accept="jpg,png,jpeg,pdf,docx"
:maxCount="5"
tips="支持扩展名:jpg、pdf、png、word,不超过10M"
:fileList="formData.departmentLicList"
:maxSize="10"
/>
const uploadCallback = (e: any) => {
const { list } = e
formData.departmentLicList = list
console.log(list, 'list')
}