All files / app/(tabs)/scanner/components/results/AiReport NutritionPieChart.tsx

94.11% Statements 16/17
84.61% Branches 11/13
66.66% Functions 2/3
93.33% Lines 14/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96                          1x     3x   3x   2x   2x                                                             1x                             3x               2x 2x     2x 7x 7x 7x                   2x    
/**
 * 营养成分饼状图组件
 */
import { Dimensions } from 'react-native';
import { PieChart } from 'react-native-chart-kit';
import { Card, Text, XStack, YStack } from 'tamagui';
import { IconSymbol } from '@/src/components/ui/IconSymbol';
import type { GenerateReportResponse } from '@/src/services/api';
 
interface NutritionPieChartProps {
  report: GenerateReportResponse;
}
 
const COLORS = ['#0088FE', '#FF8042', '#00C49F', '#FFBB28', '#8884D8', '#82ca9d'];
 
export function NutritionPieChart({ report }: NutritionPieChartProps) {
  const chartData = buildChartData(report);
 
  if (chartData.length === 0) return null;
 
  const screenWidth = Dimensions.get('window').width;
 
  return (
    <Card bordered>
      <Card.Header padded>
        <YStack gap="$3">
          <XStack alignItems="center" gap="$2">
            <IconSymbol name="chart.pie.fill" size={20} color="$purple10" />
            <Text fontSize="$5" fontWeight="600">
              营养成分占比
            </Text>
          </XStack>
          <YStack alignItems="center" paddingVertical="$3">
            <PieChart
              data={chartData}
              width={screenWidth - 64}
              height={220}
              chartConfig={{
                color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
              }}
              accessor="value"
              backgroundColor="transparent"
              paddingLeft="15"
              absolute
            />
          </YStack>
        </YStack>
      </Card.Header>
    </Card>
  );
}
 
/** 营养字段到中文名称的映射 */
const NUTRIENT_LABELS: Record<string, string> = {
  protein: '粗蛋白',
  crude_protein: '粗蛋白',
  fat: '粗脂肪',
  crude_fat: '粗脂肪',
  carbohydrates: '碳水化合物',
  fiber: '粗纤维',
  crude_fiber: '粗纤维',
  ash: '粗灰分',
  crude_ash: '粗灰分',
  moisture: '水分',
  others: '其他',
};
 
function buildChartData(report: GenerateReportResponse) {
  if (!report.percentage || !report.percent_data) return [];
 
  const data: {
    name: string;
    value: number;
    color: string;
    legendFontColor: string;
    legendFontSize: number;
  }[] = [];
  let colorIndex = 0;
 
  // 从 percent_data 动态获取营养数据
  for (const [key, value] of Object.entries(report.percent_data)) {
    Eif (value !== null && value > 0) {
      const label = NUTRIENT_LABELS[key] || key;
      data.push({
        name: label,
        value,
        color: COLORS[colorIndex++ % COLORS.length],
        legendFontColor: '#7F7F7F',
        legendFontSize: 12,
      });
    }
  }
 
  return data;
}