| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- <template>
- <div class="app-upload">
- <el-upload ref="uploadRef" :accept="acceptTypes.join(',')" :file-list="fileList" :limit="limit"
- :multiple="limit > 1" :action="uploadUrl" :before-upload="onBeforeUpload" @success="onSuccess"
- @remove="onRemove" @preview="onPreview" @exceed="onExceed">
- <template #tip>
- <div class="el-upload__tip">
- <slot name="tip"></slot>
- </div>
- </template>
- <slot>
- <el-button type="primary">{{ t('operation.upload') }}</el-button>
- </slot>
- </el-upload>
- <el-image-viewer :url-list="imageList.map((e) => e.filePath)" :initial-index="imageIndex" @close="onViewerClose"
- v-if="showViewer" teleported />
- </div>
- </template>
- <script lang="ts" setup>
- import { shallowRef, shallowReactive, computed, PropType, toRaw, onMounted } from 'vue'
- import { ElMessage, UploadProps, UploadRawFile, UploadFile, UploadFiles, UploadInstance, genFileId } from 'element-plus'
- import { getFileUrl } from '@/filters'
- import service from '@/services'
- import { i18n } from '@/stores'
- const { t } = i18n.global
- const props = defineProps({
- fileTypes: {
- type: Array as PropType<readonly ('image' | 'pdf' | 'word' | 'excel')[]>,
- default: () => ([])
- },
- limit: {
- type: Number,
- default: 1
- },
- maxSize: {
- type: Number,
- default: 0
- },
- typeMessage: {
- type: String,
- default: '请选择正确的文件类型'
- }
- })
- const emit = defineEmits(['change'])
- const uploadUrl = service.getServiceConfig('uploadUrl')
- const uploadRef = shallowRef<UploadInstance>()
- const showViewer = shallowRef(false)
- const fileList = shallowRef<UploadFiles>([])
- const acceptTypes = shallowReactive<string[]>([]) // 接受上传的文件类型
- const uploadTypes = shallowReactive<string[]>([]) // 允许上传的文件类型
- const imageIndex = shallowRef(0)
- const imageTypes = ['image/png', 'image/jpeg']
- // 预览图列表
- const imageList = computed(() => {
- const result: { uid: number, filePath: string }[] = []
- fileList.value.forEach(({ raw, uid, response }) => {
- if (raw && uid && response) {
- // 判断是否图片类型
- if (imageTypes.includes(raw.type)) {
- (response as { filePath: string }[]).forEach((e) => {
- result.push({
- uid,
- filePath: getFileUrl(e.filePath)
- })
- })
- }
- }
- })
- return result
- })
- const onChange = () => {
- const files = fileList.value.reduce((res, { response }) => {
- const list = (response ?? []) as unknown[]
- list.forEach((e) => {
- res.push(toRaw(e))
- })
- return res
- }, [] as unknown[])
- emit('change', props.limit > 1 ? files : files[0] ?? {})
- }
- // 当超出限制时的回调
- const onExceed = (files: File[]) => {
- if (props.limit === 1) {
- const el = uploadRef.value
- if (el) {
- const rawFile = files[0] as UploadRawFile
- if (uploadTypes.includes(rawFile.type)) {
- rawFile.uid = genFileId()
- el.clearFiles()
- el.handleStart(rawFile)
- el.submit()
- } else {
- ElMessage.warning(props.typeMessage)
- }
- }
- } else {
- ElMessage.warning(`最多只能上传${props.limit}个文件`)
- }
- }
- // 上传之前判断文件类型
- const onBeforeUpload = (rawFile: UploadRawFile) => {
- if (uploadTypes.includes(rawFile.type)) {
- return true
- }
- ElMessage.warning(props.typeMessage)
- return false
- }
- // 上传成功的回调
- const onSuccess = (response: unknown, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
- fileList.value = uploadFiles
- onChange()
- }
- const onRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
- fileList.value = uploadFiles
- onChange()
- }
- // 打开预览图
- const onPreview: UploadProps['onPreview'] = ({ uid, raw }: UploadFile) => {
- if (imageList.value.length) {
- // 判断是否图片类型
- if (imageTypes.includes(raw?.type ?? '')) {
- const index = imageList.value.findIndex((e) => e.uid === uid)
- if (index > -1) {
- imageIndex.value = index
- showViewer.value = true
- }
- }
- }
- }
- const onViewerClose = () => {
- showViewer.value = false
- }
- onMounted(() => {
- props.fileTypes.forEach((value) => {
- switch (value) {
- case 'image': {
- acceptTypes.push('.jpg', '.jpeg', '.png')
- uploadTypes.push(...imageTypes)
- break
- }
- case 'pdf': {
- acceptTypes.push('.pdf')
- uploadTypes.push('application/pdf')
- break
- }
- case 'word': {
- acceptTypes.push('.doc', '.docx')
- uploadTypes.push('application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
- break
- }
- case 'excel': {
- acceptTypes.push('.xls', '.xlsx')
- uploadTypes.push('application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
- break
- }
- }
- })
- })
- </script>
- <style lang="less">
- @import './index.less';
- </style>
|