|
|
@@ -710,7 +710,7 @@ const formData = reactive({
|
|
|
description: ""
|
|
|
});
|
|
|
|
|
|
-// 同步组件内部文件列表到 formData
|
|
|
+// 同步组件内部文件列表到 formData(避免上传中因过滤 blob 导致列表被清空或重复)
|
|
|
const syncFileListFromComponent = async () => {
|
|
|
if (!backgroundImagesUploadRef.value || !dialogVisible.value) return;
|
|
|
|
|
|
@@ -719,47 +719,43 @@ const syncFileListFromComponent = async () => {
|
|
|
if (componentInstance && componentInstance._fileList) {
|
|
|
const internalFileList = componentInstance._fileList.value || componentInstance._fileList;
|
|
|
if (Array.isArray(internalFileList)) {
|
|
|
+ // 若组件内仍有 blob URL,说明还有文件在上传中,不覆盖 formData,避免清空或重复
|
|
|
+ const hasUploading = internalFileList.some((file: UploadUserFile) => file?.url && String(file.url).startsWith("blob:"));
|
|
|
+ if (hasUploading) return;
|
|
|
+
|
|
|
// 同步内部文件列表到 formData(排除 blob URL,只保留服务器 URL)
|
|
|
const validFiles = internalFileList
|
|
|
.filter((file: UploadUserFile) => {
|
|
|
const hasUrl = file && file.url && typeof file.url === "string" && file.url.trim() !== "";
|
|
|
- // 排除 blob URL,只保留服务器 URL
|
|
|
- const isBlobUrl = hasUrl && file.url.startsWith("blob:");
|
|
|
+ const isBlobUrl = hasUrl && (file.url as string).startsWith("blob:");
|
|
|
return hasUrl && !isBlobUrl;
|
|
|
})
|
|
|
- .map((file: UploadUserFile) => {
|
|
|
- // 确保每个文件对象都有必要的属性
|
|
|
- return {
|
|
|
- ...file,
|
|
|
- url: file.url!.trim(),
|
|
|
- name: file.name || file.url!.split("/").pop() || "image",
|
|
|
- uid: file.uid || `file-${Date.now()}-${Math.random()}`
|
|
|
- };
|
|
|
- });
|
|
|
+ .map((file: UploadUserFile) => ({
|
|
|
+ ...file,
|
|
|
+ url: file.url!.trim(),
|
|
|
+ name: file.name || file.url!.split("/").pop() || "image",
|
|
|
+ uid: file.uid ?? `file-${Date.now()}-${Math.random()}`
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 按 URL 去重,避免同一张图出现多次
|
|
|
+ const seenUrls = new Set<string>();
|
|
|
+ const uniqueFiles = validFiles.filter((f: { url?: string }) => {
|
|
|
+ const u = f.url?.trim();
|
|
|
+ if (!u || seenUrls.has(u)) return false;
|
|
|
+ seenUrls.add(u);
|
|
|
+ return true;
|
|
|
+ });
|
|
|
|
|
|
- // 检查是否需要更新(比较 URL 列表,避免不必要的更新)
|
|
|
- // 只比较非 blob URL 的 URLs
|
|
|
const currentUrls = formData.backgroundImages
|
|
|
- .filter((f: UploadUserFile) => f && f.url && !f.url.startsWith("blob:"))
|
|
|
+ .filter((f: UploadUserFile) => f && f.url && !(f.url as string).startsWith("blob:"))
|
|
|
.map((f: UploadUserFile) => f.url!.trim())
|
|
|
.sort();
|
|
|
- const newUrls = validFiles.map((f: UploadUserFile) => f.url!.trim()).sort();
|
|
|
+ const newUrls = uniqueFiles.map((f: { url?: string }) => (f.url || "").trim()).sort();
|
|
|
|
|
|
- // 未传背景时不以组件内残留数据覆盖:表单为空且组件有项,说明是上次编辑的残留,不覆盖
|
|
|
- if (formData.backgroundImages.length === 0 && validFiles.length > 0) return;
|
|
|
+ if (formData.backgroundImages.length === 0 && uniqueFiles.length > 0) return;
|
|
|
|
|
|
- // 如果不一致,更新 formData(直接使用组件内部的文件列表)
|
|
|
if (JSON.stringify(currentUrls) !== JSON.stringify(newUrls)) {
|
|
|
- // 直接使用组件内部的文件列表,确保数据一致
|
|
|
- formData.backgroundImages = [...validFiles];
|
|
|
-
|
|
|
- console.log("同步文件列表成功,更新数量:", validFiles.length);
|
|
|
- console.log(
|
|
|
- "同步后的文件列表 URLs:",
|
|
|
- formData.backgroundImages.map(f => f.url)
|
|
|
- );
|
|
|
-
|
|
|
- // 触发验证
|
|
|
+ formData.backgroundImages = [...uniqueFiles] as UploadUserFile[];
|
|
|
await nextTick();
|
|
|
if (formRef.value) {
|
|
|
formRef.value.validateField("backgroundImages");
|
|
|
@@ -772,157 +768,11 @@ const syncFileListFromComponent = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 背景图片上传成功回调
|
|
|
-const handleBackgroundImageSuccess = async (url: string) => {
|
|
|
- console.log("=== 背景图片上传成功回调 ===");
|
|
|
- console.log("上传成功的URL:", url);
|
|
|
- console.log("当前 formData.backgroundImages 数量:", formData.backgroundImages.length);
|
|
|
- console.log(
|
|
|
- "当前 formData.backgroundImages URLs:",
|
|
|
- formData.backgroundImages.map(f => f?.url)
|
|
|
- );
|
|
|
-
|
|
|
- // 立即将新上传的 URL 添加到 formData(如果还没有的话)
|
|
|
- const urlExists = formData.backgroundImages.some((file: UploadUserFile) => {
|
|
|
- return file && file.url && file.url.trim() === url.trim();
|
|
|
- });
|
|
|
-
|
|
|
- if (!urlExists) {
|
|
|
- console.log("新上传的 URL 不在 formData 中,立即添加");
|
|
|
- const newFile: UploadUserFile = {
|
|
|
- uid: `upload-${Date.now()}-${Math.random()}`,
|
|
|
- name: url.split("/").pop() || "image.jpg",
|
|
|
- url: url.trim(),
|
|
|
- status: "success" as const,
|
|
|
- response: { fileUrl: url }
|
|
|
- };
|
|
|
- formData.backgroundImages = [...formData.backgroundImages, newFile];
|
|
|
- console.log("添加新文件后的 formData.backgroundImages 数量:", formData.backgroundImages.length);
|
|
|
- console.log(
|
|
|
- "添加新文件后的 formData.backgroundImages URLs:",
|
|
|
- formData.backgroundImages.map(f => f.url)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- // 等待组件更新完成(el-upload 的 onSuccess 可能需要时间)
|
|
|
- await nextTick();
|
|
|
- await nextTick();
|
|
|
+// 背景图片上传成功回调(仅做校验触发,不重复添加文件,避免与 UploadImgs 的 v-model/on-change 重复导致多传)
|
|
|
+const handleBackgroundImageSuccess = async (_url: string) => {
|
|
|
await nextTick();
|
|
|
-
|
|
|
- // 使用轮询方式,确保组件内部已完全更新
|
|
|
- let retryCount = 0;
|
|
|
- const maxRetries = 15;
|
|
|
-
|
|
|
- while (retryCount < maxRetries) {
|
|
|
- if (backgroundImagesUploadRef.value) {
|
|
|
- try {
|
|
|
- const componentInstance = backgroundImagesUploadRef.value;
|
|
|
- if (componentInstance && componentInstance._fileList) {
|
|
|
- const internalFileList = componentInstance._fileList.value || componentInstance._fileList;
|
|
|
- if (Array.isArray(internalFileList)) {
|
|
|
- // 获取所有有效文件(排除 blob URL)
|
|
|
- const validFiles = internalFileList
|
|
|
- .filter((file: UploadUserFile) => {
|
|
|
- const hasUrl = file && file.url && typeof file.url === "string" && file.url.trim() !== "";
|
|
|
- const isBlobUrl = hasUrl && file.url.startsWith("blob:");
|
|
|
- return hasUrl && !isBlobUrl;
|
|
|
- })
|
|
|
- .map((file: UploadUserFile) => ({
|
|
|
- ...file,
|
|
|
- url: file.url!.trim(),
|
|
|
- name: file.name || file.url!.split("/").pop() || "image",
|
|
|
- uid: file.uid || `file-${Date.now()}-${Math.random()}`
|
|
|
- }));
|
|
|
-
|
|
|
- // 检查新上传的 URL 是否已经在组件内部的文件列表中
|
|
|
- const hasNewUrl = validFiles.some((file: UploadUserFile) => {
|
|
|
- return file && file.url && file.url.trim() === url.trim();
|
|
|
- });
|
|
|
-
|
|
|
- console.log(`轮询第 ${retryCount + 1} 次 - 组件内部文件数量: ${validFiles.length}, 是否包含新URL: ${hasNewUrl}`);
|
|
|
- console.log(
|
|
|
- `轮询第 ${retryCount + 1} 次 - 组件内部文件 URLs:`,
|
|
|
- validFiles.map(f => f.url)
|
|
|
- );
|
|
|
-
|
|
|
- if (hasNewUrl || retryCount >= maxRetries - 1) {
|
|
|
- // 合并 formData 和组件内部的文件,确保包含所有图片
|
|
|
- const allUrls = new Set<string>();
|
|
|
- const mergedFiles: UploadUserFile[] = [];
|
|
|
-
|
|
|
- // 先添加 formData 中的文件
|
|
|
- formData.backgroundImages.forEach((file: UploadUserFile) => {
|
|
|
- if (file && file.url && !file.url.startsWith("blob:")) {
|
|
|
- const fileUrl = file.url.trim();
|
|
|
- if (!allUrls.has(fileUrl)) {
|
|
|
- allUrls.add(fileUrl);
|
|
|
- mergedFiles.push({
|
|
|
- ...file,
|
|
|
- url: fileUrl
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 再添加组件内部的文件(确保新上传的文件被包含)
|
|
|
- validFiles.forEach((file: UploadUserFile) => {
|
|
|
- const fileUrl = file.url;
|
|
|
- if (fileUrl && !fileUrl.startsWith("blob:") && !allUrls.has(fileUrl)) {
|
|
|
- allUrls.add(fileUrl);
|
|
|
- mergedFiles.push(file);
|
|
|
- console.log("从组件内部添加文件:", fileUrl);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 更新 formData,确保包含新上传的图片
|
|
|
- formData.backgroundImages = [...mergedFiles];
|
|
|
-
|
|
|
- console.log("上传成功后同步的文件列表,数量:", mergedFiles.length);
|
|
|
- console.log(
|
|
|
- "上传成功后同步的文件列表 URLs:",
|
|
|
- mergedFiles.map(f => f.url)
|
|
|
- );
|
|
|
-
|
|
|
- // 触发验证
|
|
|
- await nextTick();
|
|
|
- if (formRef.value) {
|
|
|
- formRef.value.validateField("backgroundImages");
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error("上传成功后同步文件列表失败:", error);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 如果还没找到新上传的 URL,等待一下再重试
|
|
|
- retryCount++;
|
|
|
- if (retryCount < maxRetries) {
|
|
|
- await new Promise(resolve => setTimeout(resolve, 150)); // 等待 150ms
|
|
|
- await nextTick();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (retryCount >= maxRetries) {
|
|
|
- console.warn("⚠️ 上传成功后,等待组件更新超时,但已同步当前文件列表");
|
|
|
- // 即使超时,也确保新上传的 URL 在 formData 中
|
|
|
- const urlExists = formData.backgroundImages.some((file: UploadUserFile) => {
|
|
|
- return file && file.url && file.url.trim() === url.trim();
|
|
|
- });
|
|
|
- if (!urlExists) {
|
|
|
- const newFile: UploadUserFile = {
|
|
|
- uid: `upload-${Date.now()}-${Math.random()}`,
|
|
|
- name: url.split("/").pop() || "image.jpg",
|
|
|
- url: url.trim(),
|
|
|
- status: "success" as const,
|
|
|
- response: { fileUrl: url }
|
|
|
- };
|
|
|
- formData.backgroundImages = [...formData.backgroundImages, newFile];
|
|
|
- console.log("超时后强制添加新文件到 formData");
|
|
|
- }
|
|
|
+ if (formRef.value) {
|
|
|
+ formRef.value.validateField("backgroundImages");
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -991,14 +841,17 @@ watch(
|
|
|
lastFileListUrls = currentUrls;
|
|
|
lastFileListLength = currentLength;
|
|
|
|
|
|
- // 直接更新 formData,确保计数正确
|
|
|
- const mappedFiles = validFiles.map((file: UploadUserFile) => ({
|
|
|
- ...file,
|
|
|
- url: file.url!.trim()
|
|
|
- }));
|
|
|
- formData.backgroundImages = [...mappedFiles];
|
|
|
-
|
|
|
- console.log("定时检查检测到文件列表变化,数量:", mappedFiles.length);
|
|
|
+ // 按 URL 去重后更新 formData,避免同一张图出现多次
|
|
|
+ const seen = new Set<string>();
|
|
|
+ const mappedFiles = validFiles
|
|
|
+ .map((file: UploadUserFile) => ({ ...file, url: (file.url || "").trim() }))
|
|
|
+ .filter((f: UploadUserFile) => {
|
|
|
+ const u = f?.url;
|
|
|
+ if (!u || seen.has(u)) return false;
|
|
|
+ seen.add(u);
|
|
|
+ return true;
|
|
|
+ });
|
|
|
+ formData.backgroundImages = [...mappedFiles] as UploadUserFile[];
|
|
|
|
|
|
// 触发验证
|
|
|
await nextTick();
|