|
@@ -1,27 +1,120 @@
|
|
|
-import React from 'react';
|
|
|
|
|
|
|
+import React, { useRef, useState, useEffect, useCallback } from 'react';
|
|
|
import { useSupplyChain } from '../context/SupplyChainContext';
|
|
import { useSupplyChain } from '../context/SupplyChainContext';
|
|
|
|
|
|
|
|
|
|
+// 图片展示模式类型
|
|
|
|
|
+type ImageDisplayMode = 'single' | 'multiple';
|
|
|
|
|
+
|
|
|
|
|
+// 滚动条状态管理
|
|
|
|
|
+interface ScrollbarState {
|
|
|
|
|
+ isDragging: boolean;
|
|
|
|
|
+ scrollPosition: number;
|
|
|
|
|
+ scrollbarWidth: number;
|
|
|
|
|
+ containerWidth: number;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
interface SupplyChainModalProps {
|
|
interface SupplyChainModalProps {
|
|
|
isOpen: boolean;
|
|
isOpen: boolean;
|
|
|
onClose: () => void;
|
|
onClose: () => void;
|
|
|
title?: string;
|
|
title?: string;
|
|
|
- imageUrl?: string;
|
|
|
|
|
|
|
+ imageUrls?: string[];
|
|
|
children?: React.ReactNode;
|
|
children?: React.ReactNode;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 滚动条交互工具函数
|
|
|
|
|
+const createScrollbarInteraction = (
|
|
|
|
|
+ containerRef: React.RefObject<HTMLDivElement | null>,
|
|
|
|
|
+ scrollState: ScrollbarState,
|
|
|
|
|
+ setScrollState: React.Dispatch<React.SetStateAction<ScrollbarState>>
|
|
|
|
|
+) => {
|
|
|
|
|
+ // 处理滚动条点击
|
|
|
|
|
+ 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 handleScrollbarDragStart = (event: React.MouseEvent) => {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ setScrollState(prev => ({ ...prev, isDragging: true }));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 处理滚动条拖动
|
|
|
|
|
+ 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 handleScrollbarDragEnd = () => {
|
|
|
|
|
+ setScrollState(prev => ({ ...prev, isDragging: false }));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ handleScrollbarClick,
|
|
|
|
|
+ handleScrollbarDragStart,
|
|
|
|
|
+ handleScrollbarDrag,
|
|
|
|
|
+ handleScrollbarDragEnd
|
|
|
|
|
+ };
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const SupplyChainModal: React.FC<SupplyChainModalProps> = ({
|
|
const SupplyChainModal: React.FC<SupplyChainModalProps> = ({
|
|
|
isOpen,
|
|
isOpen,
|
|
|
onClose,
|
|
onClose,
|
|
|
title = "江汉大米优质水稻种植核心示范基地",
|
|
title = "江汉大米优质水稻种植核心示范基地",
|
|
|
- imageUrl = "https://placehold.co/960x640",
|
|
|
|
|
- children
|
|
|
|
|
|
|
+ imageUrls = ["https://placehold.co/960x640"]
|
|
|
}) => {
|
|
}) => {
|
|
|
const { themeColor } = useSupplyChain();
|
|
const { themeColor } = useSupplyChain();
|
|
|
|
|
+ const imageContainerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
|
- if (!isOpen) return null;
|
|
|
|
|
|
|
+ // 滚动条状态管理
|
|
|
|
|
+ const [scrollState, setScrollState] = useState<ScrollbarState>({
|
|
|
|
|
+ isDragging: false,
|
|
|
|
|
+ scrollPosition: 0,
|
|
|
|
|
+ scrollbarWidth: 0,
|
|
|
|
|
+ containerWidth: 0
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 根据图片数量确定展示模式
|
|
|
|
|
+ const displayMode: ImageDisplayMode = imageUrls.length <= 1 ? 'single' : 'multiple';
|
|
|
|
|
+
|
|
|
|
|
+ // 使用滚动条交互工具函数
|
|
|
|
|
+ const {
|
|
|
|
|
+ handleScrollbarClick,
|
|
|
|
|
+ handleScrollbarDragStart,
|
|
|
|
|
+ handleScrollbarDrag,
|
|
|
|
|
+ handleScrollbarDragEnd
|
|
|
|
|
+ } = createScrollbarInteraction(imageContainerRef, scrollState, setScrollState);
|
|
|
|
|
+
|
|
|
|
|
+ // 同步滚动条位置函数
|
|
|
|
|
+ const syncScrollbarPosition = useCallback(() => {
|
|
|
|
|
+ if (imageContainerRef.current) {
|
|
|
|
|
+ const scrollLeft = imageContainerRef.current.scrollLeft;
|
|
|
|
|
+ const scrollWidth = imageContainerRef.current.scrollWidth - imageContainerRef.current.clientWidth;
|
|
|
|
|
+ const scrollPercentage = scrollWidth > 0 ? scrollLeft / scrollWidth : 0;
|
|
|
|
|
+
|
|
|
|
|
+ setScrollState(prev => ({
|
|
|
|
|
+ ...prev,
|
|
|
|
|
+ scrollPosition: scrollPercentage
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+ }, []);
|
|
|
|
|
|
|
|
// ESC键关闭弹窗
|
|
// ESC键关闭弹窗
|
|
|
- React.useEffect(() => {
|
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
const handleEsc = (event: KeyboardEvent) => {
|
|
const handleEsc = (event: KeyboardEvent) => {
|
|
|
if (event.key === 'Escape') {
|
|
if (event.key === 'Escape') {
|
|
|
onClose();
|
|
onClose();
|
|
@@ -34,6 +127,28 @@ const SupplyChainModal: React.FC<SupplyChainModalProps> = ({
|
|
|
}
|
|
}
|
|
|
}, [isOpen, onClose]);
|
|
}, [isOpen, onClose]);
|
|
|
|
|
|
|
|
|
|
+ // 监听图片容器滚动事件
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const container = imageContainerRef.current;
|
|
|
|
|
+ if (!container) return;
|
|
|
|
|
+
|
|
|
|
|
+ const handleScroll = () => {
|
|
|
|
|
+ syncScrollbarPosition();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ container.addEventListener('scroll', handleScroll);
|
|
|
|
|
+ return () => container.removeEventListener('scroll', handleScroll);
|
|
|
|
|
+ }, [syncScrollbarPosition]);
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化滚动条位置
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (imageContainerRef.current) {
|
|
|
|
|
+ syncScrollbarPosition();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [imageUrls, syncScrollbarPosition]);
|
|
|
|
|
+
|
|
|
|
|
+ if (!isOpen) return null;
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div
|
|
<div
|
|
|
data-layer="操控屏-1-粮食•油脂-粮食首页-基地弹出数据"
|
|
data-layer="操控屏-1-粮食•油脂-粮食首页-基地弹出数据"
|
|
@@ -62,44 +177,75 @@ const SupplyChainModal: React.FC<SupplyChainModalProps> = ({
|
|
|
</svg>
|
|
</svg>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* 图片样式1 */}
|
|
|
|
|
- <div data-layer="图片样式1" className="absolute h-[640px] left-[calc(50%+-0.06px)] overflow-clip rounded-[20px] top-[calc(50%+48.26px)] translate-x-[-50%] translate-y-[-50%] w-[1357px]">
|
|
|
|
|
- <div data-layer="Frame 1321316691" className="absolute left-0 top-1/2 translate-y-[-50%] content-stretch flex gap-[20px] items-center">
|
|
|
|
|
- {/* 第一张图片 */}
|
|
|
|
|
- <div data-layer="图片1" className="h-[640px] overflow-clip relative rounded-[10px] shrink-0 w-[853px]">
|
|
|
|
|
- <img
|
|
|
|
|
- data-layer="湖北农发种业老河口张集镇小麦种业示范基地 1"
|
|
|
|
|
- className="w-full h-full object-cover"
|
|
|
|
|
- src={imageUrl}
|
|
|
|
|
- alt={title}
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- {/* 第二张图片 */}
|
|
|
|
|
- <div data-layer="图片2" className="h-[640px] overflow-clip relative rounded-[10px] shrink-0 w-[940px]">
|
|
|
|
|
- <img
|
|
|
|
|
- data-layer="老河口张集镇小麦种业示范基地 1"
|
|
|
|
|
- className="w-full h-full object-cover"
|
|
|
|
|
- src={imageUrl}
|
|
|
|
|
- alt={title}
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ {/* 图片展示区域 */}
|
|
|
|
|
+ <div
|
|
|
|
|
+ data-layer="图片展示区域"
|
|
|
|
|
+ className="absolute h-[640px] left-[calc(50%+-0.06px)] rounded-[20px] top-[calc(50%+48.26px)] translate-x-[-50%] translate-y-[-50%] w-[1357px]"
|
|
|
|
|
+ >
|
|
|
|
|
+ {/* 单图模式 - 图片居中显示 */}
|
|
|
|
|
+ {displayMode === 'single' && (
|
|
|
|
|
+ <div className="flex items-center justify-center h-full">
|
|
|
|
|
+ <div className="h-[640px] overflow-clip relative rounded-[10px] w-[853px]">
|
|
|
|
|
+ <img
|
|
|
|
|
+ data-layer="单图展示"
|
|
|
|
|
+ className="w-full h-full object-cover"
|
|
|
|
|
+ src={imageUrls[0]}
|
|
|
|
|
+ alt={title}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ )}
|
|
|
|
|
|
|
|
- {/* 下拉框(滚动条) */}
|
|
|
|
|
- <div data-layer="下拉框" className="absolute contents inset-[926.48px_281.56px_147.52px_281.44px]">
|
|
|
|
|
- <div className="absolute flex inset-[926.48px_281.56px_147.52px_281.44px] items-center justify-center">
|
|
|
|
|
- <div className="flex-none h-[1357px] rotate-[270deg] w-[6px]">
|
|
|
|
|
- <div className="bg-[rgba(255,255,255,0.12)] rounded-[12px] shadow-[0px_40px_70px_0px_rgba(0,0,0,0.24)] size-full" data-name="bg" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div className="absolute flex inset-[926.48px_745.3px_147.52px_281.44px] items-center justify-center">
|
|
|
|
|
- <div className="flex-none h-[893.259px] rotate-[270deg] w-[6px]">
|
|
|
|
|
- <div className="bg-[rgba(255,255,255,0.12)] rounded-[10px] shadow-[0px_40px_70px_0px_rgba(0,0,0,0.24)] size-full" data-name="bg" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ {/* 多图模式 - 横向滚动 */}
|
|
|
|
|
+ {displayMode === 'multiple' && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div
|
|
|
|
|
+ ref={imageContainerRef}
|
|
|
|
|
+ data-layer="多图滚动容器"
|
|
|
|
|
+ className="flex overflow-x-auto gap-[20px] h-full items-center"
|
|
|
|
|
+ style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {imageUrls.map((url, index) => (
|
|
|
|
|
+ <div
|
|
|
|
|
+ key={index}
|
|
|
|
|
+ data-layer={`图片${index + 1}`}
|
|
|
|
|
+ className="h-[640px] overflow-clip relative rounded-[10px] shrink-0 w-[853px]"
|
|
|
|
|
+ >
|
|
|
|
|
+ <img
|
|
|
|
|
+ data-layer={`${title} ${index + 1}`}
|
|
|
|
|
+ className="w-full h-full object-cover"
|
|
|
|
|
+ src={url}
|
|
|
|
|
+ alt={`${title} ${index + 1}`}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 自定义滚动条 */}
|
|
|
|
|
+ <div
|
|
|
|
|
+ data-layer="滚动条容器"
|
|
|
|
|
+ className="absolute bottom-[-40px] left-1/2 transform -translate-x-1/2 w-[60%] h-[8px] bg-stone-700/30 rounded-[4px] cursor-pointer"
|
|
|
|
|
+ onClick={handleScrollbarClick}
|
|
|
|
|
+ onMouseDown={handleScrollbarDragStart}
|
|
|
|
|
+ onMouseMove={scrollState.isDragging ? handleScrollbarDrag : undefined}
|
|
|
|
|
+ onMouseUp={handleScrollbarDragEnd}
|
|
|
|
|
+ onMouseLeave={handleScrollbarDragEnd}
|
|
|
|
|
+ >
|
|
|
|
|
+ <div
|
|
|
|
|
+ data-layer="滚动条滑块"
|
|
|
|
|
+ className="absolute h-full bg-stone-400 rounded-[4px] transition-all duration-200 hover:bg-stone-300 cursor-grab active:cursor-grabbing"
|
|
|
|
|
+ style={{
|
|
|
|
|
+ width: '20%',
|
|
|
|
|
+ left: `${scrollState.scrollPosition * 80}%`,
|
|
|
|
|
+ backgroundColor: themeColor
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+
|
|
|
{/* 标题 */}
|
|
{/* 标题 */}
|
|
|
<div data-layer="标题" className="absolute content-stretch flex gap-[30px] h-[125px] items-center justify-center left-1/2 top-[42px] translate-x-[-50%] w-[1200px]">
|
|
<div data-layer="标题" className="absolute content-stretch flex gap-[30px] h-[125px] items-center justify-center left-1/2 top-[42px] translate-x-[-50%] w-[1200px]">
|
|
|
{/* 标题元素 - 光线左侧 */}
|
|
{/* 标题元素 - 光线左侧 */}
|