|
|
@@ -1,5 +1,22 @@
|
|
|
<template>
|
|
|
<div class="image-uploader">
|
|
|
+ <!-- 持久化隐藏 input,避免移动端动态创建 input 重复点击失效 -->
|
|
|
+ <input
|
|
|
+ ref="cameraInput"
|
|
|
+ type="file"
|
|
|
+ accept="image/*"
|
|
|
+ capture="environment"
|
|
|
+ style="display: none"
|
|
|
+ @change="onFileChange"
|
|
|
+ />
|
|
|
+ <input
|
|
|
+ ref="albumInput"
|
|
|
+ type="file"
|
|
|
+ accept="image/*"
|
|
|
+ style="display: none"
|
|
|
+ @change="onFileChange"
|
|
|
+ />
|
|
|
+
|
|
|
<div class="upload-grid">
|
|
|
<div
|
|
|
v-for="(img, i) in images"
|
|
|
@@ -35,8 +52,8 @@
|
|
|
<script setup lang="ts">
|
|
|
import { ref, computed } from 'vue'
|
|
|
import { showToast } from 'vant'
|
|
|
-import { takePhoto, pickFromAlbum } from '@/utils/camera'
|
|
|
import { uploadImage } from '@/api/upload'
|
|
|
+import { compressImage } from '@/utils/camera'
|
|
|
|
|
|
const props = withDefaults(defineProps<{
|
|
|
modelValue: string[]
|
|
|
@@ -47,6 +64,10 @@ const emit = defineEmits<{ 'update:modelValue': [v: string[]] }>()
|
|
|
|
|
|
const showAction = ref(false)
|
|
|
const uploading = ref(false)
|
|
|
+const cameraInput = ref<HTMLInputElement | null>(null)
|
|
|
+const albumInput = ref<HTMLInputElement | null>(null)
|
|
|
+// 记录本次选择的模式,在 onFileChange 中区分
|
|
|
+let currentMode: 'camera' | 'album' = 'album'
|
|
|
|
|
|
const actions = [
|
|
|
{ name: 'camera', description: '拍摄照片' },
|
|
|
@@ -61,18 +82,40 @@ function removeImage(i: number) {
|
|
|
emit('update:modelValue', arr)
|
|
|
}
|
|
|
|
|
|
-async function onSelect(action: { name: string }) {
|
|
|
+function onSelect(action: { name: string }) {
|
|
|
showAction.value = false
|
|
|
if (uploading.value) return
|
|
|
- uploading.value = true
|
|
|
|
|
|
- try {
|
|
|
- const files = action.name === 'camera'
|
|
|
- ? [await takePhoto()]
|
|
|
- : await pickFromAlbum(props.max - props.modelValue.length > 1)
|
|
|
+ currentMode = action.name as 'camera' | 'album'
|
|
|
|
|
|
+ if (action.name === 'camera') {
|
|
|
+ cameraInput.value?.click()
|
|
|
+ } else {
|
|
|
+ // 设置多选
|
|
|
+ const input = albumInput.value
|
|
|
+ if (input) {
|
|
|
+ input.multiple = props.max - props.modelValue.length > 1
|
|
|
+ input.click()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function onFileChange(e: Event) {
|
|
|
+ const input = e.target as HTMLInputElement
|
|
|
+ const files = input.files
|
|
|
+ if (!files?.length) {
|
|
|
+ // 用户取消,重置 input 以便下次复用
|
|
|
+ input.value = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ uploading.value = true
|
|
|
+ try {
|
|
|
showToast('上传中...')
|
|
|
- const urls = await Promise.all(files.map(uploadImage))
|
|
|
+ const compressed = await Promise.all(
|
|
|
+ Array.from(files).map((f) => compressImage(f))
|
|
|
+ )
|
|
|
+ const urls = await Promise.all(compressed.map(uploadImage))
|
|
|
emit('update:modelValue', [...props.modelValue, ...urls])
|
|
|
} catch (err: any) {
|
|
|
if (err.message !== '未选择图片') {
|
|
|
@@ -80,6 +123,8 @@ async function onSelect(action: { name: string }) {
|
|
|
}
|
|
|
} finally {
|
|
|
uploading.value = false
|
|
|
+ // 重置 input,否则重复选同一文件不会触发 change
|
|
|
+ input.value = ''
|
|
|
}
|
|
|
}
|
|
|
</script>
|