JavaScript 工具函数集
创建于:2025-07-26 15:06:11
|
更新于:2025-11-12 15:59:45

clsx & twMerge

结合 clsx 和 twMerge。

import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export const cn = (...inputs: ClassValue[]) => {
  return twMerge(clsx(...inputs));
};

formatter

formatTimeToChatTime

时间转换

export const formatTimeToChatTime = (time: Date) => {
  const now = dayjs();
  const target = dayjs(time);
 
  // 如果传入时间小于60秒,返回刚刚
  if (now.diff(target, 'second') < 60) {
    return 'common.time.Just now';
  }
 
  // 如果时间是今天,展示几时:几分
  if (now.isSame(target, 'day')) {
    return target.format('HH:mm');
  }
 
  // 如果是昨天,展示昨天
  if (now.subtract(1, 'day').isSame(target, 'day')) {
    return 'common.time.Yesterday';
  }
 
  // 如果是前天,展示前天
  if (now.subtract(2, 'day').isSame(target, 'day')) {
    return 'common.time.The day before yesterday';
  }
 
  // 如果是今年,展示某月某日
  if (now.isSame(target, 'year')) {
    return target.format('MM/DD');
  }
 
  // 如果是更久之前,展示某年某月某日
  return target.format('YYYY/M/D');
};

mergeTableCol

antd-table 列合并

/**
 * antd-table 合并列
 * @param {any[]} array
 * @param {string} key
 * @returns
 */
export function mergeTableCol(array = [], key) {
  array = sortBy(array, key);
  let count = 0;
  let indexCount = 1;
  while (indexCount < array.length) {
    const item = array.slice(count, count + 1)[0];
    if (!item.rowSpan) item.rowSpan = 1;
    if (item[key] === array[indexCount][key]) {
      item.rowSpan++;
      array[indexCount].rowSpan = 0;
    } else {
      count = indexCount;
    }
    indexCount++;
  }
  return [...array];
}

buildTree

片平数组构建树结构

import { groupBy } from 'lodash';
 
interface FlatItem {
  [key: string]: any;
}
 
interface BuildTreeOptions {
  /* 主键 */
  idKey?: string;
  /* 父级主键 */
  parentIdKey?: string;
  /* 子级 */
  childrenKey?: string;
  /* 根节点 */
  rootParentId?: any;
}
 
/* 构建树 */
export const buildTree = <T extends FlatItem>(list: T[], options: BuildTreeOptions = {}): T[] => {
  const { idKey = 'id', parentIdKey = 'parentId', childrenKey = 'children', rootParentId = null } = options;
 
  const grouped = groupBy(list, parentIdKey);
 
  function build(parentId: any): T[] {
    return (grouped[parentId] || []).map((item) => ({
      ...item,
      [childrenKey]: build(item[idKey]),
    }));
  }
 
  return build(rootParentId);
};

function

带返回的函数防抖

常规防抖

function debounce( callback, delay = 300 ){
    let timer;
    return ( ...args ) => {
      clearTimeout( timer );
      timer = setTimeout( () => {
        callback( ...args );
      }, delay );
    }
}

问题:无法获取到函数的返回值。

原因:callback 在 settimeout 中执行,返回值自然是没办法返回给使用者的。

但实际上,一个函数在一段时间内不一定执行,也不应该拿到期待的返回值。

解决方案

  1. 回调函数中传递
  2. promise resolve

回调函数中传递

这种方式后续的操作都需要在回调中处理。

import { debounce } from "lodash-es";
 
const test = (callback) => {
  console.log("test");
  
  let returnValue = 100
  callback && callback(returnValue);
  
  return returnValue
};
 
const debouncedTest = debounce(test, 100);
 
console.log(
  "return",
  debouncedTest((value) => {
    console.log(value);
  })
);

promise resolve

将含有返回值的函数当做异步函数对待,通过 promise 承接返回值。

function debounceWithReturn( callback, delay ) {
  let timer;
 
  return( ...args ) => {
    return new Promise( ( resolve, reject ) => {
      clearTimeout(timer);
      timer = setTimeout( () => {
          try {
            let output = callback(...args);
            resolve( output );
          } catch ( err ) {
            reject( err );
          }
      }, delay );
    })
 
  }
}
 
// demo
const func =  val => {
  console.log(val)
  return val
};
 
const func1 = debounce(func, 1000);
 
const a = () => {
  let t;
  t =  func1(1);
  t =  func1(2);
  t =  func1(3);
  console.log('akira', t.then(res => console.log('then', res)));
};
 
a();

request

transParams

将 params 转换为 query 参数,用于拼接在 url 上。

/**
 * 将参数转换为 query 字符串
 * @param params - 需要转换的参数对象
 * @returns 转换后的 query 字符串
 */
export function transParams(params: Record<string, any>): string {
  let result = "";
 
  for (const propName of Object.keys(params)) {
    const value = params[propName];
    const part = encodeURIComponent(propName) + "=";
 
    if (value !== null && value !== "" && typeof value !== "undefined") {
      if (typeof value === "object") {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== "" && typeof value[key] !== "undefined") {
            const paramKey = `${propName}[${key}]`;
            const subPart = encodeURIComponent(paramKey) + "=";
            result += subPart + encodeURIComponent(value[key]) + "&";
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&";
      }
    }
  }
 
  return result;
}

download

/**
 * 下载文件
 * @param url - 文件下载地址
 * @param filename - 下载后的文件名
 */
export function download(url: string, filename: string) {
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  link.style.display = "none";
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}
 
/**
 * 下载 Blob 数据
 * @param blob - Blob 数据
 * @param filename - 下载后的文件名
 */
export function downloadBlob(blob: Blob, filename: string) {
  const url = window.URL.createObjectURL(blob);
  download(url, filename);
  window.URL.revokeObjectURL(url);
}
 
/**
 * 下载文本内容
 * @param content - 文本内容
 * @param filename - 下载后的文件名
 */
export function downloadText(content: string, filename: string) {
  const blob = new Blob([content], { type: "text/plain" });
  downloadBlob(blob, filename);
}

tree

buildTree

将拍平的数组转换为树状结构

export function buildTree(data,key="children"){
  const result = []
  const map = {}
  data.forEach((value,index)=>{
    map[value.id] = value
  })
  data.forEach((value,index)=>{
    if(value.pid){
      const parent = map[value.pid];
      if(!parent.hasOwnProperty(key)){
        parent[key] = []
      }
      parent[key].push(value)
    }else{
      result.push(value)
    }
  })
  return result;
}

filterTree

根据条件过滤树,子项包含符合条件的子项,则返回其以及其父级。

export function filterTree(treeData, filter, childrenKey = "children") {
  const loop = (data) => {
    return data
      .map((item) => {
        const children = item[childrenKey];
        const hasMatched = filter(item);
        const hasMatchingChildren = children && children.some((child) => loop([child])?.length > 0);
 
        if (hasMatched || hasMatchingChildren) {
          return {
            ...item,
            [childrenKey]: children ? loop(children) : undefined,
          };
        }
 
        return null;
      })
      .filter((item) => item !== null);
  };
  return loop(treeData);
}
我也是有底线的 🫠