| 1 |
- import type { UploadRequestOptions, UploadUserFile } from "element-plus";
import { ElLoading, ElMessage } from "element-plus";
import { uploadFileToOss, isUploadUserCancelledError, isUploadApiErrorAlreadyMessaged } from "@/api/upload.js";
export { isUploadUserCancelledError };
export interface BusinessInfoImageUploadResult {
fileUrl: string;
/** 与历史 media_id / image_id 字段兼容;OSS 场景下默认与 fileUrl 一致 */
mediaId: string;
}
let blockingLoadingDepth = 0;
let blockingLoadingInst: ReturnType<typeof ElLoading.service> | null = null;
/** 页面单独 OCR 审核期间:全屏 loading + 禁止操作 */
export async function withBusinessInfoBlockingLoading<T>(
task: () => Promise<T>,
text = "识别中,请稍候..."
): Promise<T> {
if (blockingLoadingDepth === 0) {
blockingLoadingInst = ElLoading.service({
fullscreen: true,
lock: true,
text,
background: "rgba(0, 0, 0, 0.55)"
});
}
blockingLoadingDepth += 1;
try {
return await task();
} finally {
blockingLoadingDepth -= 1;
if (blockingLoadingDepth <= 0 && blockingLoadingInst) {
blockingLoadingInst.close();
blockingLoadingInst = null;
blockingLoadingDepth = 0;
}
}
}
/**
* 商家进件等:OSS STS 直传;默认走 finalize 内容审核
* @param options.skipFinalize 营业执照/身份证等:仅直传,审核由页面 OCR 完成
*/
export async function uploadBusinessInfoImageToOss(
file: File,
options: {
showUploadOverlay?: boolean;
skipFinalize?: boolean;
uploadOverlayTitle?: string;
uploadSuccessMessage?: string | null;
} = {}
): Promise<BusinessInfoImageUploadResult> {
const showOverlay = options.showUploadOverlay !== false;
const skipFinalize = options.skipFinalize === true;
const fileUrl = String(
(await uploadFileToOss(file, "image", {
skipSimpleUploadOverlay: !showOverlay,
skipFinalize,
uploadOverlayTitle: options.uploadOverlayTitle ?? (skipFinalize ? "上传中..." : undefined),
uploadSuccessMessage:
options.uploadSuccessMessage !== undefined ? options.uploadSuccessMessage : skipFinalize ? null : undefined
})) || ""
).trim();
if (!fileUrl) {
throw new Error("上传失败,未返回地址");
}
return { fileUrl, mediaId: fileUrl };
}
/**
* 营业执照 / 身份证等:OSS 直传(不走 finalize)→ 页面 OCR 审核(全屏 loading)
*/
export async function uploadBusinessInfoImageWithPageOcr(
file: File,
runOcr: (file: File) => Promise<void>,
options: { showUploadOverlay?: boolean; ocrLoadingText?: string } = {}
): Promise<BusinessInfoImageUploadResult> {
const result = await uploadBusinessInfoImageToOss(file, {
showUploadOverlay: options.showUploadOverlay !== false,
skipFinalize: true
});
await withBusinessInfoBlockingLoading(
() => runOcr(file),
options.ocrLoadingText ?? "识别中,请稍候..."
);
ElMessage.success("上传成功");
return result;
}
export function filterOutUploadUserFileByUid(
list: UploadUserFile[],
uid?: number
): UploadUserFile[] {
if (uid == null) return list;
return list.filter(f => f.uid !== uid);
}
/** 从 file-list 收集已成功上传的服务器地址(排除 blob 本地预览) */
/** 审核失败 / 上传失败:移除列表项并清空业务字段(营业执照、证件照等) */
export function failBusinessInfoUploadCleanup(handlers: {
fileList: UploadUserFile[];
setFileList: (list: UploadUserFile[]) => void;
uid?: number;
onClear?: () => void;
error?: unknown;
}) {
handlers.setFileList(filterOutUploadUserFileByUid(handlers.fileList, handlers.uid));
handlers.onClear?.();
const err = handlers.error;
if (err && !isUploadUserCancelledError(err) && !isUploadApiErrorAlreadyMessaged(err)) {
const o = err && typeof err === "object" ? (err as Record<string, unknown>) : null;
const msg = String(
(err instanceof Error ? err.message : "") || o?.msg || o?.message || (typeof err === "string" ? err : "") || "上传失败"
).trim();
if (msg) ElMessage.error(msg);
}
}
export function collectSuccessUploadUrls(list: UploadUserFile[]): string[] {
return list
.filter(f => f.status === "success")
.map(f => {
const fu = f as UploadUserFile & { url?: string; response?: { url?: string } };
return String(fu.url ?? fu.response?.url ?? "").trim();
})
.filter(u => u && !/^blob:/i.test(u) && !/^data:/i.test(u));
}
type UploadFailHandler = UploadRequestOptions["onError"];
/**
* el-upload http-request 通用:OSS 上传,失败/取消时移除列表项
*/
export async function runBusinessInfoOssUpload(
options: UploadRequestOptions,
handlers: {
fileList: UploadUserFile[];
setFileList: (list: UploadUserFile[]) => void;
onUploaded: (result: BusinessInfoImageUploadResult, file: File) => void | Promise<void>;
onFail?: () => void;
}
): Promise<void> {
const uploadFileItem = options.file as UploadUserFile;
const uid = uploadFileItem.uid;
const raw = uploadFileItem.raw || uploadFileItem;
const file = raw instanceof File ? raw : null;
if (!file) {
handlers.setFileList(filterOutUploadUserFileByUid(handlers.fileList, uid));
(options.onError as UploadFailHandler)?.(new Error("无效文件") as any);
return;
}
uploadFileItem.status = "uploading";
try {
const result = await uploadBusinessInfoImageToOss(file);
uploadFileItem.status = "success";
uploadFileItem.url = result.fileUrl;
uploadFileItem.response = { media_id: result.mediaId, url: result.fileUrl };
await handlers.onUploaded(result, file);
options.onSuccess(result as any);
} catch (err) {
failBusinessInfoUploadCleanup({
fileList: handlers.fileList,
setFileList: handlers.setFileList,
uid,
onClear: handlers.onFail,
error: err
});
(options.onError as UploadFailHandler)?.(err as any);
}
}
|