<el-upload
v-model:file-list="fileList"
accept="*/*"
:action="uploadUrl"
:before-upload="beforeUpload"
:class="['upload', drag ? 'no-border' : '']"
:disabled="disabled"
:drag="drag"
:data="uploadData"
:headers="headers"
:limit="limit"
:multiple="true"
:on-error="uploadError"
:on-exceed="handleExceed"
:on-success="uploadSuccess"
list-type="picture-card"
:show-file-list="true"
>
<slot name="empty">
<Icon icon="ep:plus" />
</slot>
<template #file="{ file }">
<Icon icon="ep:zoom-in" />
查看
<Icon icon="ep:delete" />
删除
</template>
</el-upload>
<template v-if="fileType">
只能上传 {{ showFileType.join('/') }}
</template>
的文件
<slot name="tip"></slot>
<el-image-viewer
v-if="imgViewVisible"
:url-list="viewImageUrlList"
:initial-index="currentImageIndex"
@close="imgViewVisible = false"
/>
</template>
<script lang="ts" setup>
import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
import { ElNotification } from 'element-plus'
import { getAccessToken } from '@/utils/auth'
import { propTypes } from '@/utils/propTypes'
// import { useUpload } from '@/components/UploadFile/src/useUpload'
defineOptions({ name: 'UploadImgs' })
const message = useMessage() // 消息弹窗
type FileTypes =
| 'image/apng'
| 'image/bmp'
| 'image/gif'
| 'image/jpeg'
| 'image/pjpeg'
| 'image/png'
| 'image/svg+xml'
| 'image/tiff'
| 'image/webp'
| 'image/x-icon'
| 'application/dwg'
const props = defineProps({
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
limit: propTypes.number.def(20), // 最大图片上传数 ==> 非必传(默认为 20张)
//fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif', 'image/jpg','application/dwg']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
data: propTypes.object.def({}), // 添加 data 属性
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
},
showFileType: propTypes.array.def(['.jpeg', '.png', '.svg','.dwg'])
})
const uploadData = computed(() => {
return {
tableName: props.data?.tableName || '' // 确保 tableName 被正确传递
}
})
// const baseUrl = import.meta.env.VITE_API_URL // 请求路径
const uploadUrl = import.meta.env.VITE_UPLOAD_URL // 上传路径
const headers = {
Authorization: 'Bearer ' + getAccessToken()
}
// const { uploadUrl, httpRequest } = useUpload()
const fileList = ref<UploadUserFile[]>([])
// const uploadList = ref([])
const uploadNumber = ref<number>(0)
const uploadList = ref<UploadUserFile[]>([])
// 是否显示提示
const showTip = computed(() => {
//return props.isShowTip && (props.fileType || props.fileSize)
return props.isShowTip && props.fileType
})
/**
* @description 文件上传之前判断
* @param rawFile 上传的文件
* */
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
// const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
// if (!imgSize)
// ElNotification({
// title: '温馨提示',
// message: `上传图片大小不能超过 ${props.fileSize}M!`,
// type: 'warning'
// })
// const imgType = props.fileType
// if (!imgType.includes(rawFile.type as FileTypes))
// ElNotification({
// title: '温馨提示',
// message: '上传图片不符合所需的格式!',
// type: 'warning'
// })
uploadNumber.value++
return true
//return imgType.includes(rawFile.type as FileTypes) && imgSize
//return imgType.includes(rawFile.type as FileTypes)
}
// 图片上传成功
interface UploadEmits {
(e: 'update:modelValue', value: string[]): void
}
const emit = defineEmits<UploadEmits>()
// const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
// console.log('res.data:', res.data);
// message.success('上传成功')
// // 确保res.data是字符串类型
// if (typeof res.data === 'string') {
// console.log('res.data:', res.data);
// // 删除自身
// const index = fileList.value.findIndex((item) => item.response?.data === res.data)
// fileList.value.splice(index, 1)
// uploadList.value.push({ name: res.data, url: res.data })
// if (uploadList.value.length == uploadNumber.value) {
// fileList.value.push(...uploadList.value)
// uploadList.value = []
// uploadNumber.value = 0
// emitUpdateModelValue()
// }
// } else {
// console.log('Invalid response data:', res.data)
// }
// }
// const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
// message.success('上传成功')
// // 确保响应数据有效
// if (res && typeof res.data === 'string') {
// // 删除临时上传记录
// const index = fileList.value.findIndex((item) => item.response?.data === res.data)
// if (index !== -1) {
// fileList.value.splice(index, 1)
// }
// // 添加到已上传列表
// uploadList.value.push({
// name: res.data.substring(res.data.lastIndexOf('/') + 1),
// url: `${res.data}?token=${token}`
// // url: `${res.data}`
// })
// // 检查是否所有文件都已上传完成
// if (uploadList.value.length === uploadNumber.value) {
// fileList.value.push(...uploadList.value)
// uploadList.value = []
// uploadNumber.value = 0
// emitUpdateModelValue()
// }
// } else {
// console.error('Invalid response data:', res.data)
// message.error('上传响应数据格式错误')
// }
// }
// 在 uploadSuccess 函数中修改 URL 构造方式
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
console.log('fileList',fileList.value);
message.success('上传成功')
if (res && typeof res.data === 'string') {
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
if (index !== -1) {
fileList.value.splice(index, 1)
}
// 使用 getAccessToken() 获取 token
const token = getAccessToken()
uploadList.value.push({
name: res.data.substring(res.data.lastIndexOf('/') + 1),
// url: token ? `${res.data}?token=${token}` : res.data
url: token ? `${res.data}` : res.data
// url: res.data.url
})
if (uploadList.value.length === uploadNumber.value) {
fileList.value.push(...uploadList.value)
uploadList.value = []
uploadNumber.value = 0
emitUpdateModelValue()
}
} else {
console.error('Invalid response data:', res.data)
message.error('上传响应数据格式错误')
}
}
// 监听模型绑定值变动
watch(
() => props.modelValue,
(val: string | string[]) => {
if (!val) {
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
return
}
fileList.value = [] // 保障数据为空
// fileList.value.push(
// ...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
// )
fileList.value.push(
...(val as string[]).map((url) => {
if (typeof url === 'string') {
return { name: url.substring(url.lastIndexOf('/') + 1), url }
} else {
// 处理非字符串的 url,可能是抛出错误或返回一个默认值
console.error('Invalid url:', url)
return { name: '', url: '' } // 或者其他合适的默认值
}
})
)
},
{ immediate: true, deep: true }
)
// 发送图片链接列表更新
const emitUpdateModelValue = () => {
let result: string[] = fileList.value.map((file) => file.url!)
emit('update:modelValue', result)
}
// 删除图片
const handleRemove = (uploadFile: UploadFile) => {
fileList.value = fileList.value.filter(
(item) => item.url !== uploadFile.url || item.name !== uploadFile.name
)
emit(
'update:modelValue',
fileList.value.map((file) => file.url!)
)
}
// 图片上传错误提示
const uploadError = () => {
ElNotification({
title: '温馨提示',
message: '图片上传失败,请您重新上传!',
type: 'error'
})
}
// 文件数超出提示
const handleExceed = () => {
ElNotification({
title: '温馨提示',
message: `当前最多只能上传 ${props.limit} 张图片,请移除后上传!`,
type: 'warning'
})
}
// 图片预览
const viewImageUrlList = ref<string[]>([])
const imgViewVisible = ref(false)
const currentImageIndex = ref(0) // 新增:当前点击的图片索引
const handlePictureCardPreview = (uploadFile: UploadFile) => {
viewImageUrlList.value = fileList.value.map(file => file.url!) // 将所有图片的 URL 存储在数组中
currentImageIndex.value = fileList.value.findIndex(file => file.url === uploadFile.url) // 获取当前点击的图片索引
console.log('currentImageIndex.value',currentImageIndex.value);
imgViewVisible.value = true
}
//获取cookie
const getCookie = (name) => {
const value = `; ${document.cookie}`
const parts = value.split(`; ${name}=`)
if (parts.length === 2) {
// 如果找到了对应的cookie,返回其值(去除后面的分号和其他可能的属性)
return parts.pop().split(';').shift()
}
// 如果没有找到对应的cookie,返回null或undefined
return null
}
// 获取 accessToken
const token = getCookie('accessToken')
</script>
<style lang="scss" scoped>
.is-error {
.upload {
:deep(.el-upload--picture-card),
:deep(.el-upload-dragger) {
border: 1px dashed var(--el-color-danger) !important;
&:hover {
border-color: var(--el-color-primary) !important;
}
}
}
}
:deep(.disabled) {
.el-upload--picture-card,
.el-upload-dragger {
cursor: not-allowed;
background: var(--el-disabled-bg-color) !important;
border: 1px dashed var(--el-border-color-darker);
&:hover {
border-color: var(--el-border-color-darker) !important;
}
}
}
.upload-box {
.no-border {
:deep(.el-upload--picture-card) {
border: none !important;
}
}
:deep(.upload) {
.el-upload-dragger {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 0;
overflow: hidden;
border: 1px dashed var(--el-border-color-darker);
border-radius: v-bind(borderradius);
&:hover {
border: 1px dashed var(--el-color-primary);
}
}
.el-upload-dragger.is-dragover {
background-color: var(--el-color-primary-light-9);
border: 2px dashed var(--el-color-primary) !important;
}
.el-upload-list__item,
.el-upload--picture-card {
width: v-bind(width);
height: v-bind(height);
background-color: transparent;
border-radius: v-bind(borderradius);
}
.upload-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.upload-handle {
position: absolute;
top: 0;
right: 0;
display: flex;
width: 100%;
height: 100%;
cursor: pointer;
background: rgb(0 0 0 / 60%);
opacity: 0;
box-sizing: border-box;
transition: var(--el-transition-duration-fast);
align-items: center;
justify-content: center;
.handle-icon {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 6%;
color: aliceblue;
.el-icon {
margin-bottom: 15%;
font-size: 140%;
}
span {
font-size: 100%;
}
}
}
.el-upload-list__item {
&:hover {
.upload-handle {
opacity: 1;
}
}
}
.upload-empty {
display: flex;
flex-direction: column;
align-items: center;
font-size: 12px;
line-height: 30px;
color: var(--el-color-info);
.el-icon {
font-size: 28px;
color: var(--el-text-color-secondary);<template>
<el-dialog
title="新增图纸"
v-model="dialogVisible"
:close-on-click-modal="false"
@close="handleCancel"
width="500px"
>
<el-form ref="formRef" :model="formData" label-width="80px" :rules="formRules" v-loading="formLoading">
<el-row>
<el-col :span="24">
<el-form-item label="图纸" prop = 'file'>
<UploadImgs
v-model="formData.file"
:width="'120px'"
:height="'120px'"
:show-delete="true"
:show-btn-text="false"
:data = data
@change="handleFileChange"
/>