import { BigNumber } from "bignumber.js"; /** * Check if an element has a class * @param {HTMLElement} ele * @param {string} cls * @returns {boolean} */ export function hasClass(ele: HTMLElement, cls: string) { return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)")); } /** * Add class to element * @param {HTMLElement} ele * @param {string} cls */ export function addClass(ele: HTMLElement, cls: string) { if (!hasClass(ele, cls)) ele.className += " " + cls; } /** * Remove class from element * @param {HTMLElement} ele * @param {string} cls */ export function removeClass(ele: HTMLElement, cls: string) { if (hasClass(ele, cls)) { const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)"); ele.className = ele.className.replace(reg, " "); } } /** * 判断是否是外部链接 * * @param {string} path * @returns {Boolean} */ export function isExternal(path: string) { const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); return isExternal; } /** * 格式化增长率,保留两位小数 ,并且去掉末尾的0 取绝对值 * * @param growthRate * @returns */ export function formatGrowthRate(growthRate: number) { if (growthRate === 0) { return "-"; } const formattedRate = Math.abs(growthRate * 100) .toFixed(2) .replace(/\.?0+$/, ""); return formattedRate + "%"; } /** * Parse the time to string * @param {(Object|string|number)} time * @param {string} cFormat * @returns {string} */ export function parseTime(time: string | number | Date | null, cFormat: string | undefined) { if (arguments.length === 0) { return null; } const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}"; let date; if (typeof time === "undefined" || time === null || time === "null") { return ""; } else if (typeof time === "object") { date = time; } else { if (typeof time === "string" && /^[0-9]+$/.test(time)) { time = parseInt(time); } if (typeof time === "number" && time.toString().length === 10) { time = time * 1000; } date = new Date(time); } const formatObj = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() }; const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key: keyof typeof formatObj): string => { let value = formatObj[key]; // Note: getDay() returns 0 on Sunday if (key === "a") { return ["日", "一", "二", "三", "四", "五", "六"][value]; } if (result.length > 0 && value < 10) { value = Number("0" + value.toString()); } return value.toString() || "0"; }); return time_str; } // 下载文件 export function downloadFile(obj: BlobPart, name: string, suffix: string, useUnix = true) { const url = window.URL.createObjectURL(new Blob([obj])); const link = document.createElement("a"); link.style.display = "none"; link.href = url; const newFilename = useUnix ? (parseTime(new Date(), undefined) + "-") : '' + name.trim() const fileName = newFilename + "." + suffix; link.setAttribute("download", fileName); document.body.appendChild(link); link.click(); document.body.removeChild(link); } /** * 判断主店同步是否启用 */ export function isSyncStatus() { let userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || '{}')) if (userInfo.value.isHeadShop == 0 && userInfo.value.isEnableProdSync == 1 && userInfo.value.isEnableVipSync == 1 && userInfo.value.isEnableConsSync == 1) { return true } else { return false } } /** * 判断是否有某权限 */ export function hasPermission(params: any) { let $PermissionObj = JSON.parse(localStorage.getItem("permission") || '[]') const obj = $PermissionObj.find((v: any) => v == params || v == params) if (obj) { return obj } return false } /** * 过滤输入,只允许数字和最多两位小数 * @param {string} value - 输入框当前值 * @param {boolean} isIntegerOnly - 是否只允许正整数(无小数点),开启时最小值为1 * @returns {string} 过滤后的合法值 */ export function filterNumberInput(value, isIntegerOnly = false) { // 第一步就过滤所有非数字和非小数点的字符(包括字母) let filtered = value.replace(/[^\d.]/g, ""); // 整数模式处理 if (isIntegerOnly !== false) { // 移除所有小数点 filtered = filtered.replace(/\./g, ""); // 处理前导零 filtered = filtered.replace(/^0+(\d)/, "$1") || filtered; // 空值处理(允许临时删除) if (filtered === "") { return ""; } // 最小值限制 if (filtered === isIntegerOnly || parseInt(filtered, 10) < isIntegerOnly) { return isIntegerOnly; } return filtered; } // 小数模式处理 const parts = filtered.split("."); if (parts.length > 1) { filtered = parts[0] + "." + (parts[1].substring(0, 2) || ""); } // 处理前导零 if (filtered.startsWith("0") && filtered.length > 1 && !filtered.startsWith("0.")) { filtered = filtered.replace(/^0+(\d)/, "$1"); } return filtered; } /** * 将时分秒字符串转换为完整日期格式 * @param {string} timeStr - 时分秒字符串,格式需为 HH:mm:ss(如 '00:53:00') * @param {Object} options - 可选配置项 * @param {string} [options.customDate] - 自定义日期,格式为 YYYY-MM-DD(默认使用当前日期) * @param {string} [options.format='YYYY-MM-DD HH:mm:ss'] - 输出的日期格式 * @returns {string|null} 转换后的日期字符串,失败时返回 null */ export function convertTimeToDate(timeStr, options = {}) { // 解构配置项,设置默认值 const { customDate, format = "YYYY-MM-DD HH:mm:ss" } = options; // 1. 校验时分秒格式(必须为 HH:mm:ss,允许数字1-2位) const timeRegex = /^\d{1,2}:\d{1,2}:\d{1,2}$/; if (!timeRegex.test(timeStr)) { console.error("时分秒格式错误,请使用 HH:mm:ss 格式(如 00:53:00)"); return null; } // 2. 确定日期部分(自定义日期或当前日期) let datePart; if (customDate) { // 校验自定义日期格式 if (!dayjs(customDate, "YYYY-MM-DD", true).isValid()) { console.error("自定义日期格式错误,请使用 YYYY-MM-DD 格式(如 2024-05-20)"); return null; } datePart = customDate; } else { // 使用当前日期(格式:YYYY-MM-DD) datePart = dayjs().format("YYYY-MM-DD"); } // 3. 组合日期和时分秒,生成完整日期对象 const fullDateTime = `${datePart} ${timeStr}`; const dateObj = dayjs(fullDateTime); // 4. 校验完整日期是否有效(如避免 2024-02-30 这种无效日期) if (!dateObj.isValid()) { console.error("生成的日期无效,请检查日期或时分秒是否合理"); return null; } // 5. 按指定格式返回日期字符串 return dateObj.format(format); } /** * 乘法计算并格式化结果 * @param {string|number} num1 - 第一个乘数 * @param {string|number} num2 - 第二个乘数 * @returns {string} 保留两位小数的结果(不四舍五入,补零) */ export const multiplyAndFormat = (num1: any, num2: any = 1): string => { try { // 转换为BigNumber(使用字符串构造避免精度问题) const bigNum1 = new BigNumber(num1.toString()); const bigNum2 = new BigNumber(num2.toString()); // 1. 乘法计算 const product = bigNum1.multipliedBy(bigNum2); // 2. 截断到两位小数(不四舍五入) const truncated = product.decimalPlaces(2, BigNumber.ROUND_DOWN); // 3. 格式化保留两位小数(补零) return truncated.toFixed(2); } catch (error) { console.error('计算错误:', error); return '0.00'; // 出错时返回默认值 } }; /** * 判断目标值是否包含指定字符串 * @param {string|number|array} target - 目标值(字符串/数字/数组) * @param {string} searchStr - 要查找的子字符串 * @param {object} [options] - 可选配置 * @param {boolean} [options.ignoreCase=false] - 是否忽略大小写 * @param {boolean} [options.allowEmpty=false] - 当 searchStr 为空时,是否返回 true(默认 false) * @returns {boolean} 是否包含指定字符串 */ export function includesString(target, searchStr, options = {}) { // 解构配置,设置默认值 const { ignoreCase = false, allowEmpty = false } = options; // 1. 处理 searchStr 为空的情况 if (searchStr === '' || searchStr === null || searchStr === undefined) { return allowEmpty; } // 2. 统一将目标值转为字符串(兼容数字/数组等) let targetStr = ''; if (typeof target === 'string') { targetStr = target; } else if (typeof target === 'number') { targetStr = String(target); } else if (Array.isArray(target)) { // 数组:拼接为字符串(也可改为 "数组中某一项包含",根据需求调整) targetStr = target.join(','); } else { // 其他类型(对象/布尔等):转为字符串或返回 false targetStr = String(target); } // 3. 处理大小写忽略 const processedTarget = ignoreCase ? targetStr.toLowerCase() : targetStr; const processedSearch = ignoreCase ? searchStr.toLowerCase() : searchStr; // 4. 执行包含判断 return processedTarget.includes(processedSearch); } /** * 校验手机号码(中国大陆) * - 支持 11 位手机号,号段 13x-19x * @param {string} phone * @returns {boolean} */ export function isValidMobile(phone: string): boolean { if (!phone && phone !== 0) return false; const s = String(phone).trim(); // 中国大陆手机号正则:以1开头,第二位3-9,后面9位数字,总共11位 const mobileRegex = /^1[3-9]\d{9}$/; return mobileRegex.test(s); }