From b3a8cec76cd3d2d3aa6d470e1c28ec161bc1a16b Mon Sep 17 00:00:00 2001
From: chen.lin <1442464845@qq.com>
Date: 星期二, 10 三月 2026 17:22:44 +0800
Subject: [PATCH] 路径管理-初始化功能优化

---
 rsf-admin/src/layout/TabsBar.jsx |  105 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 105 insertions(+), 0 deletions(-)

diff --git a/rsf-admin/src/layout/TabsBar.jsx b/rsf-admin/src/layout/TabsBar.jsx
index ab4f171..26d0c35 100644
--- a/rsf-admin/src/layout/TabsBar.jsx
+++ b/rsf-admin/src/layout/TabsBar.jsx
@@ -79,6 +79,8 @@
     return normalizePath(path1) === normalizePath(path2);
 };
 
+const LONG_PRESS_MS = 300;
+
 const TabsBar = () => {
     const location = useLocation();
     const navigate = useNavigate();
@@ -91,6 +93,12 @@
     const [contextMenuTab, setContextMenuTab] = useState(null);
     const contextMenuOpenRef = useRef(false);
     contextMenuOpenRef.current = contextMenu !== null;
+
+    const [draggingIndex, setDraggingIndex] = useState(null);
+    const [dropIndicatorIndex, setDropIndicatorIndex] = useState(null);
+    const longPressTimerRef = useRef(null);
+    const longPressIndexRef = useRef(null);
+    const justFinishedDragRef = useRef(false);
 
     // 鍦ㄦ爣绛鹃〉鍙抽敭锛岄樆姝㈡祻瑙堝櫒榛樿鑿滃崟
     useEffect(() => {
@@ -226,6 +234,10 @@
 
     // 鍒囨崲鏍囩椤�
     const handleTabChange = (event, newValue) => {
+        if (justFinishedDragRef.current) {
+            justFinishedDragRef.current = false;
+            return;
+        }
         const targetTab = tabs[newValue];
         if (targetTab && targetTab.path !== location.pathname) {
             navigate(targetTab.path);
@@ -415,6 +427,91 @@
         return () => document.removeEventListener('mousedown', onDocClick, true);
     }, [contextMenu]);
 
+    const clearLongPressTimer = useCallback(() => {
+        if (longPressTimerRef.current) {
+            clearTimeout(longPressTimerRef.current);
+            longPressTimerRef.current = null;
+        }
+        longPressIndexRef.current = null;
+    }, []);
+
+    const handleTabPointerDown = useCallback((e, index) => {
+        if (index < 0) return;
+        longPressIndexRef.current = index;
+        longPressTimerRef.current = setTimeout(() => {
+            longPressTimerRef.current = null;
+            setDraggingIndex(index);
+            setDropIndicatorIndex(index);
+        }, LONG_PRESS_MS);
+    }, []);
+
+    const handleTabPointerUp = useCallback(() => {
+        clearLongPressTimer();
+    }, [clearLongPressTimer]);
+
+    useEffect(() => {
+        if (draggingIndex === null) return;
+        const getDropIndex = (clientX) => {
+            const nodes = tabsBarRef.current?.querySelectorAll('[data-tab-index]');
+            if (!nodes?.length) return draggingIndex;
+            for (let i = 0; i < nodes.length; i++) {
+                const rect = nodes[i].getBoundingClientRect();
+                const mid = rect.left + rect.width / 2;
+                if (clientX <= mid) {
+                    const drop = i;
+                    if (draggingIndex === 0) return drop <= 0 ? 0 : draggingIndex;
+                    return drop <= 0 ? 1 : drop;
+                }
+            }
+            const drop = nodes.length;
+            return draggingIndex === 0 ? 0 : drop;
+        };
+        const clientXFromEvent = (e) => e.clientX ?? e.touches?.[0]?.clientX;
+        const onMove = (e) => {
+            const x = clientXFromEvent(e);
+            if (x != null) setDropIndicatorIndex(getDropIndex(x));
+        };
+        const onTouchMove = (e) => {
+            e.preventDefault();
+            onMove(e);
+        };
+        const onUp = () => {
+            justFinishedDragRef.current = true;
+            setDraggingIndex((di) => {
+                setDropIndicatorIndex((dropIdx) => {
+                    if (di !== null && dropIdx !== null && di !== dropIdx) {
+                        const newTabs = [...tabs];
+                        const [item] = newTabs.splice(di, 1);
+                        const insertAt = dropIdx > di ? dropIdx - 1 : dropIdx;
+                        newTabs.splice(insertAt, 0, item);
+                        const dashboard = newTabs.find((t) => t.path === '/dashboard');
+                        if (dashboard && newTabs[0].path !== '/dashboard') {
+                            const idx = newTabs.indexOf(dashboard);
+                            newTabs.splice(idx, 1);
+                            newTabs.unshift(dashboard);
+                        }
+                        saveTabs(newTabs);
+                        setTabs(newTabs);
+                    }
+                    return null;
+                });
+                return null;
+            });
+        };
+        document.addEventListener('mousemove', onMove, true);
+        document.addEventListener('mouseup', onUp, true);
+        document.addEventListener('touchmove', onTouchMove, { passive: false, capture: true });
+        document.addEventListener('touchend', onUp, true);
+        document.addEventListener('touchcancel', onUp, true);
+        return () => {
+            document.removeEventListener('mousemove', onMove, true);
+            document.removeEventListener('mouseup', onUp, true);
+            document.removeEventListener('touchmove', onTouchMove, true);
+            document.removeEventListener('touchend', onUp, true);
+            document.removeEventListener('touchcancel', onUp, true);
+        };
+    }, [draggingIndex, tabs]);
+
     return (
         <Box
             ref={tabsBarRef}
@@ -449,12 +546,20 @@
                         key={tab.path}
                         label={
                             <Box
+                                data-tab-index={index}
                                 onContextMenu={(e) => handleContextMenu(e, tab)}
+                                onMouseDown={(e) => handleTabPointerDown(e, index)}
+                                onMouseUp={handleTabPointerUp}
+                                onMouseLeave={handleTabPointerUp}
+                                onTouchStart={(e) => handleTabPointerDown(e, index)}
+                                onTouchEnd={handleTabPointerUp}
+                                onTouchCancel={handleTabPointerUp}
                                 sx={{
                                     display: 'flex',
                                     alignItems: 'center',
                                     gap: 0.5,
                                     width: '100%',
+                                    ...(draggingIndex === index && { opacity: 0.7 }),
                                 }}
                             >
                                 {tab.path === '/dashboard' && (

--
Gitblit v1.9.1