Files
aurora-admin/frameworks/EnhancedTabNav.jsx
T

101 lines
3.8 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useRef, useEffect } from 'react';
import './EnhancedTabNav.css';
const INITIAL = [
{ id: 'u', title: '用户管理', dirty: false, desc: '用户列表与权限配置。' },
{ id: 'r', title: '角色管理', dirty: true, desc: '角色权限分配(有未保存修改)。' },
{ id: 'm', title: '菜单管理', dirty: false, desc: '导航菜单树形配置。' },
{ id: 'p', title: '参数配置', dirty: true, desc: '系统参数键值对管理。' },
{ id: 'l', title: '日志查询', dirty: false, desc: '操作日志检索与导出。' }
];
export default function EnhancedTabNav() {
const [tabs, setTabs] = useState(INITIAL);
const [activeId, setActiveId] = useState('u');
const [dragId, setDragId] = useState(null);
const [menu, setMenu] = useState(null);
const [menuId, setMenuId] = useState('');
const root = useRef(null);
useEffect(() => {
function onDoc(e) {
if (root.current && !root.current.contains(e.target)) setMenu(null);
}
document.addEventListener('click', onDoc);
return () => document.removeEventListener('click', onDoc);
}, []);
const activeTab = tabs.filter((t) => t.id === activeId)[0] || null;
const closeTab = (id) => {
setTabs((ts) => {
if (ts.length <= 1) return ts;
const idx = ts.findIndex((t) => t.id === id);
const next = ts.slice();
next.splice(idx, 1);
if (activeId === id) setActiveId(next[Math.max(0, idx - 1)].id);
return next;
});
};
const onTabContext = (e, id) => {
e.preventDefault();
setMenu({ x: e.clientX, y: e.clientY });
setMenuId(id);
};
const onBarContext = (e) => {
if (e.target.closest('.aa-tabs-enh-tab')) e.preventDefault();
};
const menuAct = (act) => {
if (act === 'close') closeTab(menuId);
else if (act === 'other') setTabs((ts) => ts.filter((t) => t.id === menuId));
else if (act === 'all') setTabs([]);
setActiveId(tabs.length ? menuId : '');
setMenu(null);
};
const onDrop = (e) => {
const el = e.target.closest('.aa-tabs-enh-tab');
const id = el && el.dataset.id;
if (!id || !dragId) return;
setTabs((ts) => {
const from = ts.findIndex((t) => t.id === dragId);
const to = ts.findIndex((t) => t.id === id);
if (from < 0 || to < 0 || from === to) return ts;
const next = ts.slice();
const moved = next.splice(from, 1)[0];
next.splice(to, 0, moved);
return next;
});
};
return (
<div className="aa-tabs-enh" ref={root}>
<div className="aa-tabs-enh-bar" onContextMenu={onBarContext} onDragOver={(e) => e.preventDefault()} onDrop={onDrop}>
{tabs.map((t) => (
<div
key={t.id}
className={'aa-tabs-enh-tab' + (t.id === activeId ? ' is-active' : '')}
draggable="true"
data-id={t.id}
onClick={() => setActiveId(t.id)}
onDragStart={() => setDragId(t.id)}
onContextMenu={(e) => onTabContext(e, t.id)}
>
{t.dirty && <span className="aa-dot" />}
<span className="aa-tabs-enh-text">{t.title}</span>
<span className="aa-tabs-enh-close" onClick={(e) => { e.stopPropagation(); closeTab(t.id); }}>×</span>
</div>
))}
</div>
<div className="aa-tabs-enh-panel">{activeTab ? activeTab.title + ':' + activeTab.desc : '请选择页签'}</div>
{menu && (
<div className="aa-tabs-enh-menu" style={{ left: menu.x, top: menu.y }} onClick={(e) => e.stopPropagation()}>
<div onClick={() => menuAct('close')}>关闭</div>
<div onClick={() => menuAct('other')}>关闭其他</div>
<div onClick={() => menuAct('all')}>关闭全部</div>
</div>
)}
<p className="aa-tabs-enh-hint">拖拽标签可排序;右键标签打开菜单;带 ● 表示未保存。</p>
</div>
);
}