项目中常用的工具utils

76 阅读5分钟

工具库 blog.csdn.net/frontlab/ar…

算术运算(也可以引入decimal.js库)

  • 手动实现 src/utils/math.js
/**
 * @desc 获取小数位数
 * @param {value}  数字
 */
function getDecimalPlaces(value) {
  if (Math.floor(value) !== value) {
    return value?.toString().split('.')[1]?.length ?? 0;
  }
  return 0;
}

/**
 * @desc 加减运算
 * @param {a}
 * @param {b}
 * @param {decimalPlaces}  需要保留几位小数
 * @param {operator}  '+'  '-'
 * @return {res} 计算结果
 */
function addOrSubtract(a, b, decimalPlaces, operator) {
  const maxDecimalPlaces = Math.max(getDecimalPlaces(a), getDecimalPlaces(b));
  const factor = Math.pow(10, maxDecimalPlaces);
  let result;
  if (operator === '+') {
    result = (a * factor + b * factor) / factor;
  } else {
    result = (a * factor - b * factor) / factor;
  }
  return result.toFixed(decimalPlaces ?? maxDecimalPlaces);
}

/**
 * @desc 加运算
 * @param {a}
 * @param {b}
 * @param {decimalPlaces}  需要保留几位小数
 * @return {res} 计算结果
 */
export function add(a, b, decimalPlaces) {
  return addOrSubtract(a, b, decimalPlaces, '+');
}

/**
 * @desc 减运算
 * @param {a}
 * @param {b}
 * @param {decimalPlaces}  需要保留几位小数
 * @return {res} 计算结果
 */
export function subtract(a, b, decimalPlaces) {
  return addOrSubtract(a, b, decimalPlaces, '-');
}

/**
 * @desc 乘法运算
 * @param {a}
 * @param {b}
 * @param {decimalPlaces}  需要保留几位小数
 * @return {res} 计算结果
 */
export function multiply(a, b, decimalPlaces) {
  const maxDecimalPlaces = Math.max(getDecimalPlaces(a), getDecimalPlaces(b));
  const factor = Math.pow(10, getDecimalPlaces(a) + getDecimalPlaces(b));
  return ((a * factor * (b * factor)) / (factor * factor)).toFixed(decimalPlaces ?? maxDecimalPlaces);
}

/**
 * @desc 除法运算
 * @param {a}
 * @param {b}
 * @param {decimalPlaces}  需要保留几位小数
 * @return {res} 计算结果
 */
export function divide(a, b, decimalPlaces) {
  if (b !== 0) {
    const maxDecimalPlaces = Math.max(getDecimalPlaces(a), getDecimalPlaces(b));
    const factor = Math.pow(10, getDecimalPlaces(b) - getDecimalPlaces(a));
    return ((a * factor) / (b * factor)).toFixed(decimalPlaces ?? maxDecimalPlaces);
  } else {
    throw new Error('Division by zero is not allowed.');
  }
}

console.log(add(1, 3));
console.log(add(1.12, 0.03));
console.log(subtract(5, 1));
console.log(subtract(2.2, 0.0212));
console.log(multiply(5, 6));
console.log(multiply(5.05, 1.0332));
console.log(divide(9, 6));
console.log(divide(8.32, 2.2, 2));
  • decimal.js
npm install --save decimal.js  // 安装
import { Decimal } from "decimal.js"  // 引入

const a = 9.99;
const b = 8.03;
let c = new Decimal(a).add(new Decimal(b)).sub(new Decimal(5) ).mul(new Decimal(2)).div(new Decimal(1))
// c 是 Decimal 对象,你可以使用 .toNumber() .toString() 转换成对应数字 字符串类型
console.log(c.toNumber());

格式器 src/utils/formatter.js

/**
 * @desc to千分位
 * @param {number}  需要处理的数字
 * @return {result} 千分位
 */
