|
@@ -1,6 +1,7 @@
|
|
|
import { useUserStore } from "@/stores/modules/user";
|
|
import { useUserStore } from "@/stores/modules/user";
|
|
|
import { ElMessage } from "element-plus";
|
|
import { ElMessage } from "element-plus";
|
|
|
import { AI_UPLOAD_FILES_PUBLIC_BASE, BASE_AI_URL } from "@/utils/config";
|
|
import { AI_UPLOAD_FILES_PUBLIC_BASE, BASE_AI_URL } from "@/utils/config";
|
|
|
|
|
+import { withSimpleUploadOverlay } from "@/utils/withSimpleUploadOverlay";
|
|
|
|
|
|
|
|
/** 非 TUS 简单上传接口路径(与 Apifox「上传文件-非TUS」一致;开发环境经 VITE_PROXY /ai-upload 转发) */
|
|
/** 非 TUS 简单上传接口路径(与 Apifox「上传文件-非TUS」一致;开发环境经 VITE_PROXY /ai-upload 转发) */
|
|
|
const SIMPLE_UPLOAD_PATH = "/upload/simple";
|
|
const SIMPLE_UPLOAD_PATH = "/upload/simple";
|
|
@@ -311,9 +312,10 @@ function assertSimpleUploadBusinessOk(parsed) {
|
|
|
/**
|
|
/**
|
|
|
* POST multipart:字段 file;可选 filename(此处不传则服务端用原名)
|
|
* POST multipart:字段 file;可选 filename(此处不传则服务端用原名)
|
|
|
* @param {File} file
|
|
* @param {File} file
|
|
|
|
|
+ * @param {{ signal?: AbortSignal }} [fetchOptions]
|
|
|
* @returns {Promise<string>} 文件访问 URL
|
|
* @returns {Promise<string>} 文件访问 URL
|
|
|
*/
|
|
*/
|
|
|
-async function postFileToSimpleUpload(file) {
|
|
|
|
|
|
|
+async function postFileToSimpleUpload(file, fetchOptions = {}) {
|
|
|
const base = String(BASE_AI_URL || "").replace(/\/$/, "");
|
|
const base = String(BASE_AI_URL || "").replace(/\/$/, "");
|
|
|
if (!base) {
|
|
if (!base) {
|
|
|
throw new Error("未配置上传服务地址(VITE_AI_UPLOAD_BASE 或默认 /ai-upload)");
|
|
throw new Error("未配置上传服务地址(VITE_AI_UPLOAD_BASE 或默认 /ai-upload)");
|
|
@@ -329,12 +331,15 @@ async function postFileToSimpleUpload(file) {
|
|
|
headers.Authorization = token;
|
|
headers.Authorization = token;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const { signal } = fetchOptions;
|
|
|
|
|
+
|
|
|
const res = await fetch(`${base}${SIMPLE_UPLOAD_PATH}`, {
|
|
const res = await fetch(`${base}${SIMPLE_UPLOAD_PATH}`, {
|
|
|
method: "POST",
|
|
method: "POST",
|
|
|
headers,
|
|
headers,
|
|
|
/** 不带跨域 Cookie,减轻上传服务 CORS 要求(鉴权仅用 Authorization) */
|
|
/** 不带跨域 Cookie,减轻上传服务 CORS 要求(鉴权仅用 Authorization) */
|
|
|
credentials: "omit",
|
|
credentials: "omit",
|
|
|
- body: formData
|
|
|
|
|
|
|
+ body: formData,
|
|
|
|
|
+ signal: signal ?? undefined
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const rawText = await res.text();
|
|
const rawText = await res.text();
|
|
@@ -406,33 +411,49 @@ async function postFileToSimpleUpload(file) {
|
|
|
* 上传文件:图片与视频均走同一接口 POST /upload/simple,formData 键 file。
|
|
* 上传文件:图片与视频均走同一接口 POST /upload/simple,formData 键 file。
|
|
|
* @param {File | File[] | FileList} files 浏览器文件对象;支持单个 File、数组或 FileList
|
|
* @param {File | File[] | FileList} files 浏览器文件对象;支持单个 File、数组或 FileList
|
|
|
* @param {string} [_fileType] 保留参数,兼容旧调用(当前不参与分支)
|
|
* @param {string} [_fileType] 保留参数,兼容旧调用(当前不参与分支)
|
|
|
- * @param {{ showLoading?: boolean }} [options] showLoading 为 true 时用 ElMessage 提示上传中(非阻塞)
|
|
|
|
|
|
|
+ * @param {{ showLoading?: boolean; skipSimpleUploadOverlay?: boolean }} [options]
|
|
|
|
|
+ * showLoading:在未使用全局上传弹层时,用 ElMessage 提示上传中
|
|
|
|
|
+ * skipSimpleUploadOverlay:为 true 时不展示 PopupLoading(不弹「上传成功」)
|
|
|
* @returns {Promise<string[]>} 上传成功后的文件 URL 列表
|
|
* @returns {Promise<string[]>} 上传成功后的文件 URL 列表
|
|
|
*/
|
|
*/
|
|
|
export async function uploadFilesToOss(files, _fileType, options = {}) {
|
|
export async function uploadFilesToOss(files, _fileType, options = {}) {
|
|
|
- const { showLoading = false } = options;
|
|
|
|
|
|
|
+ const { showLoading = false, skipSimpleUploadOverlay = false } = options;
|
|
|
const fileArr = normalizeFiles(files);
|
|
const fileArr = normalizeFiles(files);
|
|
|
if (fileArr.length === 0) {
|
|
if (fileArr.length === 0) {
|
|
|
throw new Error("请选择要上传的文件");
|
|
throw new Error("请选择要上传的文件");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let closeLoading = () => {};
|
|
let closeLoading = () => {};
|
|
|
- if (showLoading) {
|
|
|
|
|
|
|
+ if (showLoading && skipSimpleUploadOverlay) {
|
|
|
const loading = ElMessage({ message: "上传中...", type: "info", duration: 0, showClose: false });
|
|
const loading = ElMessage({ message: "上传中...", type: "info", duration: 0, showClose: false });
|
|
|
closeLoading = () => loading.close();
|
|
closeLoading = () => loading.close();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- const uploadedUrls = [];
|
|
|
|
|
- for (const file of fileArr) {
|
|
|
|
|
- const url = await postFileToSimpleUpload(file);
|
|
|
|
|
- uploadedUrls.push(url);
|
|
|
|
|
|
|
+ const runUpload = async signal => {
|
|
|
|
|
+ const uploadedUrls = [];
|
|
|
|
|
+ for (const file of fileArr) {
|
|
|
|
|
+ const url = await postFileToSimpleUpload(file, signal ? { signal } : {});
|
|
|
|
|
+ uploadedUrls.push(url);
|
|
|
|
|
+ }
|
|
|
|
|
+ return uploadedUrls;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let uploadedUrls;
|
|
|
|
|
+ if (skipSimpleUploadOverlay) {
|
|
|
|
|
+ uploadedUrls = await runUpload(null);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uploadedUrls = await withSimpleUploadOverlay(signal => runUpload(signal));
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
closeLoading();
|
|
closeLoading();
|
|
|
return uploadedUrls;
|
|
return uploadedUrls;
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
closeLoading();
|
|
closeLoading();
|
|
|
console.error("上传失败", e);
|
|
console.error("上传失败", e);
|
|
|
|
|
+ if (e?.name === "AbortError") {
|
|
|
|
|
+ throw e;
|
|
|
|
|
+ }
|
|
|
const msg = e?.message || "上传失败";
|
|
const msg = e?.message || "上传失败";
|
|
|
ElMessage.error(msg);
|
|
ElMessage.error(msg);
|
|
|
throw e;
|
|
throw e;
|