|
@@ -0,0 +1,738 @@
|
|
|
|
|
+# Story 005.002: 统一数据Context和路由架构
|
|
|
|
|
+
|
|
|
|
|
+**父史诗**: 史诗005 - 供应链可视化大屏实现
|
|
|
|
|
+docs/prd/epic-005-supply-chain-visualization.md
|
|
|
|
|
+
|
|
|
|
|
+## Status
|
|
|
|
|
+Draft
|
|
|
|
|
+
|
|
|
|
|
+## Story
|
|
|
|
|
+**As a** 前端开发者
|
|
|
|
|
+**I want** 统一的供应链数据Context和动态路由架构
|
|
|
|
|
+**so that** 我可以通过路由参数动态加载4套组合大屏数据,实现组件复用和统一数据管理
|
|
|
|
|
+
|
|
|
|
|
+**Scope**: 扩展ThemeContext为SupplyChainContext,创建统一数据结构,实现路由参数解析,配置动态路由
|
|
|
|
|
+
|
|
|
|
|
+## Acceptance Criteria
|
|
|
|
|
+1. **扩展ThemeContext为SupplyChainContext** - 创建统一的供应链数据Context,支持4套组合和8个产业的数据管理
|
|
|
|
|
+2. **创建统一数据结构** - 定义定位点坐标、关键指标、供应链网络数据的统一接口
|
|
|
|
|
+3. **实现路由参数解析** - 通过React Router的useParams获取当前组合类型,动态加载对应数据
|
|
|
|
|
+4. **配置动态路由** - 在现有路由配置中添加4套组合的动态路由参数
|
|
|
|
|
+5. **优化数据加载机制** - 实现数据缓存和动态加载,支持路由切换时的性能优化
|
|
|
|
|
+
|
|
|
|
|
+## Tasks / Subtasks
|
|
|
|
|
+- [ ] 扩展ThemeContext为SupplyChainContext (AC: #1)
|
|
|
|
|
+ - [ ] 在`src/client/home/pages/SupplyChainDashboards/context/`目录下创建SupplyChainContext.tsx
|
|
|
|
|
+ - [ ] 扩展现有ThemeContext接口,添加组合类型和统一数据管理
|
|
|
|
|
+ - [ ] 实现组合切换和产业切换的状态管理
|
|
|
|
|
+ - [ ] 添加数据加载和缓存机制
|
|
|
|
|
+- [ ] 创建统一数据结构 (AC: #2)
|
|
|
|
|
+ - [ ] 定义组合类型接口:`DashboardType`
|
|
|
|
|
+ - [ ] 定义供应链数据接口:`SupplyChainData`
|
|
|
|
|
+ - [ ] 定义定位点坐标接口:`LocationPoint`
|
|
|
|
|
+ - [ ] 定义关键指标接口:`MetricData`
|
|
|
|
|
+ - [ ] 定义供应链网络接口:`SupplyChainNetwork`
|
|
|
|
|
+ - [ ] 定义弹出框数据接口:`PopupData`
|
|
|
|
|
+- [ ] 实现路由参数解析 (AC: #3)
|
|
|
|
|
+ - [ ] 分析现有路由配置结构
|
|
|
|
|
+ - [ ] 实现useParams获取组合类型
|
|
|
|
|
+ - [ ] 创建路由参数解析工具函数
|
|
|
|
|
+ - [ ] 添加参数验证和错误处理
|
|
|
|
|
+- [ ] 配置动态路由 (AC: #4)
|
|
|
|
|
+ - [ ] 修改`src/client/home/routes.tsx`中的路由配置
|
|
|
|
|
+ - [ ] 添加4套组合的动态路由:`/supply-chain/:dashboardType`
|
|
|
|
|
+ - [ ] 配置路由参数验证和默认值
|
|
|
|
|
+ - [ ] 集成到现有导航系统
|
|
|
|
|
+- [ ] 集成React Query和静态数据mock (AC: #5)
|
|
|
|
|
+ - [ ] 配置React Query QueryClient
|
|
|
|
|
+ - [ ] 创建静态数据mock文件,包含4套组合的完整数据定义
|
|
|
|
|
+ - [ ] 从PRD提取所有定位点坐标数据
|
|
|
|
|
+ - [ ] 实现useSupplyChainData查询Hook
|
|
|
|
|
+ - [ ] 集成React Query到SupplyChainContext
|
|
|
|
|
+ - [ ] 实现数据预加载和缓存策略
|
|
|
|
|
+ - [ ] 添加加载状态和错误处理
|
|
|
|
|
+ - [ ] 优化路由切换性能
|
|
|
|
|
+ - [ ] 实现数据懒加载机制
|
|
|
|
|
+- [ ] 集成测试和验证 (AC: #1-#5)
|
|
|
|
|
+ - [ ] 编写Context单元测试
|
|
|
|
|
+ - [ ] 测试路由参数解析功能
|
|
|
|
|
+ - [ ] 验证数据加载性能
|
|
|
|
|
+ - [ ] 测试组合切换功能
|
|
|
|
|
+
|
|
|
|
|
+## 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/routes.tsx`
|
|
|
|
|
+- **类型定义**: `src/share/types.ts` (共享类型)
|
|
|
|
|
+- **测试位置**: `tests/unit/client/home/pages/SupplyChainDashboards/context/`
|
|
|
|
|
+
|
|
|
|
|
+### 组件架构模式 [Source: architecture/component-architecture.md#前端组件架构]
|
|
|
|
|
+- **Context设计**: 遵循现有React Context模式,提供类型安全的接口
|
|
|
|
|
+- **状态管理**: 使用useState和useReducer管理本地状态
|
|
|
|
|
+- **数据流**: 单向数据流,通过Context Provider传递数据
|
|
|
|
|
+- **错误处理**: 统一的错误边界和加载状态管理
|
|
|
|
|
+
|
|
|
|
|
+### 现有ThemeContext分析 [Source: src/client/home/pages/SupplyChainDashboards/context/ThemeContext.tsx]
|
|
|
|
|
+- **当前状态**: 已实现产业类型定义和主题色管理
|
|
|
|
|
+- **扩展点**: 需要添加组合类型、数据加载、路由集成功能
|
|
|
|
|
+- **兼容性**: 保持现有ThemeContext接口不变,向后兼容
|
|
|
|
|
+
|
|
|
|
|
+### 现有路由配置分析 [Source: src/client/home/routes.tsx]
|
|
|
|
|
+- **当前路由**: 已有静态路由`/supply-chain/grain-oil`
|
|
|
|
|
+- **扩展策略**: 改为动态路由`/supply-chain/:dashboardType`
|
|
|
|
|
+- **参数验证**: 需要添加路由参数验证和默认值处理
|
|
|
|
|
+
|
|
|
|
|
+### 统一数据结构设计 [Source: docs/prd/epic-005-supply-chain-visualization.md#统一数据Context设计]
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 组合类型定义
|
|
|
|
|
+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[]>;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 扩展的Context接口
|
|
|
|
|
+interface SupplyChainContextType {
|
|
|
|
|
+ // 当前组合类型
|
|
|
|
|
+ currentDashboard: DashboardType;
|
|
|
|
|
+ // 当前产业
|
|
|
|
|
+ currentIndustry: IndustryType;
|
|
|
|
|
+ // 主题色
|
|
|
|
|
+ themeColor: string;
|
|
|
|
|
+ // 当前组合数据
|
|
|
|
|
+ currentData: SupplyChainData | null;
|
|
|
|
|
+ // 加载状态
|
|
|
|
|
+ isLoading: boolean;
|
|
|
|
|
+ // 错误信息
|
|
|
|
|
+ error: string | null;
|
|
|
|
|
+ // 设置方法
|
|
|
|
|
+ setDashboard: (dashboard: DashboardType) => void;
|
|
|
|
|
+ setIndustry: (industry: IndustryType) => void;
|
|
|
|
|
+ // 数据加载方法
|
|
|
|
|
+ loadDashboardData: (dashboard: DashboardType) => Promise<void>;
|
|
|
|
|
+ // 刷新数据
|
|
|
|
|
+ refreshData: () => Promise<void>;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 路由配置设计
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 路由参数配置
|
|
|
|
|
+const supplyChainRoutes = [
|
|
|
|
|
+ {
|
|
|
|
|
+ path: '/supply-chain/:dashboardType',
|
|
|
|
|
+ component: SupplyChainDashboard,
|
|
|
|
|
+ // 参数验证
|
|
|
|
|
+ validate: (params: { dashboardType: string }) => {
|
|
|
|
|
+ const validTypes: DashboardType[] = ['grain-oil', 'seed-fruit', 'livestock-aquaculture', 'fresh-food-salt'];
|
|
|
|
|
+ return validTypes.includes(params.dashboardType as DashboardType);
|
|
|
|
|
+ },
|
|
|
|
|
+ // 默认参数
|
|
|
|
|
+ defaultParams: { dashboardType: 'grain-oil' }
|
|
|
|
|
+ }
|
|
|
|
|
+];
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 数据集成策略 [Source: architecture/component-architecture.md#技术栈配置]
|
|
|
|
|
+
|
|
|
|
|
+#### 1. React Query集成
|
|
|
|
|
+使用@tanstack/react-query进行服务端状态管理,实现数据获取、缓存和同步:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 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. 静态数据mock定义 [Source: docs/prd/epic-005-supply-chain-visualization.md#Stories]
|
|
|
|
|
+基于现有粮食-油脂大屏数据和PRD中的坐标数据,定义4套组合的静态数据:
|
|
|
|
|
+
|
|
|
|
|
+**粮食-油脂组合**: 使用现有GrainOilDashboard中的数据
|
|
|
|
|
+
|
|
|
|
|
+**种业-果蔬组合**:
|
|
|
|
|
+- **种业定位点坐标**:
|
|
|
|
|
+ - base1[1142.89,717.84], base2[1664.25,530.82], base3[1435,527.14], base4[1203.07,514.31]
|
|
|
|
|
+ - chain1[1273.12,551.14], chain2[1403,597.5], chain3[1694.25,645.5], chain4[1237.87,761.84]
|
|
|
|
|
+- **果蔬定位点坐标**:
|
|
|
|
|
+ - base1[985,610.22], base2[1125.81,409.52], base3[1229.57,570], base4[1445,560.35]
|
|
|
|
|
+ - chain1[949.08,680.31]
|
|
|
|
|
+
|
|
|
|
|
+**畜牧-水产组合**:
|
|
|
|
|
+- **畜牧定位点坐标**:
|
|
|
|
|
+ - base1[1203.07,514.31]
|
|
|
|
|
+ - chain1[1273.12,551.14], chain2[1403,597.5], chain3[1694.25,645.5], chain4[1237.87,761.84]
|
|
|
|
|
+- **水产定位点坐标**:
|
|
|
|
|
+ - base1[1445,560.35]
|
|
|
|
|
+ - chain1[949.08,680.31], chain2[1178.36,418.4], chain3[1307.42,551.47], chain4[1403.37,603.77]
|
|
|
|
|
+
|
|
|
|
|
+**鲜食-泛盐组合**:
|
|
|
|
|
+- **鲜食定位点坐标**:
|
|
|
|
|
+ - base1[1203.07,514.31]
|
|
|
|
|
+ - chain1[1273.12,551.14], chain2[1403,597.5], chain3[1694.25,645.5], chain4[1237.87,761.84]
|
|
|
|
|
+- **泛盐定位点坐标**:
|
|
|
|
|
+ - base1[1445,560.35]
|
|
|
|
|
+ - chain1[949.08,680.31], chain2[1178.36,418.4], chain3[1319.74,441.51], chain4[1403.37,603.77]
|
|
|
|
|
+
|
|
|
|
|
+#### 3. 静态数据定义和加载策略 [Source: docs/figma-jsx/*.md]
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 静态数据定义
|
|
|
|
|
+const grainOilData: SupplyChainData = {
|
|
|
|
|
+ name: '粮食-油脂组合',
|
|
|
|
|
+ industries: ['粮食', '油脂'],
|
|
|
|
|
+ mapPoints: {
|
|
|
|
|
+ '粮食': [
|
|
|
|
|
+ { id: 'grain-base1', type: 'base', x: 1142.89, y: 717.84, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-base2', type: 'base', x: 1664.25, y: 530.82, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-base3', type: 'base', x: 1435, y: 527.14, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-base4', type: 'base', x: 1203.07, y: 514.31, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-chain2', type: 'chain', x: 1403, y: 597.5, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '粮食' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '油脂': [
|
|
|
|
|
+ { id: 'oil-base1', type: 'base', x: 985, y: 610.22, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-base2', type: 'base', x: 1125.81, y: 409.52, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-base3', type: 'base', x: 1229.57, y: 570, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-base4', type: 'base', x: 1445, y: 560.35, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-chain2', type: 'chain', x: 1178.36, y: 418.4, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-chain3', type: 'chain', x: 1307.42, y: 551.47, industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-chain4', type: 'chain', x: 1403.37, y: 603.77, industry: '油脂' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ keyMetrics: {
|
|
|
|
|
+ '粮食': [
|
|
|
|
|
+ { id: 'grain-metric1', label: '加工能力达', value: '200', unit: '万吨/年', industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-metric2', label: '自建优质水稻基地', value: '15', unit: '万亩', industry: '粮食' },
|
|
|
|
|
+ { id: 'grain-metric3', label: '辐射带动面积', value: '20', unit: '万亩', industry: '粮食' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '油脂': [
|
|
|
|
|
+ { id: 'oil-metric1', label: '加工能力达', value: '150', unit: '万吨/年', industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-metric2', label: '自建油料基地', value: '10', unit: '万亩', industry: '油脂' },
|
|
|
|
|
+ { id: 'oil-metric3', label: '辐射带动面积', value: '18', unit: '万亩', industry: '油脂' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ supplyChainNetwork: {
|
|
|
|
|
+ '粮食': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'grain-base1', to: 'grain-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'grain-base2', to: 'grain-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'grain-base3', to: 'grain-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'grain-base4', to: 'grain-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'grain-base1': { id: 'grain-base1', type: 'base', x: 1142.89, y: 717.84, industry: '粮食' },
|
|
|
|
|
+ 'grain-base2': { id: 'grain-base2', type: 'base', x: 1664.25, y: 530.82, industry: '粮食' },
|
|
|
|
|
+ 'grain-base3': { id: 'grain-base3', type: 'base', x: 1435, y: 527.14, industry: '粮食' },
|
|
|
|
|
+ 'grain-base4': { id: 'grain-base4', type: 'base', x: 1203.07, y: 514.31, industry: '粮食' },
|
|
|
|
|
+ 'grain-chain1': { id: 'grain-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '粮食' },
|
|
|
|
|
+ 'grain-chain2': { id: 'grain-chain2', type: 'chain', x: 1403, y: 597.5, industry: '粮食' },
|
|
|
|
|
+ 'grain-chain3': { id: 'grain-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '粮食' },
|
|
|
|
|
+ 'grain-chain4': { id: 'grain-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '粮食' }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ '油脂': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'oil-base1', to: 'oil-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'oil-base2', to: 'oil-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'oil-base3', to: 'oil-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'oil-base4', to: 'oil-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'oil-base1': { id: 'oil-base1', type: 'base', x: 985, y: 610.22, industry: '油脂' },
|
|
|
|
|
+ 'oil-base2': { id: 'oil-base2', type: 'base', x: 1125.81, y: 409.52, industry: '油脂' },
|
|
|
|
|
+ 'oil-base3': { id: 'oil-base3', type: 'base', x: 1229.57, y: 570, industry: '油脂' },
|
|
|
|
|
+ 'oil-base4': { id: 'oil-base4', type: 'base', x: 1445, y: 560.35, industry: '油脂' },
|
|
|
|
|
+ 'oil-chain1': { id: 'oil-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '油脂' },
|
|
|
|
|
+ 'oil-chain2': { id: 'oil-chain2', type: 'chain', x: 1178.36, y: 418.4, industry: '油脂' },
|
|
|
|
|
+ 'oil-chain3': { id: 'oil-chain3', type: 'chain', x: 1307.42, y: 551.47, industry: '油脂' },
|
|
|
|
|
+ 'oil-chain4': { id: 'oil-chain4', type: 'chain', x: 1403.37, y: 603.77, industry: '油脂' }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ popupData: {
|
|
|
|
|
+ '粮食': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'grain-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '江汉大米核心示范基地荆门/荆州/黄冈/孝感',
|
|
|
|
|
+ position: { x: 717.28, y: 273.13 },
|
|
|
|
|
+ industry: '粮食',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>20', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '6~15', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '油脂': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'oil-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '油脂核心示范基地',
|
|
|
|
|
+ position: { x: 814.45, y: 464.02 },
|
|
|
|
|
+ industry: '油脂',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>18', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '5~10', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const seedFruitData: SupplyChainData = {
|
|
|
|
|
+ name: '种业-果蔬组合',
|
|
|
|
|
+ industries: ['种业', '果蔬'],
|
|
|
|
|
+ mapPoints: {
|
|
|
|
|
+ '种业': [
|
|
|
|
|
+ { id: 'seed-base1', type: 'base', x: 1142.89, y: 717.84, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-base2', type: 'base', x: 1664.25, y: 530.82, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-base3', type: 'base', x: 1435, y: 527.14, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-base4', type: 'base', x: 1203.07, y: 514.31, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-chain2', type: 'chain', x: 1403, y: 597.5, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '种业' },
|
|
|
|
|
+ { id: 'seed-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '种业' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '果蔬': [
|
|
|
|
|
+ { id: 'fruit-base1', type: 'base', x: 985, y: 610.22, industry: '果蔬' },
|
|
|
|
|
+ { id: 'fruit-base2', type: 'base', x: 1125.81, y: 409.52, industry: '果蔬' },
|
|
|
|
|
+ { id: 'fruit-base3', type: 'base', x: 1229.57, y: 570, industry: '果蔬' },
|
|
|
|
|
+ { id: 'fruit-base4', type: 'base', x: 1445, y: 560.35, industry: '果蔬' },
|
|
|
|
|
+ { id: 'fruit-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '果蔬' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ keyMetrics: {
|
|
|
|
|
+ '种业': [
|
|
|
|
|
+ { id: 'seed-metric1', label: '良种繁育能力', value: '50', unit: '万公斤/年', industry: '种业' },
|
|
|
|
|
+ { id: 'seed-metric2', label: '自建育种基地', value: '8', unit: '万亩', industry: '种业' },
|
|
|
|
|
+ { id: 'seed-metric3', label: '辐射带动面积', value: '15', unit: '万亩', industry: '种业' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '果蔬': [
|
|
|
|
|
+ { id: 'fruit-metric1', label: '加工能力达', value: '80', unit: '万吨/年', industry: '果蔬' },
|
|
|
|
|
+ { id: 'fruit-metric2', label: '自建果蔬基地', value: '12', unit: '万亩', industry: '果蔬' },
|
|
|
|
|
+ { id: 'fruit-metric3', label: '辐射带动面积', value: '25', unit: '万亩', industry: '果蔬' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ supplyChainNetwork: {
|
|
|
|
|
+ '种业': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'seed-base1', to: 'seed-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'seed-base2', to: 'seed-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'seed-base3', to: 'seed-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'seed-base4', to: 'seed-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'seed-base1': { id: 'seed-base1', type: 'base', x: 1142.89, y: 717.84, industry: '种业' },
|
|
|
|
|
+ 'seed-base2': { id: 'seed-base2', type: 'base', x: 1664.25, y: 530.82, industry: '种业' },
|
|
|
|
|
+ 'seed-base3': { id: 'seed-base3', type: 'base', x: 1435, y: 527.14, industry: '种业' },
|
|
|
|
|
+ 'seed-base4': { id: 'seed-base4', type: 'base', x: 1203.07, y: 514.31, industry: '种业' },
|
|
|
|
|
+ 'seed-chain1': { id: 'seed-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '种业' },
|
|
|
|
|
+ 'seed-chain2': { id: 'seed-chain2', type: 'chain', x: 1403, y: 597.5, industry: '种业' },
|
|
|
|
|
+ 'seed-chain3': { id: 'seed-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '种业' },
|
|
|
|
|
+ 'seed-chain4': { id: 'seed-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '种业' }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ '果蔬': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'fruit-base1', to: 'fruit-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'fruit-base2', to: 'fruit-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'fruit-base3', to: 'fruit-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'fruit-base4', to: 'fruit-chain1', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'fruit-base1': { id: 'fruit-base1', type: 'base', x: 985, y: 610.22, industry: '果蔬' },
|
|
|
|
|
+ 'fruit-base2': { id: 'fruit-base2', type: 'base', x: 1125.81, y: 409.52, industry: '果蔬' },
|
|
|
|
|
+ 'fruit-base3': { id: 'fruit-base3', type: 'base', x: 1229.57, y: 570, industry: '果蔬' },
|
|
|
|
|
+ 'fruit-base4': { id: 'fruit-base4', type: 'base', x: 1445, y: 560.35, industry: '果蔬' },
|
|
|
|
|
+ 'fruit-chain1': { id: 'fruit-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '果蔬' }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ popupData: {
|
|
|
|
|
+ '种业': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'seed-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '种业核心示范基地',
|
|
|
|
|
+ position: { x: 717.28, y: 273.13 },
|
|
|
|
|
+ industry: '种业',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>15', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '4~8', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '果蔬': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'fruit-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '果蔬核心示范基地',
|
|
|
|
|
+ position: { x: 814.45, y: 464.02 },
|
|
|
|
|
+ industry: '果蔬',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>25', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '8~12', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const livestockAquacultureData: SupplyChainData = {
|
|
|
|
|
+ name: '畜牧-水产组合',
|
|
|
|
|
+ industries: ['畜牧', '水产'],
|
|
|
|
|
+ mapPoints: {
|
|
|
|
|
+ '畜牧': [
|
|
|
|
|
+ { id: 'livestock-base1', type: 'base', x: 1203.07, y: 514.31, industry: '畜牧' },
|
|
|
|
|
+ { id: 'livestock-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '畜牧' },
|
|
|
|
|
+ { id: 'livestock-chain2', type: 'chain', x: 1403, y: 597.5, industry: '畜牧' },
|
|
|
|
|
+ { id: 'livestock-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '畜牧' },
|
|
|
|
|
+ { id: 'livestock-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '畜牧' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '水产': [
|
|
|
|
|
+ { id: 'aquaculture-base1', type: 'base', x: 1445, y: 560.35, industry: '水产' },
|
|
|
|
|
+ { id: 'aquaculture-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '水产' },
|
|
|
|
|
+ { id: 'aquaculture-chain2', type: 'chain', x: 1178.36, y: 418.4, industry: '水产' },
|
|
|
|
|
+ { id: 'aquaculture-chain3', type: 'chain', x: 1307.42, y: 551.47, industry: '水产' },
|
|
|
|
|
+ { id: 'aquaculture-chain4', type: 'chain', x: 1403.37, y: 603.77, industry: '水产' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ keyMetrics: {
|
|
|
|
|
+ '畜牧': [
|
|
|
|
|
+ { id: 'livestock-metric1', label: '养殖规模', value: '100', unit: '万头/年', industry: '畜牧' },
|
|
|
|
|
+ { id: 'livestock-metric2', label: '自建养殖基地', value: '20', unit: '万亩', industry: '畜牧' },
|
|
|
|
|
+ { id: 'livestock-metric3', label: '辐射带动面积', value: '30', unit: '万亩', industry: '畜牧' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '水产': [
|
|
|
|
|
+ { id: 'aquaculture-metric1', label: '养殖规模', value: '50', unit: '万吨/年', industry: '水产' },
|
|
|
|
|
+ { id: 'aquaculture-metric2', label: '自建养殖基地', value: '15', unit: '万亩', industry: '水产' },
|
|
|
|
|
+ { id: 'aquaculture-metric3', label: '辐射带动面积', value: '22', unit: '万亩', industry: '水产' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ supplyChainNetwork: {
|
|
|
|
|
+ '畜牧': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'livestock-base1', to: 'livestock-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'livestock-base1', to: 'livestock-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'livestock-base1', to: 'livestock-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'livestock-base1', to: 'livestock-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'livestock-base1': { id: 'livestock-base1', type: 'base', x: 1203.07, y: 514.31, industry: '畜牧' },
|
|
|
|
|
+ 'livestock-chain1': { id: 'livestock-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '畜牧' },
|
|
|
|
|
+ 'livestock-chain2': { id: 'livestock-chain2', type: 'chain', x: 1403, y: 597.5, industry: '畜牧' },
|
|
|
|
|
+ 'livestock-chain3': { id: 'livestock-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '畜牧' },
|
|
|
|
|
+ 'livestock-chain4': { id: 'livestock-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '畜牧' }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ '水产': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'aquaculture-base1', to: 'aquaculture-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'aquaculture-base1', to: 'aquaculture-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'aquaculture-base1', to: 'aquaculture-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'aquaculture-base1', to: 'aquaculture-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'aquaculture-base1': { id: 'aquaculture-base1', type: 'base', x: 1445, y: 560.35, industry: '水产' },
|
|
|
|
|
+ 'aquaculture-chain1': { id: 'aquaculture-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '水产' },
|
|
|
|
|
+ 'aquaculture-chain2': { id: 'aquaculture-chain2', type: 'chain', x: 1178.36, y: 418.4, industry: '水产' },
|
|
|
|
|
+ 'aquaculture-chain3': { id: 'aquaculture-chain3', type: 'chain', x: 1307.42, y: 551.47, industry: '水产' },
|
|
|
|
|
+ 'aquaculture-chain4': { id: 'aquaculture-chain4', type: 'chain', x: 1403.37, y: 603.77, industry: '水产' }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ popupData: {
|
|
|
|
|
+ '畜牧': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'livestock-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '畜牧核心示范基地',
|
|
|
|
|
+ position: { x: 717.28, y: 273.13 },
|
|
|
|
|
+ industry: '畜牧',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>30', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '15~20', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '水产': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'aquaculture-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '水产核心示范基地',
|
|
|
|
|
+ position: { x: 814.45, y: 464.02 },
|
|
|
|
|
+ industry: '水产',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>22', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '10~15', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const freshFoodSaltData: SupplyChainData = {
|
|
|
|
|
+ name: '鲜食-泛盐组合',
|
|
|
|
|
+ industries: ['鲜食', '泛盐'],
|
|
|
|
|
+ mapPoints: {
|
|
|
|
|
+ '鲜食': [
|
|
|
|
|
+ { id: 'fresh-base1', type: 'base', x: 1203.07, y: 514.31, industry: '鲜食' },
|
|
|
|
|
+ { id: 'fresh-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '鲜食' },
|
|
|
|
|
+ { id: 'fresh-chain2', type: 'chain', x: 1403, y: 597.5, industry: '鲜食' },
|
|
|
|
|
+ { id: 'fresh-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '鲜食' },
|
|
|
|
|
+ { id: 'fresh-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '鲜食' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '泛盐': [
|
|
|
|
|
+ { id: 'salt-base1', type: 'base', x: 1445, y: 560.35, industry: '泛盐' },
|
|
|
|
|
+ { id: 'salt-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '泛盐' },
|
|
|
|
|
+ { id: 'salt-chain2', type: 'chain', x: 1178.36, y: 418.4, industry: '泛盐' },
|
|
|
|
|
+ { id: 'salt-chain3', type: 'chain', x: 1319.74, y: 441.51, industry: '泛盐' },
|
|
|
|
|
+ { id: 'salt-chain4', type: 'chain', x: 1403.37, y: 603.77, industry: '泛盐' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ keyMetrics: {
|
|
|
|
|
+ '鲜食': [
|
|
|
|
|
+ { id: 'fresh-metric1', label: '加工能力达', value: '60', unit: '万吨/年', industry: '鲜食' },
|
|
|
|
|
+ { id: 'fresh-metric2', label: '自建鲜食基地', value: '8', unit: '万亩', industry: '鲜食' },
|
|
|
|
|
+ { id: 'fresh-metric3', label: '辐射带动面积', value: '18', unit: '万亩', industry: '鲜食' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '泛盐': [
|
|
|
|
|
+ { id: 'salt-metric1', label: '加工能力达', value: '40', unit: '万吨/年', industry: '泛盐' },
|
|
|
|
|
+ { id: 'salt-metric2', label: '自建盐业基地', value: '6', unit: '万亩', industry: '泛盐' },
|
|
|
|
|
+ { id: 'salt-metric3', label: '辐射带动面积', value: '12', unit: '万亩', industry: '泛盐' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ supplyChainNetwork: {
|
|
|
|
|
+ '鲜食': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'fresh-base1', to: 'fresh-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'fresh-base1', to: 'fresh-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'fresh-base1', to: 'fresh-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'fresh-base1', to: 'fresh-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'fresh-base1': { id: 'fresh-base1', type: 'base', x: 1203.07, y: 514.31, industry: '鲜食' },
|
|
|
|
|
+ 'fresh-chain1': { id: 'fresh-chain1', type: 'chain', x: 1273.12, y: 551.14, industry: '鲜食' },
|
|
|
|
|
+ 'fresh-chain2': { id: 'fresh-chain2', type: 'chain', x: 1403, y: 597.5, industry: '鲜食' },
|
|
|
|
|
+ 'fresh-chain3': { id: 'fresh-chain3', type: 'chain', x: 1694.25, y: 645.5, industry: '鲜食' },
|
|
|
|
|
+ 'fresh-chain4': { id: 'fresh-chain4', type: 'chain', x: 1237.87, y: 761.84, industry: '鲜食' }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ '泛盐': {
|
|
|
|
|
+ connections: [
|
|
|
|
|
+ { from: 'salt-base1', to: 'salt-chain1', type: 'supply' },
|
|
|
|
|
+ { from: 'salt-base1', to: 'salt-chain2', type: 'supply' },
|
|
|
|
|
+ { from: 'salt-base1', to: 'salt-chain3', type: 'supply' },
|
|
|
|
|
+ { from: 'salt-base1', to: 'salt-chain4', type: 'supply' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ 'salt-base1': { id: 'salt-base1', type: 'base', x: 1445, y: 560.35, industry: '泛盐' },
|
|
|
|
|
+ 'salt-chain1': { id: 'salt-chain1', type: 'chain', x: 949.08, y: 680.31, industry: '泛盐' },
|
|
|
|
|
+ 'salt-chain2': { id: 'salt-chain2', type: 'chain', x: 1178.36, y: 418.4, industry: '泛盐' },
|
|
|
|
|
+ 'salt-chain3': { id: 'salt-chain3', type: 'chain', x: 1319.74, y: 441.51, industry: '泛盐' },
|
|
|
|
|
+ 'salt-chain4': { id: 'salt-chain4', type: 'chain', x: 1403.37, y: 603.77, industry: '泛盐' }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ popupData: {
|
|
|
|
|
+ '鲜食': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'fresh-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '鲜食核心示范基地',
|
|
|
|
|
+ position: { x: 717.28, y: 273.13 },
|
|
|
|
|
+ industry: '鲜食',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>18', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '5~8', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '泛盐': [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 'salt-popup1',
|
|
|
|
|
+ title: '源头',
|
|
|
|
|
+ content: '泛盐核心示范基地',
|
|
|
|
|
+ position: { x: 814.45, y: 464.02 },
|
|
|
|
|
+ industry: '泛盐',
|
|
|
|
|
+ metrics: [
|
|
|
|
|
+ { label: '辐射带动:', value: '>12', unit: '万亩' },
|
|
|
|
|
+ { label: '自建基地规模:', value: '3~6', unit: '万亩' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 静态数据加载函数
|
|
|
|
|
+const loadStaticData = async (dashboardType: DashboardType): Promise<SupplyChainData> => {
|
|
|
|
|
+ // 模拟API延迟
|
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
|
|
+
|
|
|
|
|
+ // 返回对应组合的静态数据
|
|
|
|
|
+ switch (dashboardType) {
|
|
|
|
|
+ case 'grain-oil':
|
|
|
|
|
+ return grainOilData;
|
|
|
|
|
+ case 'seed-fruit':
|
|
|
|
|
+ return seedFruitData;
|
|
|
|
|
+ case 'livestock-aquaculture':
|
|
|
|
|
+ return livestockAquacultureData;
|
|
|
|
|
+ case 'fresh-food-salt':
|
|
|
|
|
+ return freshFoodSaltData;
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new Error(`Unknown dashboard type: ${dashboardType}`);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4. React Query性能优化
|
|
|
|
|
+- **数据预加载**: 使用`queryClient.prefetchQuery`在路由切换前预加载目标组合数据
|
|
|
|
|
+- **缓存机制**: React Query自动管理缓存,减少重复加载
|
|
|
|
|
+- **懒加载**: 复杂组件按需加载
|
|
|
|
|
+- **错误重试**: React Query内置错误重试机制
|
|
|
|
|
+- **乐观更新**: 支持数据更新时的乐观UI更新
|
|
|
|
|
+
|
|
|
|
|
+### 兼容性要求
|
|
|
|
|
+- [ ] 现有ThemeContext功能保持不变
|
|
|
|
|
+- [ ] 现有路由配置向后兼容
|
|
|
|
|
+- [ ] 现有组件无需修改即可使用新Context
|
|
|
|
|
+- [ ] 性能指标满足要求(加载时间<3秒)
|
|
|
|
|
+
|
|
|
|
|
+### 性能要求 [Source: docs/prd/epic-005-supply-chain-visualization.md#性能验收]
|
|
|
|
|
+- **Context初始化时间**: < 100ms
|
|
|
|
|
+- **数据加载时间**: < 2秒
|
|
|
|
|
+- **路由切换响应时间**: < 1秒
|
|
|
|
|
+- **内存使用**: 合理,支持数据缓存
|
|
|
|
|
+
|
|
|
|
|
+### 测试标准要求 [Source: architecture/testing-strategy.md#单元测试]
|
|
|
|
|
+- **测试框架**: Vitest + Testing Library
|
|
|
|
|
+- **测试位置**: `tests/unit/client/home/pages/SupplyChainDashboards/context/`
|
|
|
|
|
+- **覆盖率目标**: ≥ 85%
|
|
|
|
|
+- **测试类型**: Context功能测试、路由参数测试、数据加载测试
|
|
|
|
|
+
|
|
|
|
|
+## Testing
|
|
|
|
|
+### 测试策略
|
|
|
|
|
+- **测试类型**: 单元测试 + 集成测试
|
|
|
|
|
+- **测试范围**: Context功能、路由参数解析、数据加载机制
|
|
|
|
|
+- **测试工具**: Vitest + Testing Library + React Router Testing Library
|
|
|
|
|
+- **测试数据**: 使用工厂模式创建测试数据
|
|
|
|
|
+
|
|
|
|
|
+### 测试验证点
|
|
|
|
|
+- Context正确初始化和状态管理
|
|
|
|
|
+- 路由参数正确解析和验证
|
|
|
|
|
+- React Query数据加载和缓存机制正常工作
|
|
|
|
|
+- 静态数据mock正确返回
|
|
|
|
|
+- 组合切换功能正常
|
|
|
|
|
+- 错误处理和加载状态正确
|
|
|
|
|
+- 性能指标满足要求
|
|
|
|
|
+
|
|
|
|
|
+### 测试用例
|
|
|
|
|
+- 验证SupplyChainContext初始状态
|
|
|
|
|
+- 测试组合切换功能
|
|
|
|
|
+- 验证路由参数解析
|
|
|
|
|
+- 测试React Query数据加载和缓存
|
|
|
|
|
+- 验证静态数据mock正确性
|
|
|
|
|
+- 验证错误处理机制
|
|
|
|
|
+- 测试性能指标
|
|
|
|
|
+
|
|
|
|
|
+## Change Log
|
|
|
|
|
+| Date | Version | Description | Author |
|
|
|
|
|
+|------|---------|-------------|--------|
|
|
|
|
|
+| 2025-11-15 | 1.0 | 初始故事创建,基于架构重构需求 | Claude |
|
|
|
|
|
+| 2025-11-15 | 1.1 | 集成React Query和静态数据mock,更新数据加载策略 | Claude |
|
|
|
|
|
+| 2025-11-15 | 1.2 | 从PRD提取4套组合的完整定位点坐标数据,完善静态数据定义 | Claude |
|
|
|
|
|
+| 2025-11-15 | 1.3 | 从figma-jsx文档提取所有组合的完整定位点数据,包含完整的TypeScript数据定义 | Claude |
|
|
|
|
|
+| 2025-11-15 | 1.4 | **数据提取完成**:从现有组件和figma-jsx文档提取完整的关键指标数据、供应链网络数据和弹出框数据 | Claude |
|
|
|
|
|
+
|
|
|
|
|
+## Dev Agent Record
|
|
|
|
|
+
|
|
|
|
|
+### Agent Model Used
|
|
|
|
|
+
|
|
|
|
|
+### Debug Log References
|
|
|
|
|
+
|
|
|
|
|
+### Completion Notes List
|
|
|
|
|
+
|
|
|
|
|
+### File List
|
|
|
|
|
+
|
|
|
|
|
+## QA Results
|