export function toThousands(number) {
  return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

/**
 * @desc to百分比
 * @param {number}  需要处理的数字
 * @param {decimalPlaces}  小数点后需要保留的位数
 * @return {result} 千分位
 */
export function toPercentage(number, decimalPlaces = 2) {
  const percentage = (number * 100).toFixed(decimalPlaces);
  return `${percentage}%`;
}

/**
 * @desc 手机号格式化 XXXXXXXXXXX ===>>> XXX XXXX XXXX
 * @param {phoneNumber}
 * @return {result}
 */
export function formatMobileNumber(phoneNumber) {
  // 移除所有非数字字符
  const cleanPhoneNumber = phoneNumber.replace(/\D/g, '');
  return cleanPhoneNumber.replace(/(\d{3})(\d{4})(\d{4})/, '$1 $2 $3');
}

/**
 * @desc 格式化地区信息
 * @param {String} str 要格式化的地区信息
 * @returns Object {province: "", city: "", detail: ""}
 */
export function formatAddress(str = '') {
  const result = {
    province: '',
    city: '',
    detail: '',
  };
  const provinceRegExp = /(.+?(省|自治区))(.*)/;
  const provinceList = str.match(provinceRegExp);
  let restStr = str;
  if (provinceList) {
    result.province = provinceList[1];
    restStr = provinceList[3];
  }
  const cityRegExp = /(.+?(市|自治州|行政区))(.*)/;
  const cityList = restStr.match(cityRegExp);
  if (cityList) {
    result.city = cityList[1];
    result.detail = cityList[3];
  } else {
    result.detail = restStr;
  }
  return result;
}

/**
 * @desc 格式化文件大小
 * @param {bytes}  大小
 * @param {decimalPlaces}  小数点后需要保留的位数
 * @return {result}
 */
export function formatFileSize(bytes, decimalPlaces = 2) {
  if (bytes === 0) return '0 Bytes';
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(1024));
  return `${(bytes / Math.pow(1024, i)).toFixed(decimalPlaces ?? 2)} ${sizes[i]}`;
}

/**
 * @desc 格式化文件大小
 * @param {size}
 * @param {unit}  转化后的单位
 * @param {decimalPlaces}  小数点后需要保留的位数
 * @return {result}
 */
export function formatFileSizeByUnit(size, unit = 'MB', decimalPlaces = 2) {
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const unitIndex = sizes.indexOf(unit.toUpperCase());
  if (unitIndex === -1) {
    throw new Error('Invalid unit. Available units: B, KB, MB, GB, TB');
  }
  const formattedSize = size / Math.pow(1024, unitIndex);
  return `${formattedSize.toFixed(decimalPlaces ?? 2)} ${unit}`;
}

console.log(toThousands(1234589.56)); // 1,234,589.56
console.log(toPercentage(0.23)); // 23.00%
console.log(formatMobileNumber('18817471234'));// 188 1747 1234
console.log(formatAddress("广西壮族自治区南宁市西乡塘区XX街XX号")); // {province: '广西壮族自治区', city: '南宁市', detail: '西乡塘区XX街XX号'}
console.log(formatAddress("上海市浦东新区xxx路xxx号")); // {province: '', city: '上海市', detail: '浦东新区xxx路xxx号'}
console.log(formatFileSize(1234565));  // 1.18 MB
console.log(formatFileSizeByUnit(1234565, 'KB')); // 1205.63 KB

日期辅助类 src/utils/dateHelper.js(在dayjs基础上实现)

import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween'; // 引入相关插件
dayjs.extend(isBetween);
// 封装一个日期工具类
export class DateHelper {
  /**
   * @desc 初始化
   * @param {dateString}  日期字符串 或者 日期对象
   */
  constructor(dateString) {
    this.date = dayjs(dateString);
  }
  /**
   * @desc 格式化时间
   * @param {formatString}  YYYY年M月D日  YYYY-MM-DD HH:mm:ss
   * @return {str}  格式化后的时间
   */
  format(formatString) {
    return this.date.format(formatString);
  }

  /**
   * @desc 加
   * @param {num}
   * @param {unit}  millisecond second minute hour  day  week month  year
   * @param {formatString}
   * @return {}
   */
  add(num, unit = 'day', formatString = 'YYYY-MM-DD') {
    return this.date.add(num, unit).format(formatString);
  }

