【新增】私有证书

This commit is contained in:
cai
2025-09-03 15:15:59 +08:00
parent efd052a297
commit 954cd1638d
442 changed files with 76787 additions and 7483 deletions

View File

@@ -1,130 +1,142 @@
{
"name": "@baota/utils",
"version": "1.0.0",
"type": "module",
"main": "./dist/browser.cjs",
"module": "./dist/browser.mjs",
"types": "./dist/browser.d.ts",
"files": [
"dist/**",
"dist"
],
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"test": "vitest"
},
"exports": {
"./browser": {
"development": {
"types": "./src/browser.ts",
"import": "./src/browser.ts",
"require": "./src/browser.ts"
},
"default": {
"types": "./dist/browser.d.ts",
"import": "./dist/browser.mjs",
"require": "./dist/browser.cjs"
}
},
"./business": {
"development": {
"types": "./src/business.ts",
"import": "./src/business.ts",
"require": "./src/business.ts"
},
"default": {
"types": "./dist/business.d.ts",
"import": "./dist/business.mjs",
"require": "./dist/business.cjs"
}
},
"./data": {
"development": {
"types": "./src/data.ts",
"import": "./src/data.ts",
"require": "./src/data.ts"
},
"default": {
"types": "./dist/data.d.ts",
"import": "./dist/data.mjs",
"require": "./dist/data.cjs"
}
},
"./date": {
"development": {
"types": "./src/date.ts",
"import": "./src/date.ts",
"require": "./src/date.ts"
},
"default": {
"types": "./dist/date.d.ts",
"import": "./dist/date.mjs",
"require": "./dist/date.cjs"
}
},
"./encipher": {
"development": {
"types": "./src/encipher.ts",
"import": "./src/encipher.ts",
"require": "./src/encipher.ts"
},
"default": {
"types": "./dist/encipher.d.ts",
"import": "./dist/encipher.mjs",
"require": "./dist/encipher.cjs"
}
},
"./random": {
"development": {
"types": "./src/random.ts",
"import": "./src/random.ts",
"require": "./src/random.ts"
},
"default": {
"types": "./dist/random.d.ts",
"import": "./dist/random.mjs",
"require": "./dist/random.cjs"
}
},
"./string": {
"development": {
"types": "./src/string.ts",
"import": "./src/string.ts",
"require": "./src/string.ts"
},
"default": {
"types": "./dist/string.d.ts",
"import": "./dist/string.mjs",
"require": "./dist/string.cjs"
}
},
"./type": {
"development": {
"types": "./src/type.ts",
"import": "./src/type.ts",
"require": "./src/type.ts"
},
"default": {
"types": "./dist/type.d.ts",
"import": "./dist/type.mjs",
"require": "./dist/type.cjs"
}
}
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"jsencrypt": "^3.3.2",
"md5": "^2.3.0",
"ramda": "^0.30.1"
},
"devDependencies": {
"@baota/eslint": "workspace:*",
"@baota/typescript": "workspace:*",
"@baota/prettier": "workspace:*",
"vite": "^5.0.0",
"vite-plugin-dts": "^3.0.0"
}
"name": "@baota/utils",
"version": "1.0.0",
"type": "module",
"main": "./dist/browser.cjs",
"module": "./dist/browser.mjs",
"types": "./dist/browser.d.ts",
"files": [
"dist/**",
"dist"
],
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"test": "vitest"
},
"exports": {
".": {
"development": {
"types": "./src/index.ts",
"import": "./src/index.ts",
"require": "./src/index.ts"
},
"default": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"./browser": {
"development": {
"types": "./src/browser.ts",
"import": "./src/browser.ts",
"require": "./src/browser.ts"
},
"default": {
"types": "./dist/browser.d.ts",
"import": "./dist/browser.mjs",
"require": "./dist/browser.cjs"
}
},
"./business": {
"development": {
"types": "./src/business.ts",
"import": "./src/business.ts",
"require": "./src/business.ts"
},
"default": {
"types": "./dist/business.d.ts",
"import": "./dist/business.mjs",
"require": "./dist/business.cjs"
}
},
"./data": {
"development": {
"types": "./src/data.ts",
"import": "./src/data.ts",
"require": "./src/data.ts"
},
"default": {
"types": "./dist/data.d.ts",
"import": "./dist/data.mjs",
"require": "./dist/data.cjs"
}
},
"./date": {
"development": {
"types": "./src/date.ts",
"import": "./src/date.ts",
"require": "./src/date.ts"
},
"default": {
"types": "./dist/date.d.ts",
"import": "./dist/date.mjs",
"require": "./dist/date.cjs"
}
},
"./encipher": {
"development": {
"types": "./src/encipher.ts",
"import": "./src/encipher.ts",
"require": "./src/encipher.ts"
},
"default": {
"types": "./dist/encipher.d.ts",
"import": "./dist/encipher.mjs",
"require": "./dist/encipher.cjs"
}
},
"./random": {
"development": {
"types": "./src/random.ts",
"import": "./src/random.ts",
"require": "./src/random.ts"
},
"default": {
"types": "./dist/random.d.ts",
"import": "./dist/random.mjs",
"require": "./dist/random.cjs"
}
},
"./string": {
"development": {
"types": "./src/string.ts",
"import": "./src/string.ts",
"require": "./src/string.ts"
},
"default": {
"types": "./dist/string.d.ts",
"import": "./dist/string.mjs",
"require": "./dist/string.cjs"
}
},
"./type": {
"development": {
"types": "./src/type.ts",
"import": "./src/type.ts",
"require": "./src/type.ts"
},
"default": {
"types": "./dist/type.d.ts",
"import": "./dist/type.mjs",
"require": "./dist/type.cjs"
}
}
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"jsencrypt": "^3.3.2",
"md5": "^2.3.0",
"ramda": "^0.30.1"
},
"devDependencies": {
"@baota/eslint": "workspace:*",
"@baota/typescript": "workspace:*",
"@baota/prettier": "workspace:*",
"vite": "^5.0.0",
"vite-plugin-dts": "^3.0.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,16 +3,58 @@
* 包含1、数据类型检查。2、数据转换。3、日期处理。4、数据校验。5、数据过滤与重组。6、特殊场景处理
*/
import * as R from 'ramda'
import * as R from "ramda";
// =============== 数据转换 ===============
/**
* 数字格式化
* @param num 数字
* @param decimals 小数位数
* @param thousandsSeparator 千位分隔符
* @param decimalSeparator 小数分隔符
*/
export const formatNumber = (
num: number | string,
decimals = 0,
thousandsSeparator = ",",
decimalSeparator = "."
): string => {
const number = Number(num);
if (isNaN(number)) return "";
const fixed = number.toFixed(decimals);
const parts = fixed.split(".");
// 添加千位分隔符
if (parts[0]) {
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
}
return parts.join(decimalSeparator);
};
/**
* 货币格式化
* @param amount 金额
* @param currency 货币符号
* @param decimals 小数位数
*/
export const formatCurrency = (
amount: number | string,
currency = "¥",
decimals = 2
): string => {
const formatted = formatNumber(amount, decimals);
return `${currency}${formatted}`;
};
/**
* 将对象的所有值转换为字符串
* @param {Record<string, any>} obj - 要转换的对象
* @returns {Record<string, string>} 转换后的对象
*/
export const objectToString = R.map(String)
export const objectToString = R.map(String);
/**
* 将数组转换为对象,使用指定的 key
@@ -20,36 +62,38 @@ export const objectToString = R.map(String)
* @param {Record<string, any>[]} array - 要转换的数组
* @returns {Record<string, Record<string, any>>} 转换后的对象
*/
export const arrayToObject = R.curry((key: string, array: Record<string, any>[]) => R.indexBy(R.prop(key), array)) as <
T extends Record<string, any>,
>(
key: string,
array: T[],
) => Record<string, T>
export const arrayToObject = R.curry(
(key: string, array: Record<string, any>[]) => R.indexBy(R.prop(key), array)
) as <T extends Record<string, any>>(
key: string,
array: T[]
) => Record<string, T>;
/**
* 深度扁平化对象(建议深度嵌套的对象使用)
* @param {Record<string, any>} obj - 要扁平化的对象
* @returns {Record<string, any>} 扁平化后的对象
*/
export const flattenObject = (obj: Record<string, unknown>): Record<string, unknown> => {
const result: Record<string, unknown> = {}
export const flattenObject = (
obj: Record<string, unknown>
): Record<string, unknown> => {
const result: Record<string, unknown> = {};
const flatten = (obj: Record<string, any>, prefix: string = '') => {
for (const key in obj) {
const value = obj[key]
const newKey = prefix ? `${prefix}.${key}` : key
if (value && typeof value === 'object' && !Array.isArray(value)) {
flatten(value, newKey)
} else {
result[newKey] = value
}
}
}
const flatten = (obj: Record<string, any>, prefix: string = "") => {
for (const key in obj) {
const value = obj[key];
const newKey = prefix ? `${prefix}.${key}` : key;
if (value && typeof value === "object" && !Array.isArray(value)) {
flatten(value, newKey);
} else {
result[newKey] = value;
}
}
};
flatten(obj)
return result
}
flatten(obj);
return result;
};
/**
* 验证字符串是否符合正则表达式
@@ -57,10 +101,9 @@ export const flattenObject = (obj: Record<string, unknown>): Record<string, unkn
* @param {string} str - 要验证的字符串
* @returns {boolean} 如果字符串符合正则表达式,则返回 true否则返回 false
*/
export const matchesPattern = R.curry((pattern: RegExp, str: string) => R.test(pattern, str)) as <T extends RegExp>(
pattern: T,
str: string,
) => boolean
export const matchesPattern = R.curry((pattern: RegExp, str: string) =>
R.test(pattern, str)
) as <T extends RegExp>(pattern: T, str: string) => boolean;
/**
* 验证对象是否包含所有必需的键
@@ -68,12 +111,13 @@ export const matchesPattern = R.curry((pattern: RegExp, str: string) => R.test(p
* @param {string[]} requiredKeys - 要验证的键
* @returns {boolean} 如果对象包含所有必需的键,则返回 true否则返回 false
*/
export const hasRequiredKeys = R.curry((obj: Record<string, unknown>, requiredKeys: string[]) =>
R.all(R.flip(R.has)(obj), requiredKeys),
export const hasRequiredKeys = R.curry(
(obj: Record<string, unknown>, requiredKeys: string[]) =>
R.all(R.flip(R.has)(obj), requiredKeys)
) as {
(obj: Record<string, unknown>): (requiredKeys: string[]) => boolean
(obj: Record<string, unknown>, requiredKeys: string[]): boolean
}
(obj: Record<string, unknown>): (requiredKeys: string[]) => boolean;
(obj: Record<string, unknown>, requiredKeys: string[]): boolean;
};
// ... existing code ...
/**
@@ -84,8 +128,8 @@ export const hasRequiredKeys = R.curry((obj: Record<string, unknown>, requiredKe
* @returns {boolean} 如果值在指定范围内,则返回 true否则返回 false
*/
export const isInRange = R.curry((min: number, max: number, value: number) =>
R.both(R.gte(R.__, min), R.lte(R.__, max))(value),
) as <T extends number>(min: T, max: T, value: T) => boolean
R.both(R.gte(R.__, min), R.lte(R.__, max))(value)
) as <T extends number>(min: T, max: T, value: T) => boolean;
// =============== 数据过滤与重组 ===============
@@ -96,12 +140,22 @@ export const isInRange = R.curry((min: number, max: number, value: number) =>
* @returns {Record<string, any>} 过滤后的对象
*/
export const filterObject = R.curry(
<T extends Record<string, any>>(predicate: (value: T[keyof T]) => boolean, obj: T) =>
Object.fromEntries(Object.entries(obj).filter(([_, value]) => predicate(value))),
<T extends Record<string, any>>(
predicate: (value: T[keyof T]) => boolean,
obj: T
) =>
Object.fromEntries(
Object.entries(obj).filter(([_, value]) => predicate(value))
)
) as {
<T extends Record<string, any>>(predicate: (value: T[keyof T]) => boolean): (obj: T) => Partial<T>
<T extends Record<string, any>>(predicate: (value: T[keyof T]) => boolean, obj: T): Partial<T>
}
<T extends Record<string, any>>(predicate: (value: T[keyof T]) => boolean): (
obj: T
) => Partial<T>;
<T extends Record<string, any>>(
predicate: (value: T[keyof T]) => boolean,
obj: T
): Partial<T>;
};
/**
* 按照指定的键对数组进行分组
@@ -109,9 +163,13 @@ export const filterObject = R.curry(
* @param {Record<string, any>[]} array - 要分组的数组
* @returns {Record<string, Record<string, any>[]>} 分组后的对象
*/
export const groupByKey = R.curry(<T extends Record<string, any>>(key: string, array: T[]) =>
R.groupBy(R.prop(key), array),
) as <T extends Record<string, any>>(key: string, array: T[]) => Record<string, T[]>
export const groupByKey = R.curry(
<T extends Record<string, any>>(key: string, array: T[]) =>
R.groupBy(R.prop(key), array)
) as <T extends Record<string, any>>(
key: string,
array: T[]
) => Record<string, T[]>;
/**
* 从对象数组中提取指定的键值
@@ -119,29 +177,28 @@ export const groupByKey = R.curry(<T extends Record<string, any>>(key: string, a
* @param {Record<string, any>[]} list - 要提取的对象数组
* @returns {Record<string, any>[]} 提取后的对象数组
*/
export const pluckDeep = R.curry(<T>(path: string[], list: T[]) => R.map(R.path(path), list)) as <
T extends Record<string, any>,
>(
path: string[],
list: T[],
) => T[]
export const pluckDeep = R.curry(<T>(path: string[], list: T[]) =>
R.map(R.path(path), list)
) as <T extends Record<string, any>>(path: string[], list: T[]) => T[];
/**
* 对嵌套数组进行扁平化和去重
* @param {any[]} array - 要扁平化和去重的数组
* @returns {any[]} 扁平化和去重后的数组
*/
export const flattenAndUniq = R.pipe(R.flatten, R.uniq) as <T>(array: T[]) => T[]
export const flattenAndUniq = R.pipe(R.flatten, R.uniq) as <T>(
array: T[]
) => T[];
// =============== 数据映射 ===============
type MapperOption = {
inherit?: string[] // 继承字段
deep?: boolean // 深度映射
ignore?: string[] // 忽略字段
}
inherit?: string[]; // 继承字段
deep?: boolean; // 深度映射
ignore?: string[]; // 忽略字段
};
type MapperType = [string, string][] | Record<string, string>
type DataType = Record<string, unknown> | Record<string, unknown>[]
type MapperType = [string, string][] | Record<string, string>;
type DataType = Record<string, unknown> | Record<string, unknown>[];
/**
* 对象/数组映射,根据映射表,将数组或对象映射为新的对象和数组
@@ -149,92 +206,117 @@ type DataType = Record<string, unknown> | Record<string, unknown>[]
* 增加异常处理,如果值不存在,则抛出异常。
* 返回新的对象/数组
*/
export const mapData = (mapper: MapperType, data: DataType, options: MapperOption = { deep: true }): DataType => {
const { inherit, deep, ignore } = options
export const mapData = (
mapper: MapperType,
data: DataType,
options: MapperOption = { deep: true }
): DataType => {
const { inherit, deep, ignore } = options;
// 验证 inherit 和 ignore 不能同时使用
if (inherit && ignore) {
throw new Error('inherit 和 ignore 选项不能同时使用')
}
// 验证 inherit 和 ignore 不能同时使用
if (inherit && ignore) {
throw new Error("inherit 和 ignore 选项不能同时使用");
}
// 将 mapper 转换为对象形式
const mapperObj = Array.isArray(mapper)
? mapper.reduce<Record<string, string>>((acc, [key, value]) => ({ ...acc, [key]: value }), {})
: mapper
// 将 mapper 转换为对象形式
const mapperObj = Array.isArray(mapper)
? mapper.reduce<Record<string, string>>(
(acc, [key, value]) => ({ ...acc, [key]: value }),
{}
)
: mapper;
// 处理数组
if (Array.isArray(data)) {
return data.map((item) => mapData(mapperObj, item, options) as Record<string, unknown>)
}
// 处理数组
if (Array.isArray(data)) {
return data.map(
(item) => mapData(mapperObj, item, options) as Record<string, unknown>
);
}
// 处理对象
if (typeof data === 'object' && data !== null) {
// 根据选项过滤 mapper
let finalMapper = { ...mapperObj }
if (inherit) {
finalMapper = Object.entries(mapperObj)
.filter(([key]) => inherit.includes(key))
.reduce<Record<string, string>>((acc, [key, value]) => ({ ...acc, [key]: value }), {})
} else if (ignore) {
finalMapper = Object.entries(mapperObj)
.filter(([key]) => !ignore.includes(key))
.reduce<Record<string, string>>((acc, [key, value]) => ({ ...acc, [key]: value }), {})
}
// 处理对象
if (typeof data === "object" && data !== null) {
// 根据选项过滤 mapper
let finalMapper = { ...mapperObj };
if (inherit) {
finalMapper = Object.entries(mapperObj)
.filter(([key]) => inherit.includes(key))
.reduce<Record<string, string>>(
(acc, [key, value]) => ({ ...acc, [key]: value }),
{}
);
} else if (ignore) {
finalMapper = Object.entries(mapperObj)
.filter(([key]) => !ignore.includes(key))
.reduce<Record<string, string>>(
(acc, [key, value]) => ({ ...acc, [key]: value }),
{}
);
}
return Object.entries(finalMapper).reduce<Record<string, unknown>>((result, [sourceKey, targetKey]) => {
// 处理嵌套路径
const value = sourceKey.split('.').reduce<unknown>((obj, key) => {
if (obj === undefined || obj === null) {
throw new Error(`映射键 "${sourceKey}" 不存在于源数据中`)
}
return (obj as Record<string, unknown>)[key]
}, data)
return Object.entries(finalMapper).reduce<Record<string, unknown>>(
(result, [sourceKey, targetKey]) => {
// 处理嵌套路径
const value = sourceKey.split(".").reduce<unknown>((obj, key) => {
if (obj === undefined || obj === null) {
throw new Error(`映射键 "${sourceKey}" 不存在于源数据中`);
}
return (obj as Record<string, unknown>)[key];
}, data);
// 处理值不存在的情况
if (value === undefined) {
throw new Error(`映射键 "${sourceKey}" 的值不存在`)
}
// 处理值不存在的情况
if (value === undefined) {
throw new Error(`映射键 "${sourceKey}" 的值不存在`);
}
// 处理深度映射
if (deep && typeof value === 'object' && value !== null) {
const nestedMapper = Object.entries(mapperObj)
.filter(([key]) => key.startsWith(`${sourceKey}.`))
.reduce<Record<string, string>>(
(acc, [key, val]) => ({
...acc,
[key.slice(sourceKey.length + 1)]: val,
}),
{},
)
// 处理深度映射
if (deep && typeof value === "object" && value !== null) {
const nestedMapper = Object.entries(mapperObj)
.filter(([key]) => key.startsWith(`${sourceKey}.`))
.reduce<Record<string, string>>(
(acc, [key, val]) => ({
...acc,
[key.slice(sourceKey.length + 1)]: val,
}),
{}
);
if (Object.keys(nestedMapper).length > 0) {
return {
...result,
[targetKey]: mapData(nestedMapper, value as Record<string, unknown>, options),
}
}
}
if (Object.keys(nestedMapper).length > 0) {
return {
...result,
[targetKey]: mapData(
nestedMapper,
value as Record<string, unknown>,
options
),
};
}
}
// 处理嵌套目标路径
const targetPath = (targetKey as string).split('.')
const finalKey = targetPath.pop()!
const targetObj = targetPath.reduce<Record<string, unknown>>((obj, key) => {
if (!(key in obj)) {
obj[key] = {}
}
return obj[key] as Record<string, unknown>
}, result)
// 处理嵌套目标路径
const targetPath = (targetKey as string).split(".");
const finalKey = targetPath.pop()!;
const targetObj = targetPath.reduce<Record<string, unknown>>(
(obj, key) => {
if (!(key in obj)) {
obj[key] = {};
}
return obj[key] as Record<string, unknown>;
},
result
);
if (finalKey && targetObj) {
targetObj[finalKey] = value
}
if (finalKey && targetObj) {
targetObj[finalKey] = value;
}
return result
}, {})
}
return result;
},
{}
);
}
return data
}
return data;
};
/**
* @description 生成映射表,将所有字段转换为小驼峰
@@ -242,11 +324,11 @@ export const mapData = (mapper: MapperType, data: DataType, options: MapperOptio
* @returns {Record<string, unknown>} 转换后的对象
*/
export const generateMapper = (obj: Record<string, unknown>) => {
return Object.entries(obj).map(([key, value]) => [
key.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()),
value,
])
}
return Object.entries(obj).map(([key, value]) => [
key.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()),
value,
]);
};
/**
* 将对象转换为查询字符串
@@ -254,11 +336,13 @@ export const generateMapper = (obj: Record<string, unknown>) => {
* @returns {string} 转换后的查询字符串
*/
export const objectToQueryString = (obj: Record<string, any>) => {
return Object.entries(obj)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&')
}
return Object.entries(obj)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
)
.join("&");
};
/**
* 深度合并两个对象
@@ -266,29 +350,38 @@ export const objectToQueryString = (obj: Record<string, any>) => {
* @param {Record<string, any>} source - 源对象
* @returns {Record<string, any>} 合并后的对象
*/
export const deepMerge = <T extends Record<string, any>>(target: T, source: T, isMergeArray: boolean = true): T => {
const result = { ...target } as T
export const deepMerge = <T extends Record<string, any>>(
target: T,
source: T,
isMergeArray: boolean = true
): T => {
const result = { ...target } as T;
for (const key in source) {
if (source.hasOwnProperty(key)) {
const sourceValue = source[key]
const targetValue = target[key]
for (const key in source) {
if (source.hasOwnProperty(key)) {
const sourceValue = source[key];
const targetValue = target[key];
if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
// 如果是数组,则合并数组
result[key] = (isMergeArray ? [...targetValue, ...sourceValue] : sourceValue) as T[Extract<keyof T, string>]
} else if (isObject(sourceValue) && isObject(targetValue)) {
// 如果是对象,则递归合并
result[key] = deepMerge(targetValue, sourceValue) as T[Extract<keyof T, string>]
} else {
// 其他情况直接覆盖
result[key] = sourceValue as T[Extract<keyof T, string>]
}
}
}
if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
// 如果是数组,则合并数组
result[key] = (
isMergeArray ? [...targetValue, ...sourceValue] : sourceValue
) as T[Extract<keyof T, string>];
} else if (isObject(sourceValue) && isObject(targetValue)) {
// 如果是对象,则递归合并
result[key] = deepMerge(targetValue, sourceValue) as T[Extract<
keyof T,
string
>];
} else {
// 其他情况直接覆盖
result[key] = sourceValue as T[Extract<keyof T, string>];
}
}
}
return result
}
return result;
};
/**
* 判断是否为对象
@@ -296,9 +389,8 @@ export const deepMerge = <T extends Record<string, any>>(target: T, source: T, i
* @returns {boolean} 是否为对象
*/
const isObject = (value: any): boolean => {
return value !== null && typeof value === 'object' && !Array.isArray(value)
}
return value !== null && typeof value === "object" && !Array.isArray(value);
};
/**
* @description 清理对象前后字符串
@@ -306,12 +398,14 @@ const isObject = (value: any): boolean => {
* @returns {Record<string, any>} 清理后的对象
*/
export const trimObject = (obj: Record<string, any>) => {
return Object.entries(obj).reduce<Record<string, any>>((acc, [key, value]) => {
acc[key.trim()] = value.trim()
return acc
}, {})
}
return Object.entries(obj).reduce<Record<string, any>>(
(acc, [key, value]) => {
acc[key.trim()] = value.trim();
return acc;
},
{}
);
};
/**
* 深拷贝对象(简单版)
@@ -319,7 +413,5 @@ export const trimObject = (obj: Record<string, any>) => {
* @returns {any} 拷贝后的对象
*/
export const deepClone = <T>(obj: T): T => {
return JSON.parse(JSON.stringify(obj))
}
return JSON.parse(JSON.stringify(obj));
};

