Răsfoiți Sursa

📝 docs(story): 更新供应链地图组件需求文档

- 添加SupplyChainModal图片展示布局优化需求,支持单图居中显示和多图横向滚动
- 添加滚动条交互功能需求,支持点击拖动控制图片滚动和位置同步
- 完善相关任务分解和验收标准
- 更新测试用例和变更日志
yourname 2 luni în urmă
părinte
comite
60768df8ce
1 a modificat fișierele cu 172 adăugiri și 0 ștergeri
  1. 172 0
      docs/stories/005.004.story.md

+ 172 - 0
docs/stories/005.004.story.md

@@ -19,6 +19,8 @@ Completed
 3. **组件支持产业切换** - 当用户在种业和果蔬之间切换时,组件应自动更新显示对应产业的数据
 4. **验证数据完整性** - 确保种业和果蔬的所有定位点、关键指标、供应链网络和弹出框数据都能正确显示
 5. **点击popup时弹出完整SupplyChainModal** - 当用户点击地图上的定位点时,应显示完整的SupplyChainModal弹出层,包含标题、图片和自定义内容区域
+6. **SupplyChainModal图片展示布局优化** - 弹出层应根据图片数量动态选择展示模式:单图时图片居中显示,多图时支持横向滚动
+7. **滚动条交互功能** - 多图模式下,滚动条应支持点击拖动控制图片滚动,滚动条位置与图片滚动位置同步
 
 ## Tasks / Subtasks
 - [x] 修改SupplyChainMap组件使用动态数据 (AC: #1, #3, #4)
@@ -57,6 +59,19 @@ Completed
   - [ ] 实现弹出层关闭功能
   - [ ] 验证弹出层主题色正确应用
   - [ ] 测试ESC键和遮罩层点击关闭功能
+  - [ ] 优化SupplyChainModal图片展示布局
+    - [ ] 支持单图展示模式(图片居中显示)
+    - [ ] 支持多图展示模式(图片横向滚动)
+    - [ ] 根据图片数量动态选择展示模式
+    - [ ] 实现横向滚动功能
+    - [ ] 实现滚动条交互功能
+      - [ ] 滚动条可点击拖动控制图片滚动
+      - [ ] 滚动条位置与图片滚动位置同步
+      - [ ] 滚动条样式与主题色协调
+      - [ ] 滚动条悬停效果
+    - [ ] 验证单图模式图片居中显示
+    - [ ] 验证多图模式横向滚动功能
+    - [ ] 验证滚动条交互功能正常
 - [x] 验证种业-果蔬组合路由功能 (AC: #3, #4)
   - [x] 测试路由`/supply-chain/seed-fruit`正确加载种业-果蔬数据
   - [x] 验证组合内产业切换功能(种业↔果蔬)
@@ -222,6 +237,149 @@ function getIndustryVariant(industry: string, industries: string[]): 'first' | '
 - **种业产业**: 主色 #5DEF8B
 - **果蔬产业**: 主色 #FFF586
 
+#### SupplyChainModal图片展示布局优化设计
+
+**展示模式策略:**
+- **单图模式**: 当只有一张图片时,图片在容器中居中显示
+- **多图模式**: 当有多张图片时,图片在横向滚动容器中排列,支持左右滚动
+
+**实现方案:**
+```typescript
+// 图片展示模式类型
+type ImageDisplayMode = 'single' | 'multiple';
+
+// 根据图片数量确定展示模式
+function getImageDisplayMode(imageUrls: string[]): ImageDisplayMode {
+  return imageUrls.length <= 1 ? 'single' : 'multiple';
+}
+
+// 单图模式布局
+const singleImageLayout = (
+  <div className="flex items-center justify-center">
+    <img src={imageUrls[0]} className="max-w-full max-h-full object-contain" />
+  </div>
+);
+
+// 多图模式布局
+const multipleImageLayout = (
+  <div className="flex overflow-x-auto gap-4">
+    {imageUrls.map((url, index) => (
+      <img key={index} src={url} className="flex-shrink-0 max-h-full object-contain" />
+    ))}
+  </div>
+);
+```
+
+**CSS样式要求:**
+- 单图模式:图片居中,保持宽高比
+- 多图模式:横向滚动,图片等高等宽,间距统一
+- 滚动条样式:自定义滚动条样式,与主题色协调
+
+#### 滚动条交互功能设计
+
+**滚动条交互实现方案:**
+```typescript
+// 滚动条状态管理
+interface ScrollbarState {
+  isDragging: boolean;
+  scrollPosition: number;
+  scrollbarWidth: number;
+  containerWidth: number;
+}
+
+// 滚动条交互逻辑
+const useScrollbarInteraction = (containerRef: React.RefObject<HTMLDivElement>) => {
+  const [scrollState, setScrollState] = useState<ScrollbarState>({
+    isDragging: false,
+    scrollPosition: 0,
+    scrollbarWidth: 0,
+    containerWidth: 0
+  });
+
+  // 处理滚动条点击
+  const handleScrollbarClick = (event: React.MouseEvent) => {
+    const rect = event.currentTarget.getBoundingClientRect();
+    const clickPosition = event.clientX - rect.left;
+    const scrollPercentage = clickPosition / rect.width;
+
+    if (containerRef.current) {
+      const scrollWidth = containerRef.current.scrollWidth - containerRef.current.clientWidth;
+      containerRef.current.scrollLeft = scrollWidth * scrollPercentage;
+    }
+  };
+
+  // 处理滚动条拖动
+  const handleScrollbarDrag = (event: React.MouseEvent) => {
+    if (!scrollState.isDragging) return;
+
+    const rect = event.currentTarget.getBoundingClientRect();
+    const dragPosition = event.clientX - rect.left;
+    const scrollPercentage = Math.max(0, Math.min(1, dragPosition / rect.width));
+
+    if (containerRef.current) {
+      const scrollWidth = containerRef.current.scrollWidth - containerRef.current.clientWidth;
+      containerRef.current.scrollLeft = scrollWidth * scrollPercentage;
+    }
+  };
+
+  // 同步滚动条位置
+  const syncScrollbarPosition = () => {
+    if (containerRef.current) {
+      const scrollLeft = containerRef.current.scrollLeft;
+      const scrollWidth = containerRef.current.scrollWidth - containerRef.current.clientWidth;
+      const scrollPercentage = scrollWidth > 0 ? scrollLeft / scrollWidth : 0;
+
+      setScrollState(prev => ({
+        ...prev,
+        scrollPosition: scrollPercentage
+      }));
+    }
+  };
+
+  return {
+    scrollState,
+    handleScrollbarClick,
+    handleScrollbarDrag,
+    syncScrollbarPosition
+  };
+};
+```
+
+**滚动条样式设计:**
+```css
+/* 滚动条容器 */
+.scrollbar-container {
+  position: absolute;
+  bottom: 20px;
+  left: 50%;
+  transform: translateX(-50%);
+  width: 60%;
+  height: 8px;
+  background: rgba(255, 255, 255, 0.1);
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+/* 滚动条滑块 */
+.scrollbar-thumb {
+  position: absolute;
+  height: 100%;
+  background: ${themeColor};
+  border-radius: 4px;
+  transition: background-color 0.2s ease;
+  cursor: grab;
+}
+
+.scrollbar-thumb:hover {
+  background: ${themeColor};
+  opacity: 0.8;
+}
+
+.scrollbar-thumb:active {
+  cursor: grabbing;
+}
+```
+
 ### 数据集成策略 [Source: architecture/component-architecture.md#技术栈配置]
 
 #### 1. React Query集成
@@ -339,6 +497,12 @@ const KeyMetrics: React.FC<KeyMetricsProps> = ({ title, subtitle }) => {
 - 点击定位点弹出完整SupplyChainModal
 - 弹出层主题色正确应用
 - ESC键和遮罩层点击关闭功能正常
+- 单图模式图片居中显示正确
+- 多图模式横向滚动功能正常
+- 图片展示模式根据图片数量动态切换
+- 滚动条点击拖动控制图片滚动功能正常
+- 滚动条位置与图片滚动位置同步正确
+- 滚动条样式与主题色协调
 
 ### 测试用例
 - 验证seed-fruit组合数据正确加载
@@ -352,12 +516,20 @@ const KeyMetrics: React.FC<KeyMetricsProps> = ({ title, subtitle }) => {
 - 验证弹出层主题色正确应用
 - 测试ESC键关闭弹出层功能
 - 测试遮罩层点击关闭弹出层功能
+- 测试单图模式图片居中显示
+- 测试多图模式横向滚动功能
+- 验证图片展示模式动态切换
+- 测试滚动条点击控制图片滚动
+- 测试滚动条拖动控制图片滚动
+- 验证滚动条位置与图片滚动同步
 
 ## Change Log
 | Date | Version | Description | Author |
 |------|---------|-------------|--------|
 | 2025-11-16 | 1.0 | 初始故事创建,基于Epic 005需求 | Bob (SM) |
 | 2025-11-16 | 1.1 | 添加点击popup时弹出完整SupplyChainModal的任务和验收标准 | Claude |
+| 2025-11-16 | 1.2 | 添加SupplyChainModal图片展示布局优化任务,支持单图居中显示和多图横向滚动 | Claude |
+| 2025-11-16 | 1.3 | 添加滚动条交互功能需求,支持点击拖动控制图片滚动 | Claude |
 
 ## Dev Agent Record