  /**
   * @desc 减
   * @param {num}
   * @param {unit}  millisecond second minute hour  day  week month  year
   * @param {formatString}
   * @return {}
   */
  subtract(num, unit = 'day', formatString = 'YYYY-MM-DD') {
    return this.date.subtract(num, unit).format(formatString);
  }

  /**
   * @desc 获取某年(某月)的第一天
   * @param {unit}  month:某月 year:某年
   * @param {formatString}  month:某月 year:某年
   * @return {}
   */
  getFirstDayByUnit(unit = 'month', formatString = 'YYYY-MM-DD') {
    return this.date.startOf(unit).format(formatString);
  }

  /**
   * @desc 获取某年(某月)的最后一天
   * @param {unit}  month:某月 year:某年
   * @param {formatString}  month:某月 year:某年
   * @return {}
   */
  getLastDayByUnit(unit = 'month', formatString = 'YYYY-MM-DD') {
    return this.date.endOf(unit).format(formatString);
  }

  /**
   * @desc 获取时间差
   * @param {otherDate}
   * @param {unit}
   * @return {}
   */
  getDiff(otherDate, unit = 'day') {
    return this.date.diff(otherDate, unit);
  }

  /**
   * @desc 判断一个日期是否在另外一个日期之后
   * @param {otherDate}
   * @return {} true:之后 false:之前
   */
  isAfter(otherDate) {
    return this.date.isAfter(otherDate);
  }

  /**
   * @desc 判断一个日期是否在另外一个日期之前
   * @param {otherDate}
   * @return {} true:之前 false:之后
   */
  isBefore(otherDate) {
    return this.date.isBefore(otherDate);
  }

  /**
   * @desc 判断一个日期是否在两个日期之间
   * @param {a}
   * @param {b}
   * @return {} true:是
   */
  isBetween(a, b) {
    return this.date.isBetween(a, b);
  }
}


const customDate = new DateHelper(new Date()); // 2023年8月31日
console.log(customDate.format('YYYY年M月D日')); // 2023-08-31
console.log(customDate.format('MM/DD HH:mm:ss')); // 08/31 15:33:55

console.log(customDate.add(3, 'day')); // 2023-09-03
console.log(new DateHelper(new Date()).getFirstDayByUnit()); // 2023-08-01
console.log(new DateHelper('2022-09').getLastDayByUnit()); // 2022-09-30
console.log(new DateHelper(new Date()).isAfter('2023-08-31 10:59:59')); // true
console.log(new DateHelper(new Date()).isBetween('2023-09-01 10:59:59', '2023-09-07 10:59:59')); // false

脱敏辅助类 src/utils/desensitizerHelper.js

export class DesensitizerHelper {
  // 姓名脱敏
  static desensitizeName(name) {
    if (name.length === 1) {
      return name;
    }
    return `${name[0]}${'*'.repeat(name.length - 1)}`;
  }

  // 手机号脱敏
  static desensitizePhoneNumber(phoneNumber) {
    return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
  }

  // 邮箱脱敏
  static desensitizeEmail(email) {
    const [username, domain] = email.split('@');
    const usernameLen = username.length;
    const maskedUsername = usernameLen <= 2 ? '*'.repeat(usernameLen) : `${username[0]}*${username[usernameLen - 1]}`;
    return `${maskedUsername}@${domain}`;
  }

  // 银行卡号脱敏
  static desensitizeBankCardNumber(cardNumber) {
    const visibleDigits = 4; // 可见的末尾几位数字
    const maskedDigits = cardNumber.length - visibleDigits;
    const maskedPart = '*'.repeat(maskedDigits);
    const visiblePart = cardNumber.slice(maskedDigits);
    return maskedPart + visiblePart;
  }

  // 身份证号脱敏
  static desensitizeIDNumber(idNumber) {
    return idNumber.replace(/(\d{4})\d+(\d{4})/, '$1****$2');
  }

  // 护照脱敏
  static desensitizePassportNumber(passportNumber) {
    return passportNumber.replace(/(.{2}).+(.{2})/, '$1****$2');
  }

  // 座机号脱敏
  static desensitizeLandlinePhoneNumber(landline) {
    return landline.replace(/(\d{3}-\d{4})(\d+)/, '$1****$2');
  }
  
