feat: 更新营养雷达图组件,优化营养分析卡片
- 在营养分析卡片中引入更新日期和图标,提升信息展示 - 修改营养维度标签,简化文本内容 - 更新雷达图组件,支持自定义尺寸和标签显示 - 增加尺寸配置,优化图表显示效果 - 更新饮食记录服务,添加更新时间字段以支持新功能
This commit is contained in:
@@ -4,25 +4,75 @@ import Svg, * as SvgLib from 'react-native-svg';
|
||||
|
||||
export type RadarCategory = { key: string; label: string };
|
||||
|
||||
export type RadarChartSize = 'small' | 'medium' | 'large' | number;
|
||||
|
||||
export type RadarChartProps = {
|
||||
categories: RadarCategory[];
|
||||
values: number[]; // 与 categories 一一对应
|
||||
maxValue?: number; // 默认 5
|
||||
size?: number; // 组件宽高,默认 260
|
||||
size?: RadarChartSize; // 组件宽高,默认 medium
|
||||
levels?: number; // 网格层数,默认 5
|
||||
showGrid?: boolean;
|
||||
showLabels?: boolean; // 是否显示指标文字
|
||||
};
|
||||
|
||||
export function RadarChart({ categories, values, maxValue = 5, size = 260, levels = 5, showGrid = true }: RadarChartProps) {
|
||||
const radius = size * 0.38;
|
||||
const cx = size / 2;
|
||||
const cy = size / 2;
|
||||
// 预设尺寸配置
|
||||
const SIZE_CONFIG = {
|
||||
small: { size: 116, fontSize: 8, labelOffset: -4, radiusRatio: 0.35 },
|
||||
medium: { size: 200, fontSize: 11, labelOffset: 15, radiusRatio: 0.38 },
|
||||
large: { size: 280, fontSize: 13, labelOffset: 18, radiusRatio: 0.4 },
|
||||
};
|
||||
|
||||
export function RadarChart({
|
||||
categories,
|
||||
values,
|
||||
maxValue = 5,
|
||||
size = 'medium',
|
||||
levels = 5,
|
||||
showGrid = true,
|
||||
showLabels = true
|
||||
}: RadarChartProps) {
|
||||
// 解析尺寸配置
|
||||
const config = useMemo(() => {
|
||||
if (typeof size === 'number') {
|
||||
// 自定义尺寸
|
||||
const baseSize = Math.max(80, Math.min(400, size));
|
||||
let fontSize = 11;
|
||||
let labelOffset = 15;
|
||||
let radiusRatio = 0.38;
|
||||
|
||||
// 根据尺寸自适应调整
|
||||
if (baseSize < 140) {
|
||||
fontSize = 9;
|
||||
labelOffset = 12;
|
||||
radiusRatio = 0.35;
|
||||
} else if (baseSize > 240) {
|
||||
fontSize = 13;
|
||||
labelOffset = 18;
|
||||
radiusRatio = 0.4;
|
||||
}
|
||||
|
||||
return { size: baseSize, fontSize, labelOffset, radiusRatio };
|
||||
}
|
||||
|
||||
// 预设尺寸
|
||||
return SIZE_CONFIG[size] || SIZE_CONFIG.medium;
|
||||
}, [size]);
|
||||
|
||||
const { size: actualSize, fontSize, labelOffset, radiusRatio } = config;
|
||||
const radius = actualSize * radiusRatio;
|
||||
const cx = actualSize / 2;
|
||||
const cy = actualSize / 2;
|
||||
const count = categories.length;
|
||||
|
||||
const points = useMemo(() => {
|
||||
return categories.map((_, i) => {
|
||||
const angle = (Math.PI * 2 * i) / count - Math.PI / 2; // 顶部起点
|
||||
return { angle, x: (v: number) => cx + Math.cos(angle) * v, y: (v: number) => cy + Math.sin(angle) * v };
|
||||
return {
|
||||
angle,
|
||||
x: (v: number) => cx + Math.cos(angle) * v,
|
||||
y: (v: number) => cy + Math.sin(angle) * v
|
||||
};
|
||||
});
|
||||
}, [categories, count, cx, cy]);
|
||||
|
||||
@@ -44,9 +94,12 @@ export function RadarChart({ categories, values, maxValue = 5, size = 260, level
|
||||
return polys;
|
||||
}, [levels, radius, points]);
|
||||
|
||||
// 检查是否有足够的空间显示文字
|
||||
const hasEnoughSpace = actualSize >= 100;
|
||||
|
||||
return (
|
||||
<View style={{ width: size, height: size }}>
|
||||
<Svg width={size} height={size}>
|
||||
<View style={{ width: actualSize, height: actualSize }}>
|
||||
<Svg width={actualSize} height={actualSize}>
|
||||
<SvgLib.Defs>
|
||||
<SvgLib.LinearGradient id="radarFill" x1="0" y1="0" x2="1" y2="1">
|
||||
<SvgLib.Stop offset="0%" stopColor="#BBF246" stopOpacity={0.32} />
|
||||
@@ -79,14 +132,22 @@ export function RadarChart({ categories, values, maxValue = 5, size = 260, level
|
||||
</SvgLib.G>
|
||||
)}
|
||||
|
||||
{categories.map((c, i) => {
|
||||
const r = radius + 18;
|
||||
{showLabels && hasEnoughSpace && categories.map((c, i) => {
|
||||
const r = radius + labelOffset;
|
||||
const x = points[i].x(r);
|
||||
const y = points[i].y(r);
|
||||
const anchor = Math.cos(points[i].angle) > 0.2 ? 'start' : Math.cos(points[i].angle) < -0.2 ? 'end' : 'middle';
|
||||
const dy = Math.sin(points[i].angle) > 0.6 ? 10 : Math.sin(points[i].angle) < -0.6 ? -4 : 4;
|
||||
|
||||
return (
|
||||
<SvgLib.Text key={`label-${c.key}`} x={x} y={y + dy} fontSize={11} fill="#192126" textAnchor={anchor}>
|
||||
<SvgLib.Text
|
||||
key={`label-${c.key}`}
|
||||
x={x}
|
||||
y={y + dy}
|
||||
fontSize={fontSize}
|
||||
fill="#192126"
|
||||
textAnchor={anchor}
|
||||
>
|
||||
{c.label}
|
||||
</SvgLib.Text>
|
||||
);
|
||||
@@ -99,6 +160,4 @@ export function RadarChart({ categories, values, maxValue = 5, size = 260, level
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({});
|
||||
|
||||
|
||||
const styles = StyleSheet.create({});
|
||||
Reference in New Issue
Block a user