MarketingSystemDataExportTool/web/modules/utils.js

193 lines
5.3 KiB
JavaScript

/**
* 工具函数模块 - 通用工具函数
* @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
};
})();