| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <script lang="ts" setup name="ImageUpload">
- import type { UploadProps } from 'element-plus'
- import { ElMessage } from 'element-plus'
- const props = defineProps({
- action: {
- type: String,
- required: true,
- },
- headers: {
- type: Object,
- default: () => {},
- },
- data: {
- type: Object,
- default: () => {},
- },
- name: {
- type: String,
- default: 'file',
- },
- url: {
- type: String,
- default: '',
- },
- size: {
- type: Number,
- default: 2,
- },
- width: {
- type: Number,
- default: 150,
- },
- height: {
- type: Number,
- default: 150,
- },
- placeholder: {
- type: String,
- default: '',
- },
- notip: {
- type: Boolean,
- default: false,
- },
- ext: {
- type: Array,
- default: () => ['jpg', 'png', 'gif', 'bmp'],
- },
- })
- const emit = defineEmits(['update:url', 'onSuccess'])
- const uploadData = ref({
- imageViewerVisible: false,
- progress: {
- preview: '',
- percent: 0,
- },
- })
- // 预览
- function preview() {
- uploadData.value.imageViewerVisible = true
- }
- // 关闭预览
- function previewClose() {
- uploadData.value.imageViewerVisible = false
- }
- // 移除
- function remove() {
- emit('update:url', '')
- }
- const beforeUpload: UploadProps['beforeUpload'] = (file) => {
- const fileName = file.name.split('.')
- const fileExt = fileName.at(-1)
- const isTypeOk = props.ext.includes(fileExt)
- const isSizeOk = file.size / 1024 / 1024 < props.size
- if (!isTypeOk) {
- ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
- }
- if (!isSizeOk) {
- ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
- }
- if (isTypeOk && isSizeOk) {
- uploadData.value.progress.preview = URL.createObjectURL(file)
- }
- return isTypeOk && isSizeOk
- }
- const onProgress: UploadProps['onProgress'] = (file) => {
- uploadData.value.progress.percent = ~~file.percent
- }
- const onSuccess: UploadProps['onSuccess'] = (res) => {
- uploadData.value.progress.preview = ''
- uploadData.value.progress.percent = 0
- emit('onSuccess', res)
- }
- </script>
- <template>
- <div class="upload-container">
- <el-upload
- :show-file-list="false"
- :headers="headers"
- :action="action"
- :data="data"
- :name="name"
- :before-upload="beforeUpload"
- :on-progress="onProgress"
- :on-success="onSuccess"
- drag
- class="image-upload"
- >
- <el-image v-if="url === ''" :src="url === '' ? placeholder : url" :style="`width:${width}px;height:${height}px;`" fit="fill">
- <template #error>
- <div class="image-slot" :style="`width:${width}px;height:${height}px;`">
- <el-icon>
- <svg-icon name="i-ep:plus" />
- </el-icon>
- </div>
- </template>
- </el-image>
- <div v-else class="image">
- <el-image :src="url" :style="`width:${width}px;height:${height}px;`" fit="fill" />
- <div class="mask">
- <div class="actions">
- <span title="预览" @click.stop="preview">
- <el-icon>
- <svg-icon name="i-ep:zoom-in" />
- </el-icon>
- </span>
- <span title="移除" @click.stop="remove">
- <el-icon>
- <svg-icon name="i-ep:delete" />
- </el-icon>
- </span>
- </div>
- </div>
- </div>
- <div v-show="url === '' && uploadData.progress.percent" class="progress" :style="`width:${width}px;height:${height}px;`">
- <el-image :src="uploadData.progress.preview" :style="`width:${width}px;height:${height}px;`" fit="fill" />
- <el-progress type="circle" :width="Math.min(width, height) * 0.8" :percentage="uploadData.progress.percent" />
- </div>
- </el-upload>
- <div v-if="!notip" class="el-upload__tip">
- <div style="display: inline-block;">
- <el-alert :title="`上传图片支持 ${ext.join(' / ')} 格式,且图片大小不超过 ${size}MB,建议图片尺寸为 ${width}*${height}`" type="info" show-icon :closable="false" />
- </div>
- </div>
- <el-image-viewer v-if="uploadData.imageViewerVisible" :url-list="[url]" teleported @close="previewClose" />
- </div>
- </template>
- <style lang="scss" scoped>
- .upload-container {
- line-height: initial;
- }
- .el-image {
- display: block;
- }
- .image {
- position: relative;
- border-radius: 6px;
- overflow: hidden;
- .mask {
- opacity: 0;
- position: absolute;
- top: 0;
- width: 100%;
- height: 100%;
- background-color: var(--el-overlay-color-lighter);
- transition: opacity 0.3s;
- .actions {
- width: 100px;
- height: 100px;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- @include position-center(xy);
- span {
- width: 50%;
- text-align: center;
- cursor: pointer;
- color: var(--el-color-white);
- transition: color 0.1s, transform 0.1s;
- &:hover {
- transform: scale(1.5);
- }
- .el-icon {
- font-size: 24px;
- }
- }
- }
- }
- &:hover .mask {
- opacity: 1;
- }
- }
- .image-upload {
- display: inline-block;
- vertical-align: top;
- }
- :deep(.el-upload) {
- .el-upload-dragger {
- display: inline-block;
- padding: 0;
- &.is-dragover {
- border-width: 1px;
- }
- .image-slot {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 100%;
- color: var(--el-text-color-placeholder);
- background-color: transparent;
- i {
- font-size: 30px;
- }
- }
- .progress {
- position: absolute;
- top: 0;
- &::after {
- content: "";
- position: absolute;
- width: 100%;
- height: 100%;
- left: 0;
- top: 0;
- background-color: var(--el-overlay-color-lighter);
- }
- .el-progress {
- z-index: 1;
- @include position-center(xy);
- .el-progress__text {
- color: var(--el-text-color-placeholder);
- }
- }
- }
- }
- }
- </style>
|