/** * 工具函数模块 - 通用工具函数 * @module utils */ ;(function() { 'use strict'; /** * 显示消息提示 * @param {string} text - 消息内容 * @param {'success'|'error'|'warning'|'info'} [type='success'] - 消息类型 */ const showMessage = (text, type = 'success') => { if (window.ElementPlus?.ElMessage) { window.ElementPlus.ElMessage({ message: text, type }); } else { console.log(`[${type}] ${text}`); } }; /** * 格式化日期时间 * @param {Date} date - 日期对象 * @returns {string} 格式化后的日期字符串 YYYY-MM-DD HH:mm:ss */ const formatDateTime = (date) => { const pad = (n) => String(n).padStart(2, '0'); return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; }; /** * 获取月份范围 * @param {number} [offsetMonths=-1] - 月份偏移量(负数表示往前推) * @returns {[string, string]} [开始时间, 结束时间] */ const getMonthRange = (offsetMonths = -1) => { const now = new Date(); const base = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0); const start = new Date(base.getFullYear(), base.getMonth() + offsetMonths, 1, 0, 0, 0); const end = new Date(start.getFullYear(), start.getMonth() + 1, 0, 23, 59, 59); return [formatDateTime(start), formatDateTime(end)]; }; /** * 计算对话框宽度(限制在最小和最大值之间) * @param {number} width - 目标宽度 * @param {number} [minWidth=500] - 最小宽度 * @param {number} [maxWidth=1400] - 最大宽度 * @returns {string} 宽度字符串,如 '900px' */ const clampDialogWidth = (width, minWidth = 500, maxWidth = 1400) => { const clamped = Math.max(minWidth, Math.min(maxWidth, width)); return clamped + 'px'; }; /** * 从 localStorage 获取对话框宽度 * @param {string} key - 存储键名 * @param {number} defaultWidth - 默认宽度 * @returns {string} 宽度字符串 */ const getStoredDialogWidth = (key, defaultWidth) => { const stored = localStorage.getItem(key); return stored || (defaultWidth + 'px'); }; /** * 保存对话框宽度到 localStorage * @param {string} key - 存储键名 * @param {string} width - 宽度字符串 */ const saveDialogWidth = (key, width) => { localStorage.setItem(key, width); }; /** * 计算任务进度百分比 * @param {Object} job - 任务对象 * @returns {string} 进度描述 */ const calculateJobProgress = (job) => { const estimate = Number(job.row_estimate || 0); const done = Number(job.total_rows || 0); if (job.status === 'completed') return '100%'; if (job.status === 'failed') return '失败'; if (job.status === 'canceled') return '已取消'; if (job.status === 'queued') return '0%'; if (job.status === 'running') { const effectiveEstimate = estimate > 0 ? estimate : (done > 0 ? done * 2 : 0); if (effectiveEstimate > 0) { const percent = Math.max(0, Math.min(100, Math.floor(done * 100 / effectiveEstimate))); return percent + '%'; } if (done > 0) return `已写${done.toLocaleString()}`; return '评估中'; } const effectiveEstimate = estimate > 0 ? estimate : (done > 0 ? done * 2 : 0); if (effectiveEstimate > 0) { const percent = Math.max(0, Math.min(100, Math.floor(done * 100 / effectiveEstimate))); return percent + '%'; } return '评估中'; }; /** * 解析宽度字符串为数字 * @param {string} widthStr - 宽度字符串,如 '900px' * @param {number} defaultValue - 默认值 * @returns {number} 宽度数值 */ const parseWidth = (widthStr, defaultValue) => { return parseInt(String(widthStr).replace('px', '') || String(defaultValue), 10); }; /** * 深度克隆对象 * @param {Object} obj - 要克隆的对象 * @returns {Object} 克隆后的对象 */ const deepClone = (obj) => { return JSON.parse(JSON.stringify(obj)); }; /** * 安全地获取嵌套属性 * @param {Object} obj - 对象 * @param {string} path - 属性路径,如 'a.b.c' * @param {*} defaultValue - 默认值 * @returns {*} 属性值或默认值 */ const getNestedValue = (obj, path, defaultValue = undefined) => { const keys = path.split('.'); let result = obj; for (const key of keys) { if (result == null) return defaultValue; result = result[key]; } return result !== undefined ? result : defaultValue; }; /** * 防抖函数 * @param {Function} fn - 要防抖的函数 * @param {number} delay - 延迟时间(毫秒) * @returns {Function} 防抖后的函数 */ const debounce = (fn, delay) => { let timer = null; return function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; }; /** * 节流函数 * @param {Function} fn - 要节流的函数 * @param {number} limit - 限制时间(毫秒) * @returns {Function} 节流后的函数 */ const throttle = (fn, limit) => { let inThrottle = false; return function (...args) { if (!inThrottle) { fn.apply(this, args); inThrottle = true; setTimeout(() => (inThrottle = false), limit); } }; }; // 导出模块 window.AppUtils = { showMessage, formatDateTime, getMonthRange, clampDialogWidth, getStoredDialogWidth, saveDialogWidth, calculateJobProgress, parseWidth, deepClone, getNestedValue, debounce, throttle }; })();