/* * @Author: RenXiaoDong * @Date: 2025-06-27 17:36:31 */ import dayjs from 'dayjs'; export function toFixed(num: number | string, n: number): number { return parseFloat(parseFloat(num.toString()).toFixed(n)); } export function isNotData(n: number): boolean { if (n === undefined) { return true; } return n === -2147483648; } export function splitNumber(num: number): string | number { if (!num) { return num; } const parts = num.toString().split('.'); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); return parts.join('.'); } export function formatNumberShow(...args: any[]): string | number { const [_args] = args; const { value, len = 2, split = true, showExactValue = false } = typeof _args === 'object' ? _args : { value: _args }; const getNumber = (value: number) => { return split ? splitNumber(value) : value; }; if (isNotData(value)) { return '-'; } if (value < 0) { return `-${formatNumberShow({ value: -value, len, split, showExactValue, })}`; } if (showExactValue) { return getNumber(toFixed(value, len)); } if (value < 10000) { return getNumber(toFixed(value, len)); } else if (value < 100000000) { const _n = Math.round((value / 10000) * 100) / 100; return split ? `${splitNumber(_n)}w` : `${toFixed(_n, len)}w`; } else { const _n = Math.round((value / 100000000) * 100) / 100; return split ? `${splitNumber(_n)}亿` : `${toFixed(_n, len)}亿`; } } export function formatTableField(fieldItem: any, rowValue: any, showExactValue = false) { // 获取嵌套属性值的函数 const getNestedValue = (obj: any, path: string) => { if (!obj || !path) return undefined; // 如果路径包含点号,说明是链式取值 if (path.includes('.')) { return path.split('.').reduce((current, key) => { return current && current[key] !== undefined ? current[key] : undefined; }, obj); } // 普通属性取值 return obj[path]; }; const _getValue = (value: any) => { if (!isNumber(value)) return value || '-'; return formatNumberShow({ value, showExactValue }); }; // 使用链式取值获取数据 const rawValue = getNestedValue(rowValue, fieldItem.dataIndex); const value = _getValue(rawValue ?? '-'); return `${fieldItem.prefix || ''}${value}${fieldItem.suffix || ''}`; } export function exactFormatTime(val: number, curYearFmt = 'MM-DD HH:mm:ss', otherYearFmt = 'YYYY-MM-DD HH:mm:ss') { if (!val) return '-'; const year = dayjs(val * 1000).year(); const currYear = dayjs().year(); const diff = year - currYear; const fmt = diff === 0 ? curYearFmt : otherYearFmt; return dayjs(val * 1000).format(fmt); } // 导出文件 export function downloadByUrl(url: string, filename?: string) { const a = document.createElement('a'); a.href = url; if (filename) { a.download = filename; } a.style.display = 'none'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } export function genRandomId() { return `id_${Date.now()}_${Math.floor(Math.random() * 10000)}`; } export function getImageMainColor(imageUrl: string): Promise { return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = 'Anonymous'; // 处理跨域图片 img.src = imageUrl; img.onload = () => { // 创建画布缩小图片尺寸以提高性能 const canvas = document.createElement('canvas'); const maxDimension = 100; // 最大尺寸为100px let width = img.width; let height = img.height; // 按比例缩小图片 if (width > height) { if (width > maxDimension) { height *= maxDimension / width; width = maxDimension; } } else { if (height > maxDimension) { width *= maxDimension / height; height = maxDimension; } } canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); if (!ctx) { reject(new Error('Canvas context not available')); return; } // 绘制缩小后的图片 ctx.drawImage(img, 0, 0, width, height); // 获取图片数据 const imageData = ctx.getImageData(0, 0, width, height); const data = imageData.data; // 使用中位数切分法提取主色调 const colorGroups = medianCut(data, 8); // 分成8组 // 找出最大的颜色组 let maxGroup = colorGroups[0]; for (let i = 1; i < colorGroups.length; i++) { if (colorGroups[i].count > maxGroup.count) { maxGroup = colorGroups[i]; } } // 计算组内平均颜色 const avgColor = { r: Math.round(maxGroup.sumR / maxGroup.count), g: Math.round(maxGroup.sumG / maxGroup.count), b: Math.round(maxGroup.sumB / maxGroup.count) }; resolve(`rgb(${avgColor.r},${avgColor.g},${avgColor.b})`); }; img.onerror = () => { reject(new Error('Failed to load image')); }; }); } /** * 中位数切分法进行色彩量化 * @param data 图像数据 * @param levels 切分级别 * @returns 颜色组 */ function medianCut(data: Uint8ClampedArray, levels: number): any[] { const colors = []; for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const a = data[i + 3]; // 跳过透明像素 if (a < 128) continue; colors.push({ r, g, b, count: 1, sumR: r, sumG: g, sumB: b }); } // 如果没有颜色数据,返回默认白色 if (colors.length === 0) { return [{ count: 1, sumR: 255, sumG: 255, sumB: 255 }]; } // 开始中位数切分 let colorGroups = [{ colors, count: colors.length, sumR: colors.reduce((sum, c) => sum + c.r, 0), sumG: colors.reduce((sum, c) => sum + c.g, 0), sumB: colors.reduce((sum, c) => sum + c.b, 0) }]; for (let i = 0; i < levels; i++) { const newGroups = []; for (const group of colorGroups) { if (group.colors.length <= 1) { newGroups.push(group); continue; } // 找出颜色范围最大的通道 const rMin = Math.min(...group.colors.map(c => c.r)); const rMax = Math.max(...group.colors.map(c => c.r)); const gMin = Math.min(...group.colors.map(c => c.g)); const gMax = Math.max(...group.colors.map(c => c.g)); const bMin = Math.min(...group.colors.map(c => c.b)); const bMax = Math.max(...group.colors.map(c => c.b)); const rRange = rMax - rMin; const gRange = gMax - gMin; const bRange = bMax - bMin; let sortChannel = 'r'; if (gRange > rRange && gRange > bRange) { sortChannel = 'g'; } else if (bRange > rRange && bRange > gRange) { sortChannel = 'b'; } // 按最大范围通道排序 group.colors.sort((a, b) => a[sortChannel] - b[sortChannel]); // 切分中位数 const mid = Math.floor(group.colors.length / 2); const group1 = group.colors.slice(0, mid); const group2 = group.colors.slice(mid); // 添加新组 newGroups.push({ colors: group1, count: group1.length, sumR: group1.reduce((sum, c) => sum + c.r, 0), sumG: group1.reduce((sum, c) => sum + c.g, 0), sumB: group1.reduce((sum, c) => sum + c.b, 0) }); newGroups.push({ colors: group2, count: group2.length, sumR: group2.reduce((sum, c) => sum + c.r, 0), sumG: group2.reduce((sum, c) => sum + c.g, 0), sumB: group2.reduce((sum, c) => sum + c.b, 0) }); } colorGroups = newGroups; } return colorGroups; }