|
@@ -23,7 +23,9 @@
|
|
|
<p class="info-line">
|
|
<p class="info-line">
|
|
|
工作台:<span class="info-value">{{ workbench.workStation }}</span>
|
|
工作台:<span class="info-value">{{ workbench.workStation }}</span>
|
|
|
</p>
|
|
</p>
|
|
|
- <p class="info-line hint-text">支持png/jpeg/jpg/webp/heic/heif(heic/heif自动转webp)</p>
|
|
|
|
|
|
|
+ <p class="info-line hint-text">
|
|
|
|
|
+ 支持png/jpeg/jpg/webp/heic/heif(heic/heif自动转webp)
|
|
|
|
|
+ </p>
|
|
|
<van-button
|
|
<van-button
|
|
|
size="mini"
|
|
size="mini"
|
|
|
type="primary"
|
|
type="primary"
|
|
@@ -125,16 +127,31 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="button-group">
|
|
<div class="button-group">
|
|
|
- <van-button class="action-btn" type="warning" plain @click="previewAll">预览全部</van-button>
|
|
|
|
|
- <van-button class="action-btn action-btn--danger" type="danger" plain @click="onReset">重置图片</van-button>
|
|
|
|
|
|
|
+ <van-button
|
|
|
|
|
+ class="action-btn"
|
|
|
|
|
+ type="warning"
|
|
|
|
|
+ plain
|
|
|
|
|
+ @click="previewAll"
|
|
|
|
|
+ >预览全部</van-button
|
|
|
|
|
+ >
|
|
|
|
|
+ <van-button
|
|
|
|
|
+ class="action-btn action-btn--danger"
|
|
|
|
|
+ type="danger"
|
|
|
|
|
+ plain
|
|
|
|
|
+ @click="onReset"
|
|
|
|
|
+ >重置图片</van-button
|
|
|
|
|
+ >
|
|
|
<van-button
|
|
<van-button
|
|
|
class="action-btn"
|
|
class="action-btn"
|
|
|
type="primary"
|
|
type="primary"
|
|
|
plain
|
|
plain
|
|
|
@click="retryAllFailedImages"
|
|
@click="retryAllFailedImages"
|
|
|
:disabled="!hasFailedImages()"
|
|
:disabled="!hasFailedImages()"
|
|
|
- >重传失败图片</van-button>
|
|
|
|
|
- <van-button class="action-btn" type="primary" @click="onSubmit">提交</van-button>
|
|
|
|
|
|
|
+ >重传失败图片
|
|
|
|
|
+ </van-button>
|
|
|
|
|
+ <van-button class="action-btn" type="primary" @click="onSubmit"
|
|
|
|
|
+ >提交</van-button
|
|
|
|
|
+ >
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -153,9 +170,7 @@
|
|
|
type="primary"
|
|
type="primary"
|
|
|
size="small"
|
|
size="small"
|
|
|
@click="onEditImage(previewCurrentIndex)"
|
|
@click="onEditImage(previewCurrentIndex)"
|
|
|
- :disabled="
|
|
|
|
|
- !canEditImage(getPreviewImage(previewCurrentIndex))
|
|
|
|
|
- "
|
|
|
|
|
|
|
+ :disabled="!canEditImage(getPreviewImage(previewCurrentIndex))"
|
|
|
>
|
|
>
|
|
|
编辑
|
|
编辑
|
|
|
</van-button>
|
|
</van-button>
|
|
@@ -168,6 +183,7 @@
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
import { ref, onMounted } from 'vue'
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
+
|
|
|
// Type declarations for image upload handling
|
|
// Type declarations for image upload handling
|
|
|
enum UPLOAD_STATUS {
|
|
enum UPLOAD_STATUS {
|
|
|
PENDING = 'pending',
|
|
PENDING = 'pending',
|
|
@@ -196,6 +212,7 @@ interface Workbench {
|
|
|
interface EditImageExposed {
|
|
interface EditImageExposed {
|
|
|
editImage: (image: UploadImage, type: 'outer' | 'inner') => void
|
|
editImage: (image: UploadImage, type: 'outer' | 'inner') => void
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
import {
|
|
import {
|
|
|
showFailToast,
|
|
showFailToast,
|
|
|
showNotify,
|
|
showNotify,
|
|
@@ -248,8 +265,6 @@ const handlePreviewChange = (index: number): void => {
|
|
|
previewCurrentIndex.value = index
|
|
previewCurrentIndex.value = index
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
function getWorkbench(): void {
|
|
function getWorkbench(): void {
|
|
|
returnedWorkbench()
|
|
returnedWorkbench()
|
|
|
.then((res) => {
|
|
.then((res) => {
|
|
@@ -262,7 +277,9 @@ function getWorkbench(): void {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const beforeReadImage = async (file: File | File[]): Promise<UploadImage[] | false> => {
|
|
|
|
|
|
|
+const beforeReadImage = async (
|
|
|
|
|
+ file: File | File[],
|
|
|
|
|
+): Promise<File[] | false> => {
|
|
|
const files = Array.isArray(file) ? file : [file]
|
|
const files = Array.isArray(file) ? file : [file]
|
|
|
const normalizedFiles: File[] = []
|
|
const normalizedFiles: File[] = []
|
|
|
|
|
|
|
@@ -293,17 +310,59 @@ const beforeReadImage = async (file: File | File[]): Promise<UploadImage[] | fal
|
|
|
normalizedFiles.push(f)
|
|
normalizedFiles.push(f)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 返回增强的图片对象数组,包含状态信息
|
|
|
|
|
- return normalizedFiles.map((f) => ({
|
|
|
|
|
- file: f, // 原始File对象
|
|
|
|
|
|
|
+ // 返回原始 File 数组 给 van-uploader 进行默认处理,
|
|
|
|
|
+ // 我们会在 after-add watcher 中将其转换为 UploadImage 对象。
|
|
|
|
|
+ return normalizedFiles
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 将 van-uploader 添加的文件项规范化为 UploadImage 对象
|
|
|
|
|
+function toUploadImage(item: any): UploadImage {
|
|
|
|
|
+ // Vant 可能直接传入 File,也可能是 { file, url, status }
|
|
|
|
|
+ const fileObj: File = item?.file instanceof File ? item.file : item instanceof File ? item : null
|
|
|
|
|
+ const url = item?.url || (fileObj ? URL.createObjectURL(fileObj) : null)
|
|
|
|
|
+ return {
|
|
|
|
|
+ file: fileObj,
|
|
|
status: UPLOAD_STATUS.PENDING,
|
|
status: UPLOAD_STATUS.PENDING,
|
|
|
- url: null,
|
|
|
|
|
|
|
+ url: url,
|
|
|
error: null,
|
|
error: null,
|
|
|
retryCount: 0,
|
|
retryCount: 0,
|
|
|
- originalFile: f, // 保留用于重传
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ originalFile: fileObj,
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 监听 van-uploader 绑定的数组,自动将新加入的原始文件项转换为 UploadImage
|
|
|
|
|
+import { watch } from 'vue'
|
|
|
|
|
+
|
|
|
|
|
+watch(
|
|
|
|
|
+ outerImages,
|
|
|
|
|
+ (newVal, oldVal) => {
|
|
|
|
|
+ if (!Array.isArray(newVal)) return
|
|
|
|
|
+ // 如果数组项已经是 UploadImage(包含 status 字段),跳过
|
|
|
|
|
+ const needsNormalize = newVal.some((it: any) => !it || !('status' in it))
|
|
|
|
|
+ if (!needsNormalize) return
|
|
|
|
|
+
|
|
|
|
|
+ const normalized = newVal.map((it: any) => {
|
|
|
|
|
+ return 'status' in it ? it : toUploadImage(it)
|
|
|
|
|
+ })
|
|
|
|
|
+ outerImages.value = normalized
|
|
|
|
|
+ },
|
|
|
|
|
+ { deep: true },
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+watch(
|
|
|
|
|
+ innerImages,
|
|
|
|
|
+ (newVal, oldVal) => {
|
|
|
|
|
+ if (!Array.isArray(newVal)) return
|
|
|
|
|
+ const needsNormalize = newVal.some((it: any) => !it || !('status' in it))
|
|
|
|
|
+ if (!needsNormalize) return
|
|
|
|
|
+ const normalized = newVal.map((it: any) => {
|
|
|
|
|
+ return 'status' in it ? it : toUploadImage(it)
|
|
|
|
|
+ })
|
|
|
|
|
+ innerImages.value = normalized
|
|
|
|
|
+ },
|
|
|
|
|
+ { deep: true },
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
// 单张图片上传函数
|
|
// 单张图片上传函数
|
|
|
const uploadSingleImage = async (
|
|
const uploadSingleImage = async (
|
|
|
imageObj: UploadImage,
|
|
imageObj: UploadImage,
|
|
@@ -346,7 +405,7 @@ const uploadCategoryWithStatus = async (
|
|
|
results.push({ success: true, item })
|
|
results.push({ success: true, item })
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
- console.log(item,categoryName)
|
|
|
|
|
|
|
+ console.log(item, categoryName)
|
|
|
const success = await uploadSingleImage(item, categoryName)
|
|
const success = await uploadSingleImage(item, categoryName)
|
|
|
results.push({ success, item })
|
|
results.push({ success, item })
|
|
|
}
|
|
}
|
|
@@ -697,6 +756,8 @@ function getFileNameByMime(fileName: string, dataUrl: string): string {
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
<style scoped lang="sass">
|
|
<style scoped lang="sass">
|
|
|
.van-nav-bar
|
|
.van-nav-bar
|
|
|
.left-btn
|
|
.left-btn
|
|
@@ -836,3 +897,4 @@ function getFileNameByMime(fileName: string, dataUrl: string): string {
|
|
|
.upload-status.pending
|
|
.upload-status.pending
|
|
|
background-color: #969799
|
|
background-color: #969799
|
|
|
</style>
|
|
</style>
|
|
|
|
|
+
|