  // 地址脱敏
  static desensitizeAddress(address) {
    const visiblePart = address.substring(0, 6);
    const maskedPart = '*'.repeat(address.length - visiblePart.length);
    return visiblePart + maskedPart;
  }
}

校验器 src/utils/validators

const patterns = {
  nameCn: /^[\u4e00-\u9fa5]{2,4}$/, // 2-4个汉字,用于验证中文姓名
  nameEn: /^[A-Za-z]+([-']?[A-Za-z]+)?\s[A-Za-z]+([-']?[A-Za-z]+)?$/, //英文姓名可以包含字母、空格、连字符(-)和撇号(')等符号
  mobile: /^(13[0-9]|14[5-9]|15[0-3,5-9]|16[5,6]|17[0-8]|18[0-9]|19[1,8,9])\d{8}$/, // 用于验证手机号码
  email: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/, //用于验证电子邮箱
  chinese: /[\u4e00-\u9fa5]/, //匹配所有汉字
  geZeroInt: /^\d+$/, // 用于验证大于或等于0的整数
  int: /^-?\d+$/, // 用于验证整数,包括正整数、负整数和0
  geZero: /^\d+(\.\d+)?$/, // 用于验证大于或等于0的数字,可以是整数或小数
  idNo: /^\d{17}(\d|x)$/,// 用于验证身份证号码,支持18位身份证和17位身份证+x
  qqNo: /^[1-9]\d{4,10}$/, // 用于验证QQ号码,5-11位数字,第一位不能是0
  weNo: /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/, // 用于验证微信号码,6-20位字符,以字母开头,允许使用数字、字母、下划线和中划线
  carNo: /^[\u4e00-\u9fa5]{1}[A-Z]{1}[A-Z_0-9]{5}$/, // 用于验证车牌号码,车牌号格式为汉字 + 大写字母 + 5位数字或字母
  creditCode: /^[0-9A-Z]{18}$/, // 用于验证统一社会信用代码,18位数字
}
const validators = {}

/**
 * @desc 生成各种类别校验器   使用方式:validateNameCn("王xx") validateMobile('18817761234')
 */
for (const key of Object.keys(patterns)) {
  const PascalCaseKey = key.slice(0, 1).toUpperCase() + key.slice(1)
  validators[`validate${PascalCaseKey}`] = (val) => patterns[key].test(val)
}
export { validators };

lodash辅助器 src/utils/lodashHelper

import debounce  from 'lodash/debounce'
import cloneDeep from 'lodash/cloneDeep'

export class LodashHelper {

  static cloneDeep(obj) {
    return cloneDeep(obj);
  }

  static throttle(func, wait, options) {
    return throttle(func, wait, options);
  }

  static debounce(func, wait, options) {
    return debounce(func, wait, options);
  }
  
}

字符串操作 src/utils/character

/**
 * @desc 随机字符串
 * @param {length}  指定长度
 * @param {characters}  限定字符串范围
 * @return {} 
 */
export function generateRandomString(length = 20, characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
  let result = '';
  for (let i = 0; i < length; i++) {
    const randomIndex = Math.floor(Math.random() * characters.length);
    result += characters[randomIndex];
  }
  return result;
}

文件下载 src/utils/downloadFile

/**
 * @desc 将 url 转化成对应的文件并下载
 * @param {url}  
 * @param {fileName}  
 * @return {} 
 */
export function downloadFileByURL(url, fileName) {
  const aEle = document.createElement('a')
  aEle.setAttribute('href', url)
  fileName = decodeURIComponent(fileName)
  aEle.setAttribute('download', fileName)
  document.body.appendChild(aEle)
  aEle.click()
  document.body.removeChild(aEle)
}

/**
 * @desc 将 blob 转化成对应的文件并下载
 * @param {blob}  
 * @param {mimeType}  image/png  application/pdf  https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types 
 * @param {fileName}  文件名称 不带后缀
 * @return {} 
 */
export function downloadFileByBlob(blob, mimeType, fileName) {
  const url = window.URL.createObjectURL(new Blob([blob], { type: mimeType }))
  downloadFileByURL(url, fileName)
}

const content = 'Hello, World!';
downloadFileByBlob(content, 'text/plain', 'test');