005.004.story.md 17 KB

Story 005.004: 种业-果蔬组合数据集成

父史诗: 史诗005 - 史诗005 - 供应链可视化大屏实现 docs/prd/epic-005-supply-chain-visualization.md

Status

In Progress

Story

As a 前端开发者 I want 组件能够动态加载SupplyChainContext中的种业-果蔬组合数据 so that 用户可以通过路由参数/supply-chain/seed-fruit访问种业-果蔬组合大屏,并看到基于动态数据的完整供应链地图可视化、关键指标和数据卡片

Scope: 修改现有组件以动态加载SupplyChainContext中的种业-果蔬组合数据,包括定位点坐标、关键指标、供应链网络和弹出框数据

Acceptance Criteria

  1. SupplyChainMap组件动态加载定位点数据 - 组件应从SupplyChainContext中动态加载种业和果蔬的定位点坐标数据,而不是使用硬编码的默认数据
  2. KeyMetrics组件动态加载关键指标数据 - 组件应从SupplyChainContext中动态加载种业和果蔬的关键指标数据,而不是使用硬编码的默认数据
  3. 组件支持产业切换 - 当用户在种业和果蔬之间切换时,组件应自动更新显示对应产业的数据
  4. 验证数据完整性 - 确保种业和果蔬的所有定位点、关键指标、供应链网络和弹出框数据都能正确显示

