operation-system/src/components/myUploadImage.vue

387 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="component-upload-image">
<div class="upload-tip">图片比例1:1建议尺寸为800 * 800px支持{{fileType.join(",")}}等格式大小 {{ fileSize }} MB以内</div>
<el-upload multiple :disabled="disabled" :action="uploadImgUrl" list-type="picture-card"
:on-success="handleUploadSuccess" :before-upload="handleBeforeUpload" :data="data" :limit="limit"
:on-error="handleUploadError" :on-exceed="handleExceed" ref="imageUpload" :before-remove="handleDelete"
:show-file-list="true" :headers="headers" :file-list="fileList" :on-preview="handlePictureCardPreview"
:class="{ hide: fileList.length >= limit }">
<el-icon size="30px" color="red">
<Plus />
</el-icon>
<div class="el-upload__text">上传图片</div>
<!-- 自定义文件列表项 -->
<template #file="{ file }">
<div class="custom-upload-item">
<img :src="file.url" class="el-upload-list__item-thumbnail" />
<!-- 主图角标只在第一张图片显示 -->
<div v-if="isShowSubscript">
<span v-if="fileList[0]?.uid === file.uid" class="main-image-badge">主图</span>
</div>
<!-- 自定义删除按钮带文字 -->
<span class="custom-delete-btn" @click.stop="handleDelete(file)">
<el-icon>
<Delete />
</el-icon>
<span>删除</span>
</span>
</div>
</template>
</el-upload>
<div class="upload-hint">至多可上传{{ limit }}张图片</div>
<!-- 上传提示 -->
<!-- <div class="el-upload__tip" v-if="showTip && !disabled">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
</template>
的文件
</div> -->
<el-dialog v-model="dialogVisible" title="预览" width="800px" append-to-body>
<img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
</el-dialog>
</div>
</template>
<script setup>
import { getToken } from "@/utils/auth"
import { isExternal } from "@/utils/validate"
import Sortable from 'sortablejs'
const props = defineProps({
modelValue: [String, Object, Array],
// 上传接口地址
action: {
type: String,
default: "/uploadApis/upload"
},
// 上传携带的参数
data: {
type: Object
},
// 图片数量限制
limit: {
type: Number,
default: 5
},
isShowSubscript: {
type: Boolean,
default: true
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 2
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["png", "jpg", "jpeg"]
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
},
// 禁用组件(仅查看图片)
disabled: {
type: Boolean,
default: false
},
// 拖动排序
drag: {
type: Boolean,
default: true
}
})
const { proxy } = getCurrentInstance()
const emit = defineEmits()
const number = ref(0)
const uploadList = ref([])
const dialogImageUrl = ref("")
const dialogVisible = ref(false)
const baseUrl = import.meta.env.VITE_APP_BASE_API
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) // 上传的图片服务器地址
const headers = ref({ Authorization: "Bearer " + getToken() })
const fileList = ref([])
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)
)
watch(() => props.modelValue, val => {
if (val) {
// 首先将值转为数组
const list = Array.isArray(val) ? val : props.modelValue.split(",")
// 然后将数组转为对象数组
fileList.value = list.map(item => {
if (typeof item === "string") {
if (item.indexOf(baseUrl) === -1 && !isExternal(item)) {
item = { name: baseUrl + item, url: baseUrl + item }
} else {
item = { name: item, url: item }
}
}
return item
})
} else {
fileList.value = []
return []
}
}, { deep: true, immediate: true })
// 上传前loading加载
function handleBeforeUpload(file) {
let isImg = false
if (props.fileType.length) {
let fileExtension = ""
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1)
}
isImg = props.fileType.some(type => {
if (file.type.indexOf(type) > -1) return true
if (fileExtension && fileExtension.indexOf(type) > -1) return true
return false
})
} else {
isImg = file.type.indexOf("image") > -1
}
if (!isImg) {
proxy.$modal.msgError(`文件格式不正确,请上传${props.fileType.join("/")}图片格式文件!`)
return false
}
if (file.name.includes(',')) {
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!')
return false
}
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize
if (!isLt) {
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`)
return false
}
}
proxy.$modal.loading("正在上传图片,请稍候...")
number.value++
}
// 文件个数超出
function handleExceed() {
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
}
// 上传成功回调
function handleUploadSuccess(res, file) {
if (res.code === 200) {
uploadList.value.push({ name: res.data.name, url: res.data.url })
uploadList.value.forEach(item => {
item.url = "http://gov-cloud.oss-cn-chengdu.aliyuncs.com/" + item.url;
})
uploadedSuccessfully()
} else {
number.value--
proxy.$modal.closeLoading()
proxy.$modal.msgError(res.msg)
proxy.$refs.imageUpload.handleRemove(file)
uploadedSuccessfully()
}
}
// 删除图片
function handleDelete(file) {
const findex = fileList.value.map(f => f.name).indexOf(file.name)
if (findex > -1 && uploadList.value.length === number.value) {
fileList.value.splice(findex, 1)
emit("update:modelValue", listToString(fileList.value))
return false
}
}
// 上传结束处理
function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length > 0) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
uploadList.value = []
number.value = 0
emit("update:modelValue", listToString(fileList.value))
proxy.$modal.closeLoading()
}
}
// 上传失败
function handleUploadError() {
proxy.$modal.msgError("上传图片失败")
proxy.$modal.closeLoading()
}
// 预览
function handlePictureCardPreview(file) {
dialogImageUrl.value = file.url
dialogVisible.value = true
}
// 对象转成指定字符串分隔
function listToString(list, separator) {
let strs = ""
separator = separator || ","
for (let i in list) {
if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
strs += list[i].url.replace(baseUrl, "") + separator
}
}
return strs != "" ? strs.substr(0, strs.length - 1) : ""
}
// 初始化拖拽排序
onMounted(() => {
if (props.drag && !props.disabled) {
nextTick(() => {
const element = proxy.$refs.imageUpload?.$el?.querySelector('.el-upload-list')
Sortable.create(element, {
onEnd: (evt) => {
const movedItem = fileList.value.splice(evt.oldIndex, 1)[0]
fileList.value.splice(evt.newIndex, 0, movedItem)
emit('update:modelValue', listToString(fileList.value))
}
})
})
}
})
</script>
<style scoped lang="scss">
// .el-upload--picture-card 控制加号部分
:deep(.hide .el-upload--picture-card) {
display: none;
}
// 上传图标加号颜色
.el-upload--picture-card>i {
// color: #25BF82;
}
:deep() {
.el-upload--picture-card:hover,
.el-upload--picture-card:focus {
border-color: #cdd0d6 !important;
color: #333 !important
}
}
:deep(.el-upload.el-upload--picture-card.is-disabled) {
display: none !important;
}
// 上传图片样式-开始
.upload-tip {
color: #999;
font-size: 12px;
margin-bottom: 10px;
}
.upload-hint {
color: #999;
font-size: 12px;
margin-top: 10px;
}
.el-upload__text {
font-size: 12px;
line-height: 30px;
color: #666;
}
:deep(.el-upload--picture-card) {
width: 120px;
height: 120px;
line-height: 100px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
:deep(.el-upload-dragger) {
width: 120px;
height: 120px;
padding: 10px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
/* 完全隐藏上传按钮区域 */
.hide-upload :deep(.el-upload--picture-card) {
display: none !important;
}
/* 隐藏默认的查看按钮 */
:deep(.el-upload-list__item-actions .el-upload-list__item-preview) {
display: none !important;
}
/* 自定义删除按钮(带文字) */
.custom-delete-btn {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: white;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
cursor: pointer;
}
.custom-delete-btn:hover {
background: rgba(0, 0, 0, 0.7);
}
.custom-delete-btn .el-icon {
margin-right: 4px;
font-size: 14px;
}
/* 调整上传卡片样式 */
:deep(.el-upload--picture-card) {
width: 120px;
height: 120px;
}
:deep(.el-upload-list__item),
:deep(.el-upload-list--picture-card .el-upload-list__item) {
width: 120px !important;
height: 120px !important;
}
/* 主图角标样式 */
.main-image-badge {
position: absolute;
top: 0;
right: 0;
background: #25BF82;
color: white;
padding: 0 6px;
font-size: 10px;
border-radius: 0 4px 0 8px;
/* 右上和左下圆角 */
z-index: 2;
line-height: 20px;
}
</style>