|
|
|
|
@ -84,7 +84,7 @@ const emit = defineEmits(['update:modelValue'])
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
|
|
|
|
fileType: propTypes.array.def(['png', 'jpg', 'jpeg', 'doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
|
|
|
|
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
|
|
|
|
fileSize: propTypes.number.def(100), // 大小限制(MB)
|
|
|
|
|
limit: propTypes.number.def(5), // 数量限制
|
|
|
|
|
autoUpload: propTypes.bool.def(true), // 自动上传
|
|
|
|
|
drag: propTypes.bool.def(false), // 拖拽上传
|
|
|
|
|
@ -97,6 +97,7 @@ const uploadRef = ref<UploadInstance>()
|
|
|
|
|
const uploadList = ref<UploadUserFile[]>([])
|
|
|
|
|
const fileList = ref<UploadUserFile[]>([])
|
|
|
|
|
const uploadNumber = ref<number>(0)
|
|
|
|
|
const nameMap = ref<Record<string, string>>({})
|
|
|
|
|
|
|
|
|
|
const { uploadUrl, httpRequest } = useUpload()
|
|
|
|
|
|
|
|
|
|
@ -130,14 +131,41 @@ const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
|
|
|
|
// const handleFileChange = (uploadFile: UploadFile): void => {
|
|
|
|
|
// uploadRef.value.data.path = uploadFile.name
|
|
|
|
|
// }
|
|
|
|
|
// 文件上传成功
|
|
|
|
|
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
|
|
|
|
const getUrlFromRes = (res: any): string => {
|
|
|
|
|
if (!res) return ''
|
|
|
|
|
if (typeof res === 'string') return res
|
|
|
|
|
if (typeof res.data === 'string') return res.data
|
|
|
|
|
const data = res.data || res
|
|
|
|
|
if (typeof data === 'string') return data
|
|
|
|
|
if (data.fileUrl) return data.fileUrl
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getNameFromRes = (res: any, url: string): string => {
|
|
|
|
|
const data = res && res.data ? res.data : res
|
|
|
|
|
if (data && typeof data === 'object' && data.fileName) return data.fileName
|
|
|
|
|
if (!url) return ''
|
|
|
|
|
const idx = url.lastIndexOf('/')
|
|
|
|
|
return idx !== -1 ? url.substring(idx + 1) : url
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleFileSuccess: UploadProps['onSuccess'] = (res: any, uploadFile): void => {
|
|
|
|
|
const url = getUrlFromRes(res)
|
|
|
|
|
if (!url) {
|
|
|
|
|
message.error('上传返回数据异常')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const name = getNameFromRes(res, url)
|
|
|
|
|
if (url && name) {
|
|
|
|
|
nameMap.value[url] = name
|
|
|
|
|
}
|
|
|
|
|
message.success('上传成功')
|
|
|
|
|
// 删除自身
|
|
|
|
|
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) {
|
|
|
|
|
const index = fileList.value.findIndex((item) => item.uid === uploadFile.uid)
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
fileList.value.splice(index, 1)
|
|
|
|
|
}
|
|
|
|
|
uploadList.value.push({ name, url })
|
|
|
|
|
if (uploadList.value.length === uploadNumber.value) {
|
|
|
|
|
fileList.value.push(...uploadList.value)
|
|
|
|
|
uploadList.value = []
|
|
|
|
|
uploadNumber.value = 0
|
|
|
|
|
@ -156,7 +184,10 @@ const excelUploadError: UploadProps['onError'] = (): void => {
|
|
|
|
|
const handleRemove = (file: UploadFile) => {
|
|
|
|
|
const index = fileList.value.map((f) => f.name).indexOf(file.name)
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
fileList.value.splice(index, 1)
|
|
|
|
|
const removed = fileList.value.splice(index, 1)[0]
|
|
|
|
|
if (removed && removed.url && nameMap.value[removed.url]) {
|
|
|
|
|
delete nameMap.value[removed.url]
|
|
|
|
|
}
|
|
|
|
|
emitUpdateModelValue()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -164,6 +195,98 @@ const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
|
|
|
|
console.log(uploadFile)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getDisplayNameByUrl = (url: string): string => {
|
|
|
|
|
const cached = nameMap.value[url]
|
|
|
|
|
if (cached) return cached
|
|
|
|
|
const idx = url.lastIndexOf('/')
|
|
|
|
|
return idx !== -1 ? url.substring(idx + 1) : url
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parseModelValueToInfos = (val: string | string[]): { fileName: string; fileUrl: string }[] => {
|
|
|
|
|
const result: { fileName: string; fileUrl: string }[] = []
|
|
|
|
|
|
|
|
|
|
const addFromUrl = (url: string) => {
|
|
|
|
|
const trimmed = url.trim()
|
|
|
|
|
if (!trimmed) return
|
|
|
|
|
const idx = trimmed.lastIndexOf('/')
|
|
|
|
|
const name = idx !== -1 ? trimmed.substring(idx + 1) : trimmed
|
|
|
|
|
result.push({ fileName: name, fileUrl: trimmed })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleString = (text: string) => {
|
|
|
|
|
const trimmed = text.trim()
|
|
|
|
|
if (!trimmed) return
|
|
|
|
|
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(trimmed)
|
|
|
|
|
if (Array.isArray(parsed)) {
|
|
|
|
|
parsed.forEach((item) => {
|
|
|
|
|
if (item && typeof item === 'object' && item.fileUrl) {
|
|
|
|
|
const u = String(item.fileUrl)
|
|
|
|
|
const n = item.fileName ? String(item.fileName) : undefined
|
|
|
|
|
const idx = u.lastIndexOf('/')
|
|
|
|
|
const name = n || (idx !== -1 ? u.substring(idx + 1) : u)
|
|
|
|
|
result.push({ fileName: name, fileUrl: u })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (parsed && typeof parsed === 'object' && parsed.fileUrl) {
|
|
|
|
|
const u = String(parsed.fileUrl)
|
|
|
|
|
const n = parsed.fileName ? String(parsed.fileName) : undefined
|
|
|
|
|
const idx = u.lastIndexOf('/')
|
|
|
|
|
const name = n || (idx !== -1 ? u.substring(idx + 1) : u)
|
|
|
|
|
result.push({ fileName: name, fileUrl: u })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
trimmed
|
|
|
|
|
.split(',')
|
|
|
|
|
.map((s) => s.trim())
|
|
|
|
|
.filter((s) => !!s)
|
|
|
|
|
.forEach((u) => addFromUrl(u))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleAny = (v: any) => {
|
|
|
|
|
if (!v) return
|
|
|
|
|
if (typeof v === 'string') {
|
|
|
|
|
handleString(v)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (Array.isArray(v)) {
|
|
|
|
|
v.forEach((item) => handleAny(item))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (typeof v === 'object' && v.fileUrl) {
|
|
|
|
|
const u = String(v.fileUrl)
|
|
|
|
|
const n = v.fileName ? String(v.fileName) : undefined
|
|
|
|
|
const idx = u.lastIndexOf('/')
|
|
|
|
|
const name = n || (idx !== -1 ? u.substring(idx + 1) : u)
|
|
|
|
|
result.push({ fileName: name, fileUrl: u })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleAny(val as any)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const buildInfosFromFileList = (): { fileName: string; fileUrl: string }[] => {
|
|
|
|
|
const infos: { fileName: string; fileUrl: string }[] = []
|
|
|
|
|
fileList.value.forEach((file) => {
|
|
|
|
|
if (!file.url) return
|
|
|
|
|
const url = file.url as string
|
|
|
|
|
const cached = nameMap.value[url]
|
|
|
|
|
let name = cached || file.name || ''
|
|
|
|
|
if (!name) {
|
|
|
|
|
const idx = url.lastIndexOf('/')
|
|
|
|
|
name = idx !== -1 ? url.substring(idx + 1) : url
|
|
|
|
|
}
|
|
|
|
|
infos.push({ fileName: String(name), fileUrl: url })
|
|
|
|
|
})
|
|
|
|
|
return infos
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 监听模型绑定值变动
|
|
|
|
|
watch(
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
@ -173,28 +296,31 @@ watch(
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileList.value = [] // 保障数据为空
|
|
|
|
|
// 情况1:字符串
|
|
|
|
|
if (isString(val)) {
|
|
|
|
|
fileList.value.push(
|
|
|
|
|
...val.split(',').map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// 情况2:数组
|
|
|
|
|
fileList.value = []
|
|
|
|
|
nameMap.value = {}
|
|
|
|
|
const infos = parseModelValueToInfos(val)
|
|
|
|
|
if (!infos.length) return
|
|
|
|
|
fileList.value.push(
|
|
|
|
|
...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
|
|
|
|
...infos.map((item) => {
|
|
|
|
|
nameMap.value[item.fileUrl] = item.fileName
|
|
|
|
|
return { name: item.fileName, url: item.fileUrl }
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true, deep: true }
|
|
|
|
|
)
|
|
|
|
|
// 发送文件链接列表更新
|
|
|
|
|
const emitUpdateModelValue = () => {
|
|
|
|
|
// 情况1:数组结果
|
|
|
|
|
let result: string | string[] = fileList.value.map((file) => file.url!)
|
|
|
|
|
// 情况2:逗号分隔的字符串
|
|
|
|
|
const infos = buildInfosFromFileList()
|
|
|
|
|
let result: string | string[]
|
|
|
|
|
if (props.limit === 1 || isString(props.modelValue)) {
|
|
|
|
|
result = result.join(',')
|
|
|
|
|
if (!infos.length) {
|
|
|
|
|
result = ''
|
|
|
|
|
} else {
|
|
|
|
|
result = JSON.stringify(infos[0])
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
result = JSON.stringify(infos)
|
|
|
|
|
}
|
|
|
|
|
emit('update:modelValue', result)
|
|
|
|
|
}
|
|
|
|
|
|