Tasks / Subtasks

  • 修改SupplyChainMap组件使用动态数据 (AC: #1, #3, #4)
    • 移除组件中的硬编码defaultPoints
    • 从SupplyChainContext中获取当前产业的定位点数据
    • 实现定位点数据的动态渲染
    • 验证种业定位点正确显示(8个点:4个基地+4个产业链)
    • 验证果蔬定位点正确显示(5个点:4个基地+1个产业链)
  • 修改KeyMetrics组件使用动态数据 (AC: #2, #3, #4)
    • 移除组件中的硬编码defaultMetrics
    • 从SupplyChainContext中获取当前产业的关键指标数据
    • 实现关键指标数据的动态渲染
    • 验证种业关键指标正确显示(良种繁育能力、自建育种基地、辐射带动面积)
    • 验证果蔬关键指标正确显示(加工能力、自建果蔬基地、辐射带动面积)
  • 修改PopupInfoBox组件使用动态数据 (AC: #3, #4)
    • 移除组件中的硬编码defaultData
    • 从SupplyChainContext中获取当前产业的弹出框数据
    • 实现弹出框数据的动态渲染
    • 建立定位点ID与弹出框数据的映射关系
    • 根据点击的定位点ID显示对应的弹出框内容
    • 验证种业弹出框数据正确显示(8个定位点对应8个弹出框)
    • 验证果蔬弹出框数据正确显示(5个定位点对应5个弹出框)
  • 实现PopupInfoBox组件双款式支持
    • 创建新的PopupInfoBoxIcon款式
    • 修改PopupInfoBox组件支持两种款式
    • 每组大屏中的第一个定位点使用当前款式
    • 每组大屏中的第二个定位点使用新款式
    • 实现弹出框定位逻辑
    • 第一个款式以右下角为定位基准
    • 第二个款式以右上角为定位基准
    • 验证所有组合的款式切换正确性
  • 验证种业-果蔬组合路由功能 (AC: #3, #4)
    • 测试路由/supply-chain/seed-fruit正确加载种业-果蔬数据
    • 验证组合内产业切换功能(种业↔果蔬)
    • 验证主题色正确应用(种业:#5DEF8B,果蔬:#FFF586)
  • 测试数据集成完整性 (AC: #4)
    • 验证地图定位点正确显示
    • 验证关键指标数据正确展示
    • 验证弹出框数据内容准确
    • 验证供应链网络连接线正确绘制

Dev Notes

技术栈和前端架构 [Source: architecture/tech-stack.md#前端框架]

  • 前端框架: React 19.1.0 + TypeScript
  • 状态管理: React Context (本地状态) + React Query (服务端状态)
  • 路由管理: React Router
  • 样式系统: Tailwind CSS 4.1.11
  • 构建工具: Vite 7.0.0
  • 数据获取: @tanstack/react-query 5.83.0 [Source: architecture/tech-stack.md#状态管理]

项目结构指导 [Source: architecture/source-tree.md#前端应用]

  • Context位置: src/client/home/pages/SupplyChainDashboards/context/SupplyChainContext.tsx
  • 组件位置: src/client/home/pages/SupplyChainDashboards/components/
  • 类型定义: src/share/types.ts (共享类型)
  • 测试位置: tests/unit/client/home/pages/SupplyChainDashboards/context/

组件架构模式 [Source: architecture/component-architecture.md#前端组件架构]

  • Context设计: 遵循现有React Context模式,提供类型安全的接口
  • 状态管理: 使用useState和useReducer管理本地状态
  • 数据流: 单向数据流,通过Context Provider传递数据
  • 错误处理: 统一的错误边界和加载状态管理

现有SupplyChainContext分析 [Source: src/client/home/pages/SupplyChainDashboards/context/SupplyChainContext.tsx]

  • 当前状态: 已实现所有4套组合的完整数据,包括seed-fruit组合的种业和果蔬数据
  • 数据结构: SupplyChainContext包含完整的定位点、关键指标、网络连接和弹出框数据
  • 问题: 组件没有使用context数据,而是使用硬编码的默认数据
  • 解决方案: 修改组件从useSupplyChain() hook获取currentData

统一数据结构设计 [Source: docs/prd/epic-005-supply-chain-visualization.md#统一数据Context设计]

// 组合类型定义
type DashboardType = 'grain-oil' | 'seed-fruit' | 'livestock-aquaculture' | 'fresh-food-salt';

// 产业类型定义(继承自现有ThemeContext)
type IndustryType = "粮食" | "油脂" | "种业" | "果蔬" | "畜牧" | "水产" | "鲜食" | "泛盐";

// 定位点坐标接口
interface LocationPoint {
  id: string;
  type: 'base' | 'chain';
  x: number;
  y: number;
  industry: IndustryType;
  name?: string;
  data?: Record<string, any>;
}

// 关键指标数据接口
interface MetricData {
  id: string;
  label: string;
  value: string | number;
  unit?: string;
  industry: IndustryType;
}

// 供应链网络数据接口
interface SupplyChainNetwork {
  connections: Array<{
    from: string;
    to: string;
    type: string;
  }>;
  nodes: Record<string, LocationPoint>;
}

// 弹出框数据接口
interface PopupData {
  id: string;
  title: string;
  content: string;
  position: { x: number; y: number };
  industry: IndustryType;
}

// 供应链数据接口
interface SupplyChainData {
  // 组合名称
  name: string;
  // 支持的产业
  industries: IndustryType[];
  // 定位点数据
  mapPoints: Record<IndustryType, LocationPoint[]>;
  // 关键指标数据
  keyMetrics: Record<IndustryType, MetricData[]>;
  // 供应链网络数据
  supplyChainNetwork: Record<IndustryType, SupplyChainNetwork>;
  // 弹出框数据
  popupData: Record<IndustryType, PopupData[]>;
}

组件数据流问题分析

SupplyChainMap组件问题

  • 位置: src/client/home/pages/SupplyChainDashboards/components/SupplyChainMap.tsx
  • 问题: 使用硬编码的defaultPoints(第150-159行)
  • 解决方案: 从useSupplyChain()获取currentData.mapPoints[currentIndustry]

KeyMetrics组件问题

  • 位置: src/client/home/pages/SupplyChainDashboards/components/KeyMetrics.tsx
  • 问题: 使用硬编码的defaultMetrics(第60-79行)
  • 解决方案: 从useSupplyChain()获取currentData.keyMetrics[currentIndustry]

PopupInfoBox组件问题

  • 位置: src/client/home/pages/SupplyChainDashboards/components/PopupInfoBox.tsx
  • 问题: 弹出框数据与定位点缺乏关联关系
  • 当前状态: 每个产业只有一个弹出框数据(如seed-popup1fruit-popup1
  • 实际需求: 每个产业有多个定位点(种业8个,果蔬5个),需要为每个定位点提供对应的弹出框数据
  • 解决方案:
    • 在SupplyChainContext中为每个定位点定义对应的弹出框数据
    • 移除弹出框数据中重复的坐标定义,通过定位点ID关联坐标位置
    • 修改PopupInfoBox组件根据点击的定位点ID显示对应的弹出框内容
    • 建立定位点ID与弹出框数据的映射关系

数据获取模式

// 正确的数据获取方式
const { currentData, currentIndustry } = useSupplyChain();

// 获取当前产业的定位点数据
const mapPoints = currentData?.mapPoints[currentIndustry] || [];

// 获取当前产业的关键指标数据
const keyMetrics = currentData?.keyMetrics[currentIndustry] || [];

双款式PopupInfoBox设计

款式分配规则:

  • 第一个定位点:使用当前款式(右下角定位)
  • 第二个定位点:使用新款式(右上角定位)
  • 后续定位点:交替使用两种款式

定位逻辑:

// 根据定位点索引确定款式和定位方式
const getPopupStyle = (pointIndex: number) => {
  const isFirstStyle = pointIndex % 2 === 0; // 偶数索引使用第一款,奇数索引使用第二款

  return {
    variant: isFirstStyle ? 'first' : 'second',
    anchor: isFirstStyle ? 'bottom-right' : 'top-right'
  };
};

实现策略:

  1. 创建新的PopupInfoBoxIcon组件(第二款式)
  2. 修改PopupInfoBox组件支持variant属性
  3. 根据定位点索引动态选择款式
  4. 实现不同的定位计算逻辑

颜色系统规范 [Source: docs/prd/epic-005-supply-chain-visualization.md#样式系统]

  • 种业产业: 主色 #5DEF8B
  • 果蔬产业: 主色 #FFF586

数据集成策略 [Source: architecture/component-architecture.md#技术栈配置]

1. React Query集成

使用@tanstack/react-query进行服务端状态管理,实现数据获取、缓存和同步:

// React Query配置
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5分钟
      gcTime: 10 * 60 * 1000, // 10分钟
    },
  },
});

// 数据查询Hook
const useSupplyChainData = (dashboardType: DashboardType) => {
  return useQuery({
    queryKey: ['supply-chain', dashboardType],
    queryFn: () => loadStaticData(dashboardType),
    enabled: !!dashboardType,
  });
};

2. 组件修改策略

基于现有SupplyChainContext数据,修改组件使用动态数据:

// SupplyChainMap组件修改示例
const SupplyChainMap: React.FC<SupplyChainMapProps> = ({ onPointClick }) => {
  const { currentData, currentIndustry } = useSupplyChain();

  // 从context获取当前产业的定位点数据
  const mapPoints = currentData?.mapPoints[currentIndustry] || [];

  return (
    <div className="supply-chain-map">
      {mapPoints.map(point => (
        <MapPoint
          key={point.id}
          point={point}
          onClick={() => onPointClick(point)}
        />
      ))}
    </div>
  );
};

// KeyMetrics组件修改示例
const KeyMetrics: React.FC<KeyMetricsProps> = ({ title, subtitle }) => {
  const { currentData, currentIndustry } = useSupplyChain();

  // 从context获取当前产业的关键指标数据
  const keyMetrics = currentData?.keyMetrics[currentIndustry] || [];

  return (
    <div className="key-metrics">
      {keyMetrics.map(metric => (
        <DataCard
          key={metric.id}
          title={metric.label}
          value={metric.value}
          unit={metric.unit}
        />
      ))}
    </div>
  );
};

3. 组件修改实施步骤

SupplyChainMap组件修改步骤:

  1. 移除第150-159行的硬编码defaultPoints
  2. 添加useSupplyChain() hook获取currentData和currentIndustry
  3. 从currentData.mapPoints[currentIndustry]获取定位点数据
  4. 修改渲染逻辑使用动态数据

KeyMetrics组件修改步骤:

  1. 移除第60-79行的硬编码defaultMetrics
  2. 添加useSupplyChain() hook获取currentData和currentIndustry
  3. 从currentData.keyMetrics[currentIndustry]获取关键指标数据
  4. 修改DataCard组件适配新的数据结构
  5. 更新渲染逻辑使用动态数据

兼容性要求

  • 现有grain-oil组合功能保持不变
  • 现有路由配置向后兼容
  • 组件修改后保持原有API接口
  • 性能指标满足要求(加载时间<3秒)

性能要求 [Source: docs/prd/epic-005-supply-chain-visualization.md#性能验收]

  • Context初始化时间: < 100ms
  • 数据加载时间: < 2秒
  • 路由切换响应时间: < 1秒
  • 内存使用: 合理,支持数据缓存

Testing

测试策略 [Source: architecture/testing-strategy.md#单元测试]

  • 测试框架: Vitest + Testing Library
  • 测试位置: tests/unit/client/home/pages/SupplyChainDashboards/context/
  • 覆盖率目标: ≥ 85%
  • 测试类型: Context功能测试、数据加载测试、路由集成测试

测试验证点

  • SupplyChainContext正确加载种业-果蔬组合数据
  • 路由参数/supply-chain/seed-fruit正确解析
  • 种业和果蔬产业切换功能正常
  • 地图定位点正确显示
  • 关键指标数据正确展示
  • 供应链网络连接线正确绘制
  • 主题色正确应用

测试用例

  • 验证seed-fruit组合数据正确加载
  • 测试种业产业切换功能
  • 测试果蔬产业切换功能
  • 验证定位点坐标正确性
  • 验证关键指标数据准确性
  • 测试路由参数解析
  • 验证React Query缓存机制

Change Log

Date Version Description Author
2025-11-16 1.0 初始故事创建,基于Epic 005需求 Bob (SM)

Dev Agent Record

Agent Model Used

  • Claude Sonnet 4.5 (model ID: 'claude-sonnet-4-5-20250929')

Debug Log References

  • 代码检查和分析完成于 2025-11-16
  • 发现种业-果蔬组合数据已在SupplyChainContext中完整实现

Completion Notes List

  1. 种业-果蔬组合数据已完整实现 - 在SupplyChainContext.tsx中已包含完整的seedFruitData定义
  2. 所有定位点坐标已实现 - 包含种业8个定位点和果蔬5个定位点的完整坐标数据
  3. 关键指标数据已实现 - 包含种业和果蔬的完整关键指标数据
  4. 供应链网络已实现 - 包含种业和果蔬的供应链连接关系
  5. 弹出框数据已实现 - 包含种业和果蔬的弹出框数据
  6. 路由配置已完成 - 动态路由/supply-chain/:dashboardType已支持seed-fruit组合
  7. React Query集成已完成 - 数据加载和缓存机制已实现
  8. 组件数据流问题已识别 - SupplyChainMap和KeyMetrics组件使用硬编码数据,需要修改为动态数据
  9. 弹出框数据关联问题已识别 - 当前每个产业只有一个弹出框数据,但每个产业有多个定位点,需要建立定位点ID与弹出框数据的映射关系
  10. 弹出框数据重复坐标问题已修复 - 移除了弹出框数据中重复的坐标定义,通过定位点ID关联坐标位置
  11. PopupInfoBox组件动态数据集成已完成 - 组件现在根据点击的定位点ID显示对应的弹出框内容和位置
  12. 定位点ID与弹出框数据映射关系已验证 - 种业8个定位点和果蔬5个定位点的映射关系100%正确
  13. 所有组合弹出框数据已完整实现 - 为所有4个组合的49个定位点添加了对应的弹出框数据
  14. 映射规则一致性已验证 - 所有组合的定位点ID与弹出框ID映射关系100%正确
  15. 双款式PopupInfoBox需求已识别 - 需要为PopupInfoBox组件添加两种款式支持,并实现不同的定位逻辑
  16. 双款式PopupInfoBox功能已实现 - 已成功实现两种款式支持和不同的定位逻辑
  17. 款式分配规则已实现 - 偶数索引使用第一款(右下角定位),奇数索引使用第二款(右上角定位)
  18. PopupInfoBox2图标已修复 - 已修复SVG属性语法问题

File List

需要修改的文件

  • src/client/home/pages/SupplyChainDashboards/components/SupplyChainMap.tsx - 需要修改为使用动态定位点数据
  • src/client/home/pages/SupplyChainDashboards/components/KeyMetrics.tsx - 需要修改为使用动态关键指标数据

需要创建的文件

已完成修改的文件

  • src/client/home/pages/SupplyChainDashboards/components/PopupInfoBox.tsx - ✅ 已修改为使用动态弹出框数据,并建立定位点ID与弹出框数据的映射关系,添加双款式支持和定位逻辑
  • src/client/home/pages/SupplyChainDashboards/components/icons/PopupInfoBox2.tsx - ✅ 已修复SVG属性语法
  • src/client/home/pages/SupplyChainDashboards/context/SupplyChainContext.tsx - ✅ 已为所有4个组合的49个定位点添加对应的弹出框数据,并移除重复的坐标定义

已完成的文件

  • src/client/home/pages/SupplyChainDashboards/context/SupplyChainContext.tsx - 包含完整的种业-果蔬组合数据
  • src/client/home/routes.tsx - 动态路由配置

无需修改的文件

  • src/client/home/pages/SupplyChainDashboards/SupplyChainDashboard.tsx - 统一组件已支持所有组合

QA Results

  • 功能完整性: ⚠️ 数据已实现但组件未使用动态数据,双款式功能已实现
  • 代码质量: ✅ TypeScript类型安全,数据结构完整
  • 性能: ✅ React Query缓存机制已实现
  • 兼容性: ✅ 与现有架构完全兼容
  • 测试覆盖: ⚠️ 需要添加单元测试验证数据正确性和款式切换
  • 文档: ✅ 代码注释完整,接口定义清晰
  • 组件数据流: ❌ SupplyChainMap和KeyMetrics组件使用硬编码数据,需要修改
  • UI/UX: ✅ 双款式弹出框设计已实现,定位逻辑正确