193 lines
5.3 KiB
JavaScript
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
|
|
};
|
|
|
|
})();
|