Browse Source

在中等屏幕时,浮动菜单默认显示在右侧,抽屉布局,打开按钮可拖拽移动位置,抽屉打开时自动隐藏按钮

dengdx 2 months ago
parent
commit
8144f984b5
2 changed files with 103 additions and 23 deletions
  1. 2 2
      src/layouts/BasicLayout.tsx
  2. 101 21
      src/layouts/NavbarFloat.tsx

+ 2 - 2
src/layouts/BasicLayout.tsx

@@ -172,8 +172,8 @@ const BasicLayout: React.FC<BasicLayoutProps> = () => {
           </TabBar>
         </div>
         <NavbarFloat
-          className="fixed top-1/2 transform -translate-y-1/2 hidden sm:hidden xs:hidden md:block lg:hidden xl:hidden"
-          position="left"
+          className="fixed hidden sm:hidden xs:hidden md:block lg:hidden xl:hidden"
+          position="right"
         />
       </Layout>
     </ConfigProvider>

+ 101 - 21
src/layouts/NavbarFloat.tsx

@@ -1,6 +1,7 @@
-import React from 'react';
-import { Menu, Button } from 'antd';
+import React, { useState, useRef } from 'react';
+import { Menu, Button, Drawer } from 'antd';
 import {
+  MenuUnfoldOutlined,
   UserOutlined,
   FileSearchOutlined,
   AlertOutlined,
@@ -18,14 +19,43 @@ const NavbarFloat: React.FC<NavbarFloatProps> = ({
   position = 'left',
   className,
 }) => {
-  const style: React.CSSProperties = {
-    position: 'fixed',
-    top: '50%',
-    transform: 'translateY(-50%)',
-    [position]: 0,
-    backgroundColor: '#fff',
-    boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
-    borderRadius: position === 'left' ? '0 4px 4px 0' : '4px 0 0 4px',
+  const [open, setOpen] = useState(false);
+  const [btnPos, setBtnPos] = useState({
+    x: window.innerWidth - 96,
+    y: window.innerHeight - 96,
+  });
+  const [dragged, setDragged] = useState(false);
+  const dragging = useRef(false);
+  const offset = useRef({ x: 0, y: 0 });
+
+  // 拖拽事件
+  const onMouseDown = (e: React.MouseEvent) => {
+    dragging.current = true;
+    setDragged(false);
+    offset.current = {
+      x: e.clientX - btnPos.x,
+      y: e.clientY - btnPos.y,
+    };
+    document.addEventListener('mousemove', onMouseMove);
+    document.addEventListener('mouseup', onMouseUp);
+  };
+
+  const onMouseMove = (e: MouseEvent) => {
+    if (!dragging.current) return;
+    let x = e.clientX - offset.current.x;
+    let y = e.clientY - offset.current.y;
+    // 限制在窗口内
+    x = Math.max(0, Math.min(window.innerWidth - 80, x));
+    y = Math.max(0, Math.min(window.innerHeight - 80, y));
+    setBtnPos({ x, y });
+    setDragged(true); // 标记为拖拽
+  };
+
+  const onMouseUp = () => {
+    dragging.current = false;
+    setTimeout(() => setDragged(false), 100); // 短暂延迟后重置
+    document.removeEventListener('mousemove', onMouseMove);
+    document.removeEventListener('mouseup', onMouseUp);
   };
 
   const menuItems = [
@@ -58,20 +88,70 @@ const NavbarFloat: React.FC<NavbarFloatProps> = ({
 
   const handleClick: MenuProps['onClick'] = (e) => {
     console.log('clicked ', e.key);
+    setOpen(false); // 点击菜单后自动关闭抽屉
   };
 
+  const drawerPlacement = position === 'right' ? 'right' : 'left';
+
   return (
-    <div
-      style={{ ...style, backgroundColor: 'transparent' }}
-      className={className}
-    >
-      <Menu
-        mode="vertical"
-        onClick={handleClick}
-        items={menuItems}
-        style={{ border: 'none', backgroundColor: 'transparent' }}
-      />
-    </div>
+    <>
+      {!open && (
+        <div
+          style={{
+            position: 'fixed',
+            left: btnPos.x,
+            top: btnPos.y,
+            zIndex: 1100,
+            cursor: 'grab',
+          }}
+          className={className}
+          onMouseDown={onMouseDown}
+        >
+          <Button
+            type="primary"
+            shape="circle"
+            size="large"
+            icon={<MenuUnfoldOutlined style={{ fontSize: 32 }} />}
+            onClick={(e) => {
+              e.stopPropagation();
+              if (dragged) return; // 拖拽后不弹抽屉
+              setOpen(true);
+            }}
+            style={{
+              width: 64,
+              height: 64,
+              backgroundColor: '#fff',
+              color: '#333',
+              boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
+              display: 'flex',
+              alignItems: 'center',
+              justifyContent: 'center',
+              userSelect: 'none',
+            }}
+          />
+        </div>
+      )}
+      <Drawer
+        placement={drawerPlacement}
+        open={open}
+        onClose={() => setOpen(false)}
+        closable={false}
+        bodyStyle={{ padding: 0, background: 'transparent' }}
+        width={120}
+        maskClosable={true}
+        style={{
+          top: 0,
+          height: '100vh',
+        }}
+      >
+        <Menu
+          mode="vertical"
+          onClick={handleClick}
+          items={menuItems}
+          style={{ border: 'none', backgroundColor: 'transparent' }}
+        />
+      </Drawer>
+    </>
   );
 };