import { defineStore } from "pinia"; import { ref } from "vue"; import { ElMessage } from "element-plus"; /** 上传占 90%,内容审核占 10% */ const UPLOAD_PERCENT_MAX = 90; const AUDIT_PERCENT_MAX = 100; const AUDIT_ROTATE_MESSAGES = [ "检测违规内容中...", "审核内容合规性...", "筛查敏感信息中...", "识别不良信息中...", "智能风控核验中..." ]; const AUDIT_TITLE_DONE = "上传完毕,处理中..."; const AUDIT_TITLE_SUCCESS = "上传成功"; let uploadCreepTimer: ReturnType | null = null; let auditMessageTimer: ReturnType | null = null; let auditProgressTimer: ReturnType | null = null; let auditMessageIndex = 0; let activeController: AbortController | null = null; function clearUploadCreepTimer() { if (uploadCreepTimer) { clearInterval(uploadCreepTimer); uploadCreepTimer = null; } } function clearAuditTimers() { if (auditMessageTimer) { clearInterval(auditMessageTimer); auditMessageTimer = null; } if (auditProgressTimer) { clearInterval(auditProgressTimer); auditProgressTimer = null; } } function clearAllTimers() { clearUploadCreepTimer(); clearAuditTimers(); } function clampPercent(n: number) { return Math.min(AUDIT_PERCENT_MAX, Math.max(0, Math.round(n))); } export const useSimpleUploadOverlayStore = defineStore("simple-upload-overlay", () => { const show = ref(false); const percent = ref(0); const title = ref("上传中"); const cancelText = ref("取消上传"); function beginUpload(opts?: { title?: string }) { activeController?.abort(new DOMException("已开始新的上传", "AbortError")); clearAllTimers(); activeController = new AbortController(); title.value = opts?.title ?? "上传中"; percent.value = 0; show.value = true; auditMessageIndex = 0; /** OSS 未回调 progress 时缓慢推进,上限留到 85% 等待真实进度 */ uploadCreepTimer = setInterval(() => { if (percent.value < 85) { percent.value = Math.min(85, percent.value + 1); } }, 220); return activeController.signal; } /** 单文件 OSS 上传进度 ratio 0~1 → 0~90% */ function setUploadProgress(ratio: number, opts?: { skipAudit?: boolean }) { const r = Math.min(1, Math.max(0, Number(ratio) || 0)); const max = opts?.skipAudit ? AUDIT_PERCENT_MAX : UPLOAD_PERCENT_MAX; const next = clampPercent(r * max); if (next > percent.value) { percent.value = next; } if (next >= UPLOAD_PERCENT_MAX - 1) { clearUploadCreepTimer(); } } /** 多文件:第 index 个文件、共 total 个,单文件内 ratio 0~1 */ function setMultiFileUploadProgress(fileIndex: number, totalFiles: number, ratio: number) { const total = Math.max(1, totalFiles); const idx = Math.min(Math.max(0, fileIndex), total - 1); const r = Math.min(1, Math.max(0, Number(ratio) || 0)); const base = (idx / total) * UPLOAD_PERCENT_MAX; const span = UPLOAD_PERCENT_MAX / total; const next = clampPercent(base + r * span); if (next > percent.value) { percent.value = next; } if (r >= 1 && idx === total - 1) { clearUploadCreepTimer(); percent.value = UPLOAD_PERCENT_MAX; } } /** 多文件审核阶段进度 */ function setMultiFileAuditProgress(fileIndex: number, totalFiles: number, ratio: number) { const total = Math.max(1, totalFiles); const idx = Math.min(Math.max(0, fileIndex), total - 1); const r = Math.min(1, Math.max(0, Number(ratio) || 0)); const base = UPLOAD_PERCENT_MAX + (idx / total) * (AUDIT_PERCENT_MAX - UPLOAD_PERCENT_MAX); const span = (AUDIT_PERCENT_MAX - UPLOAD_PERCENT_MAX) / total; percent.value = clampPercent(base + r * span); } function startAuditMessageRotation() { clearAuditTimers(); auditMessageIndex = 0; title.value = AUDIT_TITLE_DONE; auditMessageTimer = setInterval(() => { title.value = AUDIT_ROTATE_MESSAGES[auditMessageIndex % AUDIT_ROTATE_MESSAGES.length]; auditMessageIndex += 1; }, 1200); let auditP = percent.value < UPLOAD_PERCENT_MAX ? UPLOAD_PERCENT_MAX : percent.value; percent.value = UPLOAD_PERCENT_MAX; auditProgressTimer = setInterval(() => { if (auditP < 99) { auditP = Math.min(99, auditP + 0.35); percent.value = clampPercent(auditP); } }, 280); } /** 进入审核阶段:固定 90%,先显示「上传完毕,处理中...」再轮播审核文案 */ function beginAuditPhase() { clearUploadCreepTimer(); percent.value = UPLOAD_PERCENT_MAX; title.value = AUDIT_TITLE_DONE; setTimeout(() => { if (!show.value) return; startAuditMessageRotation(); }, 600); } function beginAuditPhaseForBatch(fileIndex: number, totalFiles: number) { clearUploadCreepTimer(); const total = Math.max(1, totalFiles); const idx = Math.min(Math.max(0, fileIndex), total - 1); const auditBase = UPLOAD_PERCENT_MAX + (idx / total) * (AUDIT_PERCENT_MAX - UPLOAD_PERCENT_MAX); percent.value = clampPercent(Math.max(UPLOAD_PERCENT_MAX, auditBase)); title.value = AUDIT_TITLE_DONE; setTimeout(() => { if (!show.value) return; startAuditMessageRotation(); }, 600); } function completeSuccess() { clearAllTimers(); percent.value = AUDIT_PERCENT_MAX; title.value = AUDIT_TITLE_SUCCESS; } /** @deprecated 请用 completeSuccess */ function bumpToComplete() { completeSuccess(); } function dismiss() { clearAllTimers(); show.value = false; percent.value = 0; title.value = "上传中"; activeController = null; } function userCancel() { activeController?.abort(new DOMException("用户已取消上传", "AbortError")); dismiss(); ElMessage.info("取消上传"); } function getActiveAbortSignal(): AbortSignal | undefined { return activeController?.signal; } return { show, percent, title, cancelText, beginUpload, setUploadProgress, setMultiFileUploadProgress, setMultiFileAuditProgress, beginAuditPhase, beginAuditPhaseForBatch, completeSuccess, bumpToComplete, dismiss, userCancel, getActiveAbortSignal }; });