util.ts 2.09 KB
export function bindMethods<T extends object>(instance: T): void {
  const prototype = Object.getPrototypeOf(instance);
  const propertyNames = Object.getOwnPropertyNames(prototype);

  propertyNames.forEach((propertyName) => {
    const descriptor = Object.getOwnPropertyDescriptor(prototype, propertyName);
    const propertyValue = instance[propertyName as keyof T];

    if (
      typeof propertyValue === 'function' &&
      propertyName !== 'constructor' &&
      descriptor &&
      !descriptor.get &&
      !descriptor.set
    ) {
      instance[propertyName as keyof T] = propertyValue.bind(instance);
    }
  });
}

/**
 * 获取嵌套对象的字段值
 * @param obj - 要查找的对象
 * @param path - 用于查找字段的路径,使用小数点分隔
 * @returns 字段值,或者未找到时返回 undefined
 */
export function getNestedValue<T>(obj: T, path: string): any {
  if (typeof path !== 'string' || path.length === 0) {
    throw new Error('Path must be a non-empty string');
  }
  // 把路径字符串按 "." 分割成数组
  const keys = path.split('.') as (number | string)[];

  let current: any = obj;

  for (const key of keys) {
    if (current === null || current === undefined) {
      return undefined;
    }
    current = current[key as keyof typeof current];
  }

  return current;
}

/**
 * 分转元 + 千分位格式化(TS 严格模式安全)
 * @param amount 分
 * @param decimals 小数位,默认 2
 */
export function fenToYuan(
  amount: bigint | number | string,
  decimals: number = 2,
): string {
  if (amount === null || amount === undefined || amount === '') {
    return (0).toFixed(decimals);
  }

  const num = Number(amount);
  if (Number.isNaN(num)) {
    return (0).toFixed(decimals);
  }

  const isNegative = num < 0;
  const yuan = Math.abs(num) / 100;

  const fixed = yuan.toFixed(decimals);

  // 👇 关键修复点
  const parts = fixed.split('.');
  const integer = parts[0] ?? '0';
  const decimal = parts[1];

  const thousand = integer.replaceAll(/\B(?=(\d{3})+(?!\d))/g, ',');

  return `${isNegative ? '-' : ''}${thousand}${decimal ? `.${decimal}` : ''}`;
}