shop-wx/doc/examples/utils-example.ts

473 lines
11 KiB
TypeScript
Raw Normal View History

/**
*
*
*/
/**
*
* @param date Date | string | number
* @param format 'YYYY-MM-DD HH:mm:ss'
* @returns
*/
export function formatDate(date: Date | string | number, format = 'YYYY-MM-DD HH:mm:ss'): string {
// 如果传入的是字符串或数字,先转换为 Date 对象
const dateObj = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date;
// 验证日期有效性
if (isNaN(dateObj.getTime())) {
throw new Error('无效的日期格式');
}
const year = dateObj.getFullYear();
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
const day = String(dateObj.getDate()).padStart(2, '0');
const hours = String(dateObj.getHours()).padStart(2, '0');
const minutes = String(dateObj.getMinutes()).padStart(2, '0');
const seconds = String(dateObj.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', String(year))
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
/**
*
* @param date
* @returns 23
*/
export function formatRelativeTime(date: Date | string | number): string {
const now = new Date();
const targetDate = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date;
const diff = now.getTime() - targetDate.getTime();
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
const week = 7 * day;
const month = 30 * day;
const year = 365 * day;
if (diff < minute) {
return '刚刚';
} else if (diff < hour) {
return `${Math.floor(diff / minute)}分钟前`;
} else if (diff < day) {
return `${Math.floor(diff / hour)}小时前`;
} else if (diff < week) {
return `${Math.floor(diff / day)}天前`;
} else if (diff < month) {
return `${Math.floor(diff / week)}周前`;
} else if (diff < year) {
return `${Math.floor(diff / month)}月前`;
} else {
return `${Math.floor(diff / year)}年前`;
}
}
/**
*
* @param func
* @param delay
* @returns
*/
export function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): (...args: Parameters<T>) => void {
let timerId: NodeJS.Timeout | null = null;
return function (this: any, ...args: Parameters<T>) {
// 清除之前的定时器
if (timerId) {
clearTimeout(timerId);
}
// 设置新的定时器
timerId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
/**
*
* @param func
* @param delay
* @returns
*/
export function throttle<T extends (...args: any[]) => any>(
func: T,
delay: number
): (...args: Parameters<T>) => void {
let lastExecTime = 0;
return function (this: any, ...args: Parameters<T>) {
const currentTime = Date.now();
// 如果间隔时间未到,不执行函数
if (currentTime - lastExecTime < delay) {
return;
}
// 更新最后执行时间
lastExecTime = currentTime;
// 执行函数
func.apply(this, args);
};
}
/**
*
* @param obj
* @returns
*/
export function deepClone<T>(obj: T): T {
// 处理基本类型和 null
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理 Date 类型
if (obj instanceof Date) {
return new Date(obj.getTime()) as unknown as T;
}
// 处理 Array 类型
if (Array.isArray(obj)) {
return obj.map(item => deepClone(item)) as unknown as T;
}
// 处理对象类型
const clonedObj = {} as T;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
/**
* ID
* @param prefix
* @returns ID字符串
*/
export function generateId(prefix = 'id'): string {
const timestamp = Date.now().toString(36);
const randomStr = Math.random().toString(36).substring(2, 8);
return `${prefix}_${timestamp}_${randomStr}`;
}
/**
*
* @param num
* @param precision 2
* @returns
*/
export function formatNumber(num: number, precision = 2): string {
return Number(num).toLocaleString('zh-CN', {
minimumFractionDigits: precision,
maximumFractionDigits: precision,
});
}
/**
*
* @param price
* @returns ¥100.00
*/
export function formatPrice(price: number): string {
return `¥${formatNumber(price, 2)}`;
}
/**
*
* @param phone
* @returns
*/
export function validatePhone(phone: string): boolean {
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phone);
}
/**
*
* @param email
* @returns
*/
export function validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
/**
*
* @param idCard
* @returns
*/
export function validateIdCard(idCard: string): boolean {
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return idCardRegex.test(idCard);
}
/**
*
* @param src
* @returns Promise<{width: number, height: number}>
*/
export function getImageSize(src: string): Promise<{ width: number; height: number }> {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve({
width: img.width,
height: img.height,
});
};
img.onerror = reject;
img.src = src;
});
}
/**
*
* @param url
* @param filename
*/
export function downloadFile(url: string, filename?: string): void {
const link = document.createElement('a');
link.href = url;
if (filename) {
link.download = filename;
}
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
/**
*
* @param text
*/
export function copyToClipboard(text: string): Promise<void> {
if (navigator.clipboard) {
return navigator.clipboard.writeText(text);
}
// 兼容旧版浏览器
return new Promise((resolve, reject) => {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
resolve();
} catch (err) {
reject(err);
} finally {
document.body.removeChild(textarea);
}
});
}
/**
* URL参数
* @param url URL地址URL
* @returns
*/
export function getUrlParams(url?: string): Record<string, string> {
const urlObj = new URL(url || window.location.href);
const params: Record<string, string> = {};
urlObj.searchParams.forEach((value, key) => {
params[key] = value;
});
return params;
}
/**
* URL参数
* @param params
* @param replace false
*/
export function setUrlParams(params: Record<string, any>, replace = false): void {
const url = new URL(window.location.href);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.set(key, String(value));
} else {
url.searchParams.delete(key);
}
});
if (replace) {
window.history.replaceState({}, '', url.toString());
} else {
window.history.pushState({}, '', url.toString());
}
}
/**
*
* @param array
* @param size
* @returns
*/
export function chunk<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
/**
*
* @param array
* @param key
* @returns
*/
export function unique<T>(array: T[], key?: keyof T): T[] {
if (!key) {
return Array.from(new Set(array));
}
const seen = new Set();
return array.filter(item => {
const value = item[key];
if (seen.has(value)) {
return false;
}
seen.add(value);
return true;
});
}
/**
*
* @param array
* @param key
* @param order 'asc' | 'desc''asc'
* @returns
*/
export function sortBy<T>(array: T[], key: keyof T, order: 'asc' | 'desc' = 'asc'): T[] {
return [...array].sort((a, b) => {
const aVal = a[key];
const bVal = b[key];
if (aVal < bVal) {
return order === 'asc' ? -1 : 1;
}
if (aVal > bVal) {
return order === 'asc' ? 1 : -1;
}
return 0;
});
}
/**
*
* @param data
* @param options
* @returns
*/
export function arrayToTree<T>(
data: T[],
options: {
id?: keyof T;
parentId?: keyof T;
children?: string;
}
): T[] {
const { id = 'id', parentId = 'parentId', children = 'children' } = options;
const tree: T[] = [];
const map = new Map<string, T & { [key: string]: any }>();
// 建立索引
data.forEach(item => {
map.set(String(item[id]), { ...item, [children]: [] });
});
// 构建树
data.forEach(item => {
const node = map.get(String(item[id]))!;
const parentIdValue = item[parentId];
if (parentIdValue && map.has(String(parentIdValue))) {
map.get(String(parentIdValue))![children].push(node);
} else {
tree.push(node as unknown as T);
}
});
return tree;
}
/**
* Promise
* @param ms
* @returns Promise
*/
export function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
*
* @param fn
* @param retries 3
* @param delay 1000ms
* @returns Promise
*/
export async function retry<T>(
fn: () => Promise<T>,
retries = 3,
delayMs = 1000
): Promise<T> {
let lastError: Error;
for (let i = 0; i <= retries; i++) {
try {
return await fn();
} catch (err) {
lastError = err as Error;
if (i === retries) {
throw lastError;
}
await delay(delayMs);
}
}
throw lastError!;
}
/**
*
* @returns
*/
export function getRandomColor(): string {
return `#${Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')}`;
}
/**
*
* @param point1 1 {x, y}
* @param point2 2 {x, y}
* @returns
*/
export function getDistance(
point1: { x: number; y: number },
point2: { x: number; y: number }
): number {
const dx = point2.x - point1.x;
const dy = point2.y - point1.y;
return Math.sqrt(dx * dx + dy * dy);
}