|
|
@@ -2,7 +2,9 @@
|
|
|
const props = withDefaults(
|
|
|
defineProps<{
|
|
|
modelValue: string | any[]
|
|
|
+ prefixUrl?: string
|
|
|
uploadType?: "image" | "file"
|
|
|
+ uploadUrl?: string
|
|
|
limit?: number // 图片数量限制
|
|
|
fileSize?: number // 大小限制(MB)
|
|
|
fileType?: string[] // 文件类型, 例如['png', 'jpg', 'jpeg']
|
|
|
@@ -10,27 +12,38 @@ const props = withDefaults(
|
|
|
}>(),
|
|
|
{
|
|
|
uploadType: "image",
|
|
|
+ prefixUrl: "/oss/preview/",
|
|
|
limit: 5,
|
|
|
fileSize: 5,
|
|
|
fileType: () => ["png", "jpg", "jpeg"],
|
|
|
isShowTip: true
|
|
|
}
|
|
|
)
|
|
|
-const emits = defineEmits<(e: "update:modelValue", v: any) => void>()
|
|
|
+const emits = defineEmits<{
|
|
|
+ (e: "update:modelValue", v: any): void
|
|
|
+ (e: "uploadSuccess", v: any, v2: any): void
|
|
|
+ (e: "delete", v: any): void
|
|
|
+}>()
|
|
|
const imageUploadRef = ref()
|
|
|
const number = ref(0)
|
|
|
const uploadList = ref<any[]>([])
|
|
|
-const dialogImageUrl = ref("")
|
|
|
-const dialogVisible = ref(false)
|
|
|
+const showViewer = ref(false)
|
|
|
+const viewerInitialIndex = ref(0)
|
|
|
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
|
|
-const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload") // 上传的图片服务器地址
|
|
|
+const uploadImgUrl = ref(
|
|
|
+ import.meta.env.VITE_APP_BASE_API + (props.uploadUrl ? props.uploadUrl : "/oss/upload/common")
|
|
|
+) // 上传的图片服务器地址
|
|
|
const headers = ref({ Authorization: "Bearer " + getToken() })
|
|
|
const fileList = ref<any[]>([])
|
|
|
const isImage = computed(() => {
|
|
|
return props.uploadType == "image"
|
|
|
})
|
|
|
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize))
|
|
|
-
|
|
|
+const imageUrls = computed(() => {
|
|
|
+ return fileList.value.map((v) => {
|
|
|
+ return v.url
|
|
|
+ })
|
|
|
+})
|
|
|
// 上传前校检格式和大小
|
|
|
function handleBeforeUpload(file: any) {
|
|
|
let flag = false
|
|
|
@@ -76,8 +89,16 @@ function handleUploadError() {
|
|
|
|
|
|
// 上传成功回调
|
|
|
function handleUploadSuccess(res: any, file: any) {
|
|
|
- if (res.code === 200) {
|
|
|
- uploadList.value.push({ name: res.fileName, url: res.fileName })
|
|
|
+ console.log("RES", res, file)
|
|
|
+ if (res.code === 200 && res.data.url) {
|
|
|
+ let url = `${baseUrl}/${props.prefixUrl ? props.prefixUrl : ""}/${res.data.url}`
|
|
|
+ url = url
|
|
|
+ .replace("https://", "$$$$")
|
|
|
+ .replace("http://", "@@@@")
|
|
|
+ .replace(/\/\//g, "/")
|
|
|
+ .replace("$$$$", "https://")
|
|
|
+ .replace("@@@@", "http://")
|
|
|
+ uploadList.value.push({ name: res.data.url, url })
|
|
|
uploadedSuccessfully()
|
|
|
} else {
|
|
|
number.value--
|
|
|
@@ -86,13 +107,14 @@ function handleUploadSuccess(res: any, file: any) {
|
|
|
imageUploadRef.value.handleRemove(file)
|
|
|
uploadedSuccessfully()
|
|
|
}
|
|
|
+ emits("uploadSuccess", res, file)
|
|
|
}
|
|
|
|
|
|
// 删除图片
|
|
|
function handleDelete(file: any) {
|
|
|
- const findex = fileList.value.map((f) => f.name).indexOf(file.name)
|
|
|
- if (findex > -1 && uploadList.value.length === number.value) {
|
|
|
- fileList.value.splice(findex, 1)
|
|
|
+ const fileIndex = fileList.value.map((f) => f.name).indexOf(file.name)
|
|
|
+ if (fileIndex > -1 && uploadList.value.length === number.value) {
|
|
|
+ fileList.value.splice(fileIndex, 1)
|
|
|
emits("update:modelValue", listToString(fileList.value))
|
|
|
return false
|
|
|
}
|
|
|
@@ -100,8 +122,9 @@ function handleDelete(file: any) {
|
|
|
}
|
|
|
// 删除文件
|
|
|
function handleDeleteFile(index: number) {
|
|
|
- fileList.value.splice(index, 1)
|
|
|
+ const file = fileList.value.splice(index, 1)
|
|
|
emits("update:modelValue", listToString(fileList.value))
|
|
|
+ emits("delete", file)
|
|
|
}
|
|
|
|
|
|
// 上传结束处理
|
|
|
@@ -117,8 +140,10 @@ function uploadedSuccessfully() {
|
|
|
|
|
|
// 预览
|
|
|
function handlePictureCardPreview(file: any) {
|
|
|
- dialogImageUrl.value = file.url
|
|
|
- dialogVisible.value = true
|
|
|
+ //dialogImageUrl.value = file.url
|
|
|
+ console.log("---", file)
|
|
|
+ viewerInitialIndex.value = file.index //fileList.value.map((f) => f.url).indexOf(file.url)
|
|
|
+ showViewer.value = true
|
|
|
}
|
|
|
|
|
|
// 获取文件名称
|
|
|
@@ -132,30 +157,41 @@ function getFileName(name: string) {
|
|
|
|
|
|
// 对象转成指定字符串分隔
|
|
|
function listToString(list: any[], separator?: string) {
|
|
|
- let strs = ""
|
|
|
+ let str = ""
|
|
|
separator = separator ?? ","
|
|
|
list.forEach((v) => {
|
|
|
if (undefined !== v.url && v.url.indexOf("blob:") !== 0) {
|
|
|
- strs += v.url.replace(baseUrl, "") + separator
|
|
|
+ str += v.name + separator
|
|
|
}
|
|
|
})
|
|
|
- return strs != "" ? strs.substring(0, strs.length - 1) : ""
|
|
|
+ return str != "" ? str.substring(0, str.length - 1) : ""
|
|
|
}
|
|
|
|
|
|
watch(
|
|
|
() => props.modelValue,
|
|
|
(val) => {
|
|
|
if (val) {
|
|
|
- let temp = 1
|
|
|
+ let temp = 0
|
|
|
// 首先将值转为数组
|
|
|
const list = Array.isArray(val) ? val : (props.modelValue as string)?.split(",")
|
|
|
// 然后将数组转为对象数组
|
|
|
fileList.value = list.map((item) => {
|
|
|
if (typeof item === "string") {
|
|
|
if (!item.includes(baseUrl)) {
|
|
|
- item = { name: baseUrl + item, url: baseUrl + item }
|
|
|
+ let url = `${baseUrl}/${props.prefixUrl ? props.prefixUrl : ""}/${item}`
|
|
|
+ url = url
|
|
|
+ .replace("https://", "$$$$")
|
|
|
+ .replace("http://", "@@@@")
|
|
|
+ .replace(/\/\//g, "/")
|
|
|
+ .replace("$$$$", "https://")
|
|
|
+ .replace("@@@@", "http://")
|
|
|
+ item = {
|
|
|
+ name: item,
|
|
|
+ url,
|
|
|
+ index: temp
|
|
|
+ }
|
|
|
} else {
|
|
|
- item = { name: item, url: item }
|
|
|
+ item = { name: item, url: item, index: temp }
|
|
|
}
|
|
|
item.uid = item.uid || new Date().getTime() + temp++
|
|
|
}
|
|
|
@@ -193,12 +229,19 @@ onMounted(init)
|
|
|
:headers="headers"
|
|
|
:on-preview="isImage ? handlePictureCardPreview : undefined"
|
|
|
:class="{ hide: fileList.length >= limit, 'upload-file-uploader': !isImage }">
|
|
|
- <el-icon v-if="isImage" class="avatar-uploader-icon"><plus /></el-icon>
|
|
|
+ <el-icon v-if="isImage" class="avatar-uploader-icon">
|
|
|
+ <VbIcon icon-name="plus-square"></VbIcon>
|
|
|
+ </el-icon>
|
|
|
<el-button v-else type="primary">选取文件</el-button>
|
|
|
</el-upload>
|
|
|
<!-- 上传提示 -->
|
|
|
<div class="el-upload__tip" v-if="showTip">
|
|
|
- 请上传
|
|
|
+ <template v-if="limit">
|
|
|
+ 最多可上传
|
|
|
+ <b style="color: #f56c6c">{{ limit }}</b>
|
|
|
+ {{ isImage ? "张" : "个" }}
|
|
|
+ </template>
|
|
|
+ <span v-else>请上传</span>
|
|
|
<template v-if="fileSize">
|
|
|
大小不超过
|
|
|
<b style="color: #f56c6c">{{ fileSize }}MB</b>
|
|
|
@@ -207,15 +250,17 @@ onMounted(init)
|
|
|
格式为
|
|
|
<b style="color: #f56c6c">{{ fileType.join("/") }}</b>
|
|
|
</template>
|
|
|
- 的文件
|
|
|
+ 的{{ isImage ? "图片" : "文件" }}。
|
|
|
</div>
|
|
|
-
|
|
|
- <el-dialog v-if="isImage" v-model="dialogVisible" title="预览" width="800px" append-to-body>
|
|
|
- <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
|
|
|
- </el-dialog>
|
|
|
+ <el-image-viewer
|
|
|
+ v-if="isImage && showViewer"
|
|
|
+ :url-list="imageUrls"
|
|
|
+ :initial-index="viewerInitialIndex"
|
|
|
+ :teleported="true"
|
|
|
+ @close="() => (showViewer = false)" />
|
|
|
<!-- 文件列表 -->
|
|
|
<transition-group
|
|
|
- v-else
|
|
|
+ v-if="!isImage"
|
|
|
class="upload-file-list el-upload-list el-upload-list--text"
|
|
|
name="el-fade-in-linear"
|
|
|
tag="ul">
|