View File

@@ -2,7 +2,7 @@
* 文件定义:日期处理
*/
import * as R from 'ramda'
import * as R from "ramda";
/* -------------- 1、日期处理 -------------- */
/**
@@ -11,36 +11,44 @@ import * as R from 'ramda'
* @param {string} format - 格式化字符串
* @returns {string} 格式化后的日期字符串
*/
export const formatDate = (date: string | number | Date, format: string = 'yyyy-MM-dd HH:mm:ss'): string => {
// 处理秒级时间戳
const timestamp = !!Number(date) && date.toString().length === 10 ? new Date(Number(date) * 1000) : new Date(date)
export const formatDate = (
date: string | number | Date | null | undefined,
format: string = "yyyy-MM-dd HH:mm:ss"
): string => {
if (!date) return "--";
// 处理秒级时间戳
const timestamp =
!!Number(date) && date.toString().length === 10
? new Date(Number(date) * 1000)
: new Date(date);
// 使用Ramda创建日期映射
const dateMap = R.zipObj(
['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss'],
[
timestamp.getFullYear(),
timestamp.getMonth() + 1,
timestamp.getDate(),
timestamp.getHours(),
timestamp.getMinutes(),
timestamp.getSeconds(),
],
)
// 使用Ramda创建日期映射
const dateMap = R.zipObj(
["yyyy", "MM", "dd", "HH", "mm", "ss"],
[
timestamp.getFullYear(),
timestamp.getMonth() + 1,
timestamp.getDate(),
timestamp.getHours(),
timestamp.getMinutes(),
timestamp.getSeconds(),
]
);
// 使用Ramda的reduce函数替换格式字符串中的占位符
return R.reduce(
(result: string, key: string) => {
const value = dateMap[key as keyof typeof dateMap]
// 将单位数的月、日、时、分、秒前面补0
const formattedValue = key !== 'yyyy' && value < 10 ? `0${value}` : `${value}`
// 使用正则表达式全局替换所有匹配项
return result.replace(new RegExp(key, 'g'), formattedValue)
},
format,
R.keys(dateMap),
)
}
// 使用Ramda的reduce函数替换格式字符串中的占位符
return R.reduce(
(result: string, key: string) => {
const value = dateMap[key as keyof typeof dateMap];
// 将单位数的月、日、时、分、秒前面补0
const formattedValue =
key !== "yyyy" && value < 10 ? `0${value}` : `${value}`;
// 使用正则表达式全局替换所有匹配项
return result.replace(new RegExp(key, "g"), formattedValue);
},
format,
R.keys(dateMap)
);
};
/**
* 获取两个日期之间的天数差
@@ -48,14 +56,21 @@ export const formatDate = (date: string | number | Date, format: string = 'yyyy-
* @param {string | number | Date} endDate - 结束日期
* @returns {number} 天数差
*/
export const getDaysDiff = (startDate: string | number | Date, endDate: string | number | Date): number => {
const start = new Date(startDate)
const end = new Date(endDate)
const startDay = new Date(start.getFullYear(), start.getMonth(), start.getDate())
const endDay = new Date(end.getFullYear(), end.getMonth(), end.getDate())
const diff = endDay.getTime() - startDay.getTime()
return Math.floor(diff / (1000 * 60 * 60 * 24))
}
export const getDaysDiff = (
startDate: string | number | Date,
endDate: string | number | Date
): number => {
const start = new Date(startDate);
const end = new Date(endDate);
const startDay = new Date(
start.getFullYear(),
start.getMonth(),
start.getDate()
);
const endDay = new Date(end.getFullYear(), end.getMonth(), end.getDate());
const diff = endDay.getTime() - startDay.getTime();
return Math.floor(diff / (1000 * 60 * 60 * 24));
};
/**
* 柯里化版本的getDaysDiff
@@ -64,9 +79,11 @@ export const getDaysDiff = (startDate: string | number | Date, endDate: string |
* @returns {number} 天数差
*/
export const getDaysDiffCurried: {
(startDate: string | number | Date, endDate: string | number | Date): number
(startDate: string | number | Date): (endDate: string | number | Date) => number
} = R.curry(getDaysDiff)
(startDate: string | number | Date, endDate: string | number | Date): number;
(startDate: string | number | Date): (
endDate: string | number | Date
) => number;
} = R.curry(getDaysDiff);
/**
* 判断日期是否在指定范围内
@@ -76,15 +93,15 @@ export const getDaysDiffCurried: {
* @returns {boolean} 是否在范围内
*/
export const isDateInRange = (
date: string | number | Date,
startDate: string | number | Date,
endDate: string | number | Date,
date: string | number | Date,
startDate: string | number | Date,
endDate: string | number | Date
): boolean => {
const targetTime = new Date(date).getTime()
const startTime = new Date(startDate).getTime()
const endTime = new Date(endDate).getTime()
return targetTime >= startTime && targetTime <= endTime
}
const targetTime = new Date(date).getTime();
const startTime = new Date(startDate).getTime();
const endTime = new Date(endDate).getTime();
return targetTime >= startTime && targetTime <= endTime;
};
/**
* 柯里化版本的isDateInRange
@@ -94,13 +111,24 @@ export const isDateInRange = (
* @returns {boolean} 是否在范围内
*/
export const isDateInRangeCurried: {
(date: string | number | Date, startDate: string | number | Date, endDate: string | number | Date): boolean
(date: string | number | Date): {
(startDate: string | number | Date, endDate: string | number | Date): boolean
(startDate: string | number | Date): (endDate: string | number | Date) => boolean
}
(date: string | number | Date, startDate: string | number | Date): (endDate: string | number | Date) => boolean
} = R.curry(isDateInRange)
(
date: string | number | Date,
startDate: string | number | Date,
endDate: string | number | Date
): boolean;
(date: string | number | Date): {
(
startDate: string | number | Date,
endDate: string | number | Date
): boolean;
(startDate: string | number | Date): (
endDate: string | number | Date
) => boolean;
};
(date: string | number | Date, startDate: string | number | Date): (
endDate: string | number | Date
) => boolean;
} = R.curry(isDateInRange);
/**
* 获取指定日期的开始时间00:00:00
@@ -108,9 +136,9 @@ export const isDateInRangeCurried: {
* @returns {Date} 日期的开始时间
*/
export const getStartOfDay = (date: string | number | Date): Date => {
const d = new Date(date)
return new Date(d.getFullYear(), d.getMonth(), d.getDate())
}
const d = new Date(date);
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
};
/**
* 获取指定日期的结束时间23:59:59
@@ -118,9 +146,9 @@ export const getStartOfDay = (date: string | number | Date): Date => {
* @returns {Date} 日期的结束时间
*/
export const getEndOfDay = (date: string | number | Date): Date => {
const d = new Date(date)
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999)
}
const d = new Date(date);
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
};
/**
* 添加天数到指定日期
@@ -129,39 +157,47 @@ export const getEndOfDay = (date: string | number | Date): Date => {
* @returns {Date} 新日期
*/
export const addDays = (days: number, date: string | number | Date): Date => {
const result = new Date(date)
result.setDate(result.getDate() + days)
return result
}
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
};
// 柯里化版本的addDays
export const addDaysCurried: {
(days: number, date: string | number | Date): Date
(days: number): (date: string | number | Date) => Date
} = R.curry(addDays)
(days: number, date: string | number | Date): Date;
(days: number): (date: string | number | Date) => Date;
} = R.curry(addDays);
/**
* 格式化相对时间刚刚、x分钟前、x小时前、x天前
* @param {string | number | Date} date - 日期
* @returns {string} 格式化后的相对时间
*/
export const formatRelativeTime = (date: string | number | Date): string => {
const now = new Date().getTime()
const target = new Date(date).getTime()
const diff = now - target
export const formatRelativeTime = (
date: string | number | Date | null | undefined,
format: string = "YYYY-MM-DD"
): string => {
if (!date) return "--";
const timestamp =
!!Number(date) && date.toString().length === 10
? new Date(Number(date) * 1000)
: new Date(date);
const now = new Date().getTime();
const target = new Date(timestamp).getTime();
const diff = now - target;
if (diff < 1000 * 60) {
return '刚刚'
} else if (diff < 1000 * 60 * 60) {
return `${Math.floor(diff / (1000 * 60))}分钟前`
} else if (diff < 1000 * 60 * 60 * 24) {
return `${Math.floor(diff / (1000 * 60 * 60))}小时前`
} else if (diff < 1000 * 60 * 60 * 24 * 30) {
return `${Math.floor(diff / (1000 * 60 * 60 * 24))}天前`
} else {
return formatDate(date, 'YYYY-MM-DD')
}
}
if (diff < 1000 * 60) {
return "刚刚";
} else if (diff < 1000 * 60 * 60) {
return `${Math.floor(diff / (1000 * 60))}分钟前`;
} else if (diff < 1000 * 60 * 60 * 24) {
return `${Math.floor(diff / (1000 * 60 * 60))}小时前`;
} else if (diff < 1000 * 60 * 60 * 24 * 30) {
return `${Math.floor(diff / (1000 * 60 * 60 * 24))}天前`;
} else {
return formatDate(date, format);
}
};
/**
* 获取指定日期是星期几
@@ -169,9 +205,9 @@ export const formatRelativeTime = (date: string | number | Date): string => {
* @returns {string} 星期几
*/
export const getDayOfWeek = (date: string | number | Date): string => {
const days = ['日', '一', '二', '三', '四', '五', '六']
return `星期${days[new Date(date).getDay()]}`
}
const days = ["日", "一", "二", "三", "四", "五", "六"];
return `星期${days[new Date(date).getDay()]}`;
};
/**
* 获取指定距离到期时间
@@ -180,12 +216,12 @@ export const getDayOfWeek = (date: string | number | Date): string => {
* @returns {string} 距离到期时间
*/
export const getDaysUntilExpiration = (
date: string | number | Date,
expirationDate: string | number | Date = new Date(),
date: string | number | Date,
expirationDate: string | number | Date = new Date()
): string => {
const target = new Date(date)
const expiration = new Date(expirationDate)
const diff = expiration.getTime() - target.getTime()
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
return days > 0 ? `${days}` : '已过期'
}
const target = new Date(date);
const expiration = new Date(expirationDate);
const diff = expiration.getTime() - target.getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
return days > 0 ? `${days}` : "已过期";
};