import { scaleLinear } from 'd3'; import tinycolor from 'tinycolor2'; import { ComponentType, Defs, getElementBounds, Group, Rect } from '../../jsx'; import { ItemDesc, ItemIcon, ItemLabel, ItemValue } from '../components'; import { getItemProps } from '../utils'; import { registerItem } from './registry'; import type { BaseItemProps } from './types'; export interface ProgressCardProps extends BaseItemProps { width?: number; height?: number; iconSize?: number; gap?: number; progressHeight?: number; borderRadius?: number; } export const ProgressCard: ComponentType = (props) => { const [ { datum, data, indexes, width = 280, height = 120, iconSize = 32, gap = 12, progressHeight = 8, borderRadius = 12, positionH = 'normal', themeColors, valueFormatter = (v: any) => `${v}%`, }, restProps, ] = getItemProps(props, [ 'width', 'height', 'iconSize', 'gap', 'progressHeight', 'borderRadius', ]); const value = datum.value; const displayValue = value ?? 0; const maxValue = Math.max(...data.items.map((item) => item.value ?? 0), 100); const progressWidth = width - 2 * gap; // 计算进度条的填充宽度 const progressScale = scaleLinear() .domain([0, maxValue]) .range([0, progressWidth]); const progressFillWidth = progressScale(displayValue); // 生成唯一的渐变ID const gradientId = `${themeColors.colorPrimary}-progress`; const progressBgId = `${themeColors.colorPrimaryBg}-progress-bg`; // 获取元素边界用于布局计算 const labelBounds = getElementBounds(); // 计算布局位置 const contentY = gap; const iconX = positionH === 'flipped' ? width - gap - iconSize : gap; const iconY = contentY; const textStartX = positionH === 'flipped' ? gap : iconSize + 2 * gap; const textWidth = width - iconSize - 3 * gap; const textY = iconY; const progressY = height - gap - progressHeight; const hasValue = value !== undefined; return ( {/* 定义渐变 */} {/* 卡片背景 */} {/* 图标 */} {/* 标签 */} {datum.label} {/* 数值 */} {hasValue && ( )} {/* 描述 */} {datum.desc} {/* 进度条背景 */} {/* 进度条填充 */} ); }; registerItem('progress-card', { component: ProgressCard, composites: ['icon', 'label', 'value', 'desc'], });