用户反馈:销假时选完图片后无转圈、无预览、无报错,服务端也收不到 upload-policy 请求(访问日志可证)。 原实现用游离的 input(createElement 后未挂 DOM)且 accept 限定 image/png,image/jpeg,image/jpg;在部分第三方内核(如 iPhone 夸克) 下,游离 input 不触发 onchange,iOS 相册的 HEIC 等格式也会被判为 不匹配,导致 input.files 为空 → 命中 `if (!file) return` 静默返回。 抽出 pickImageFiles() 统一修复: - accept 改 image/*(与首页上传一致); - input 挂到 DOM,用完移除; - 监听 cancel 区分「用户取消」(静默)与「选了但读不到文件」(可见提示); - 销假图片、请假附件两处共用,并修正 uploading 标志在提前 return 之后才置位的问题。
This commit is contained in:
@@ -1040,28 +1040,56 @@ async function uploadImageToObs(file: File): Promise<string | null> {
|
||||
return previewJson.data.previewUrl
|
||||
}
|
||||
|
||||
function handleImageUpload() {
|
||||
if (uploadingImage.value) return
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.accept = 'image/png,image/jpeg,image/jpg'
|
||||
input.onchange = async () => {
|
||||
const file = input.files?.[0]
|
||||
if (!file) return
|
||||
|
||||
uploadingImage.value = true
|
||||
try {
|
||||
const previewUrl = await uploadImageToObs(file)
|
||||
if (!previewUrl) return
|
||||
form.value.imageUrl = previewUrl
|
||||
message.success('图片上传成功')
|
||||
} catch (e: any) {
|
||||
message.error('图片上传失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
uploadingImage.value = false
|
||||
// 打开系统文件选择器。两点与原实现不同,用于修复部分浏览器「选完图片无任何反应」:
|
||||
// 1. input 必须挂到 DOM:部分第三方内核(如 iPhone 夸克)对游离 input 不触发 onchange;
|
||||
// 2. accept 用 image/*:旧写法限定 png/jpeg,iOS 相册的 HEIC 等格式会被判为不匹配,
|
||||
// 选完拿不到 file(表现为无转圈、无预览、无报错、也无任何请求)。
|
||||
// 返回 picked=false 表示用户取消(静默返回);picked=true 但 files 为空表示
|
||||
// 选择器返回了但不含可用文件,调用方应给出可见提示,避免再次静默失败。
|
||||
function pickImageFiles(multiple = false): Promise<{ picked: boolean; files: File[] }> {
|
||||
return new Promise((resolve) => {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.accept = 'image/*'
|
||||
input.multiple = multiple
|
||||
input.style.position = 'fixed'
|
||||
input.style.left = '-9999px'
|
||||
let settled = false
|
||||
const finish = (picked: boolean, files: File[]) => {
|
||||
if (settled) return
|
||||
settled = true
|
||||
input.remove()
|
||||
resolve({ picked, files })
|
||||
}
|
||||
input.onchange = () => finish(true, Array.from(input.files || []))
|
||||
// 用户取消:iOS 16+/现代内核会触发 cancel,提前清理避免 input 泄漏
|
||||
input.addEventListener('cancel', () => finish(false, []))
|
||||
document.body.appendChild(input)
|
||||
input.click()
|
||||
})
|
||||
}
|
||||
|
||||
async function handleImageUpload() {
|
||||
if (uploadingImage.value) return
|
||||
const { picked, files } = await pickImageFiles()
|
||||
if (!picked) return // 用户取消,静默
|
||||
const file = files[0]
|
||||
if (!file) {
|
||||
message.warning('未能读取所选图片,请重试或换一张图片')
|
||||
return
|
||||
}
|
||||
|
||||
uploadingImage.value = true
|
||||
try {
|
||||
const previewUrl = await uploadImageToObs(file)
|
||||
if (!previewUrl) return
|
||||
form.value.imageUrl = previewUrl
|
||||
message.success('图片上传成功')
|
||||
} catch (e: any) {
|
||||
message.error('图片上传失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
uploadingImage.value = false
|
||||
}
|
||||
input.click()
|
||||
}
|
||||
|
||||
function removeImage() {
|
||||
@@ -1446,42 +1474,39 @@ async function loadApplyApprovers() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleApplyImageUpload() {
|
||||
async function handleApplyImageUpload() {
|
||||
if (uploadingApplyImage.value) return
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.accept = 'image/png,image/jpeg,image/jpg'
|
||||
input.multiple = true
|
||||
input.onchange = async () => {
|
||||
const files = Array.from(input.files || [])
|
||||
if (files.length === 0) return
|
||||
const remain = applyAttachLimit.value - applyForm.value.imageUrls.length
|
||||
if (remain <= 0) {
|
||||
message.warning(`附件最多 ${applyAttachLimit.value} 张`)
|
||||
return
|
||||
}
|
||||
// 超出上限的多选文件直接丢弃(避免静默只传部分却无提示)
|
||||
const picked = files.slice(0, remain)
|
||||
if (files.length > remain) {
|
||||
message.warning(`附件最多 ${applyAttachLimit.value} 张,本次仅上传前 ${picked.length} 张`)
|
||||
}
|
||||
uploadingApplyImage.value = true
|
||||
try {
|
||||
let uploaded = 0
|
||||
for (const file of picked) {
|
||||
const previewUrl = await uploadImageToObs(file)
|
||||
if (!previewUrl) continue
|
||||
applyForm.value.imageUrls = [...applyForm.value.imageUrls, previewUrl]
|
||||
uploaded++
|
||||
}
|
||||
if (uploaded > 0) message.success('图片上传成功')
|
||||
} catch (e: any) {
|
||||
message.error('图片上传失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
uploadingApplyImage.value = false
|
||||
}
|
||||
const { picked, files } = await pickImageFiles(true)
|
||||
if (!picked) return // 用户取消,静默
|
||||
if (files.length === 0) {
|
||||
message.warning('未能读取所选图片,请重试或换一张图片')
|
||||
return
|
||||
}
|
||||
const remain = applyAttachLimit.value - applyForm.value.imageUrls.length
|
||||
if (remain <= 0) {
|
||||
message.warning(`附件最多 ${applyAttachLimit.value} 张`)
|
||||
return
|
||||
}
|
||||
// 超出上限的多选文件直接丢弃(避免静默只传部分却无提示)
|
||||
const pickedFiles = files.slice(0, remain)
|
||||
if (files.length > remain) {
|
||||
message.warning(`附件最多 ${applyAttachLimit.value} 张,本次仅上传前 ${pickedFiles.length} 张`)
|
||||
}
|
||||
uploadingApplyImage.value = true
|
||||
try {
|
||||
let uploaded = 0
|
||||
for (const file of pickedFiles) {
|
||||
const previewUrl = await uploadImageToObs(file)
|
||||
if (!previewUrl) continue
|
||||
applyForm.value.imageUrls = [...applyForm.value.imageUrls, previewUrl]
|
||||
uploaded++
|
||||
}
|
||||
if (uploaded > 0) message.success('图片上传成功')
|
||||
} catch (e: any) {
|
||||
message.error('图片上传失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
uploadingApplyImage.value = false
|
||||
}
|
||||
input.click()
|
||||
}
|
||||
|
||||
function removeApplyImage(idx: number) {
|
||||
|
||||
Reference in New Issue
Block a user