基础布局

master_basic
张瑞宁 1 year ago
parent f03fe68d93
commit 311558a3a7

@ -0,0 +1,52 @@
.menu_item {
width: 260px;
height: 32px;
background: linear-gradient(180deg, #4AB4E4 0%, #A2E1FF 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 600;
color: #FFFFFF;
cursor: pointer;
position: relative;
.triangle {
position: absolute;
right: 20px;
width: 0;
height: 0;
border-left: 6px solid #fff;
border-bottom: 6px solid transparent;
border-top: 6px solid transparent;
transition: all 0.15s ease-in;
}
}
.item_warp {
transition: all 2s ease-in;
.item1 {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin: 20px 0;
cursor: pointer;
}
.item1_img {
width: 60px;
height: 60px;
background-color: saddlebrown;
}
.item1_name {
width: 100%;
text-align: center;
font-size: 14px;
font-weight: 400;
color: #1A374A;
margin-top: 6px;
}
}

@ -0,0 +1,206 @@
import { FC, useEffect, useRef, useState } from 'react';
import styles from './index.less';
import { history, useLocation, useParams } from 'umi';
import { Menu, Tree } from 'antd';
interface PageProps {
data: Array<{
// 菜单名称
name: string;
// 是否展开
check: boolean;
// 内容是否有目录结构
isTree?: boolean;
// 菜单列表 name: 名称; url: 路由; img: 图标;
data: Array<{ name: string; url: string; img: any; }>
}>;
}
const LeftMenuCom: FC<PageProps> = ({ data }) => {
const [list, setList] = useState<any>([]);
const route = useLocation();
const urlParams = useParams();
const [selectedKeys, setSelectedKeys] = useState<any>([]);
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
const [showContextMenu, setShowContextMenu] = useState(false);
const treeRef = useRef(null);
const [menuItems, setMenuItems] = useState<any>([]);
useEffect(() => {
menuItemsConfig();
data.forEach((item, index) => {
// 默认展开第一个
// item.check = (index == 0 ? true : false);
// 默认全部展开
item.check = true;
if (item?.isTree) setSelectedKeys([urlParams?.id])
})
setList([...data])
const handleClickOutside = (e: any) => {
if (treeRef.current && !treeRef.current.contains(e.target)) {
// 点击了 MyTree 组件以外的地方,隐藏菜单
setShowContextMenu(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [])
const menuItemsConfig = () => {
if (`/registerManage/deviceRegister/${urlParams?.id}` == route?.pathname) {
setMenuItems([
{key: '1', label: '删除'},
{key: '2', label: '刷新'},
])
}else if (`/registerManage/unitLocation/${urlParams?.id}` == route?.pathname) {
setMenuItems([
{key: '1', label: '修改'},
{key: '2', label: '刷新'},
{key: '3', label: '新建'},
{key: '4', label: '删除'}
])
}
}
const handleRightClick = (e: any, node: any) => {
if (node?.isFolder) return
e.preventDefault();
setContextMenuPosition({ x: e.clientX, y: e.clientY }); // 记录右键菜单位置
setSelectedKeys([node.key]); // 根据节点设置选中的 keys
setShowContextMenu(true); // 显示右键菜单
};
const handleSelect = (selectedKeys: any, name: any) => {
if (selectedKeys.length == 0) return;
setSelectedKeys(selectedKeys);
switch (name) {
case '设备注册':
history.push(`/registerManage/deviceRegister/${selectedKeys[0]}`)
break;
case '单位位置管理':
history.push(`/registerManage/unitLocation/${selectedKeys[0]}`)
break;
}
};
const handleContextMenuClick = (e: any) => {
setShowContextMenu(false); // 隐藏右键菜单
};
const treeData = [
{
title: 'Parent 1',
key: '0-1',
selectable: false,
isFolder: true,
children: [
{
title: 'Child 1',
key: '0-1-1',
},
{
title: 'Child 2',
key: '0-1-2',
},
],
},
{
title: 'Parent 2',
key: '0-2',
selectable: false,
isFolder: true,
children: [
{
title: 'Child 3',
key: '0-2-1',
},
{
title: 'Child 4',
key: '0-2-2',
},
],
},
];
return (
<div>
{list.map((item: any, index: number) => {
return (
<div key={index}>
{/* 类型 */}
<div className={styles.menu_item} onClick={() => {
list[index].check = !list[index].check;
setList([...list])
}}>
<div>{item.name}</div>
<div className={styles.triangle} style={{ transform: list[index].check ? 'rotate(90deg)' : '' }}></div>
</div>
{/* 列表 */}
<div className={styles.item_warp} style={{ display: list[index].check ? 'block' : 'none' }}>
{
item?.isTree ?
// 树结构
<div>
<Tree
style={{ padding: 20 }}
onContextMenu={(e) => e.preventDefault()}
onRightClick={({ event, node }) => handleRightClick(event, node)}
onSelect={(e) => { handleSelect(e, item.name) }}
selectedKeys={selectedKeys}
treeData={treeData}
blockNode={true}
defaultExpandAll={true}
/>
{showContextMenu && (
<div ref={treeRef}>
<Menu
style={{
zIndex: 99,
width: 111,
position: 'absolute',
left: contextMenuPosition.x,
top: contextMenuPosition.y,
boxShadow: '0px 4px 8px 0px rgba(156, 172, 180, 0.5)'
}}
mode="vertical"
onClick={handleContextMenuClick}
items={menuItems}
/>
</div>
)}
</div> :
// 列表item
item.data.map((item1: any, index1: number) => {
return (
<div className={styles.item1} key={index1} onClick={() => {
setSelectedKeys([])
history.push(item1.url)
}}>
<div className={styles.item1_img}></div>
<div className={styles.item1_name} style={{ color: (route.pathname == item1.url ? 'red' : '') }}>
{item1.name}
</div>
</div>
)
})
}
</div>
</div>
)
})}
</div >
)
}
export default LeftMenuCom

@ -20,13 +20,13 @@
.nav_warp_t {
width: 100%;
height: 72px;
height: 92px;
background: linear-gradient(180deg, #F7FCFF 0%, #ECF7FF 100%);
justify-content: space-between;
}
.logo_icon {
width: 40px;
width: 46px;
height: 46px;
background-color: saddlebrown;
margin: 0 10px 0 20px;
@ -40,12 +40,26 @@
}
.menu_list {
div {
width: 52px;
height: 52px;
background-color: saddlebrown;
margin-right: 30px;
cursor: pointer;
margin-right:200px;
&_item {
&_img {
width: 50px;
height: 50px;
background-color: saddlebrown;
margin-right: 40px;
cursor: pointer;
}
&_name {
font-size: 14px;
font-weight: 400;
color: #1A374A;
margin-top: 6px;
}
.name_active{
color:#E30000;
}
}
}
@ -62,59 +76,78 @@
}
.nav_warp_b {
width: 100%;
height: 40px;
display:flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(rgb(74, 180, 228) 0%, rgb(162, 225, 255) 100%);
box-shadow: 0px 2px 4px 0px rgba(36, 57, 75, 0.5);
display: flex;
&_version{
margin-left:20px;
}
.tab_warp {
padding: 0 30px;
&_menu {
display: flex;
justify-content: center;
position: relative;
overflow: hidden;
&:not(:last-child)::after {
position: absolute;
right: 0;
top: 50%;
bottom: 50%;
transform: translateY(-50%);
content: '';
height: 28px;
border-right: 1px solid rgba(217, 217, 217, .55);
height: 28px;
height: 40px;
.tab_warp {
padding: 0 30px;
display: flex;
justify-content: center;
position: relative;
overflow: hidden;
&:not(:last-child)::after {
position: absolute;
right: 0;
top: 50%;
bottom: 50%;
transform: translateY(-50%);
content: '';
height: 28px;
border-right: 1px solid rgba(217, 217, 217, .55);
height: 28px;
}
}
}
.tab_con {
width: auto;
padding: 0 44px;
border: 1px solid rgba(0, 0, 0, 0);
margin-top: 4px;
font-size: 16px;
font-weight: 600;
color: #fff;
cursor: pointer;
border-radius: 4px 4px 0px 0px;
display: flex;
padding-top: 8px;
.tab_con {
width: auto;
padding: 0 44px;
border: 1px solid rgba(0, 0, 0, 0);
margin-top: 4px;
font-size: 16px;
font-weight: 600;
color: #fff;
cursor: pointer;
border-radius: 4px 4px 0px 0px;
display: flex;
padding-top: 8px;
&:hover {
border: 1px solid #fff;
background: linear-gradient(180deg, #CAE9F7 0%, #FFFFFF 100%);
color: #4A6B89;
}
}
&:hover {
.active_tab {
border: 1px solid #fff;
background: linear-gradient(180deg, #CAE9F7 0%, #FFFFFF 100%);
color: #4A6B89;
}
}
.active_tab {
border: 1px solid #fff;
background: linear-gradient(180deg, #CAE9F7 0%, #FFFFFF 100%);
color: #4A6B89;
}
}
.nav_warp_sub{
width:100%;
height:40px;
line-height:40px;
text-align:center;
color: #4A6B89;
font-size:16px;
font-weight: 600;
background: linear-gradient(180deg, #CAE9F7 0%, #FFFFFF 100%);
}
}
.main_warp {
@ -147,20 +180,20 @@
overflow-y: auto;
background: #F9F9F9;
}
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
color: #fff;
font-size: 14px;
padding: 0 20px;
height: 50px;
background: linear-gradient(180deg, #BEE5F8 0%, #4AB4E4 100%);
box-shadow: 0px -2px 4px 0px rgba(199, 206, 213, 0.5);
border-radius: 1px;
border: 2px solid;
border-image: linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)) 2 2;
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
color: #fff;
font-size: 14px;
padding: 0 20px;
height: 50px;
background: linear-gradient(180deg, #BEE5F8 0%, #4AB4E4 100%);
box-shadow: 0px -2px 4px 0px rgba(199, 206, 213, 0.5);
border-radius: 1px;
border: 2px solid;
border-image: linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)) 2 2;
}
}

@ -163,7 +163,7 @@ export default function Layout() {
}
return false;
};
// 获取路由名称
const getCurrentPageName = (pathname: string) => {
@ -189,76 +189,81 @@ export default function Layout() {
<div className={styles.system_title}>XXXX</div>
</div>
<Select
value={selectVal}
style={{ width: 180 }}
onChange={(e) => {
// 改变下拉框数据时将布局信息清空
localStorage.setItem('layoutInfo', JSON.stringify(null));
setSelectVal(e)
setTabList([...tabsType[e]])
setActive(1)
}}
options={options2}
/>
<div className={styles.menu_list}>
<div title=''></div>
<div title=''></div>
<div title=''></div>
<div title=''></div>
<div title=''></div>
<div title=''></div>
{
options2.map((item: any) => {
return <div
className={styles.menu_list_item}
onClick={() => {
// 点击一级路由时将布局信息清空
localStorage.setItem('layoutInfo', JSON.stringify(null));
setSelectVal(item.value)
setTabList([...tabsType[item.value]])
setActive(1)
}}
>
<div className={styles.menu_list_item_img}></div>
<div className={`${styles.menu_list_item_name} ${selectVal === item.value ? styles.name_active :''}`}>{item.label}</div>
</div>
})
}
</div>
</div>
<div className={styles.nav_warp_b}>
<div className={styles.nav_warp_b_version}>v1.2.0.0</div>
<div className={styles.nav_warp_b_menu}>
{
tabList.map((item: any, index: number) => {
return <div key={index} className={styles.tab_warp}>
<div className={`${styles.tab_con} ${(index + 1) == active ? styles.active_tab : ''}`}
onClick={() => {
let info = MenuType[item.name];
setActive(item.id)
setActiveName(item.name)
if (isTreeType(info)) return
if (info.length == 0 || info[0].data && info[0].data.length == 0) {
history.push('/construction');
return
}
// 改变activeName时跳转菜单栏的第一个路由
if (info[0].data) {
history.push(`${info[0].data[0].url}`)
} else {
history.push(`${info[0].url}`)
}
}}>
{item.name}
</div>
</div>
})
}
</div>
<div className={styles.times}>
<div>XXXX3023-00-00 15:00:00</div>
<div>XXXX3023-00-00 15:00:00</div>
</div>
</div>
<div className={styles.nav_warp_b}>
{
tabList.map((item: any, index: number) => {
return <div key={index} className={styles.tab_warp}>
<div className={`${styles.tab_con} ${(index + 1) == active ? styles.active_tab : ''}`}
onClick={() => {
let info = MenuType[item.name];
setActive(item.id)
setActiveName(item.name)
if (isTreeType(info)) return
if (info.length == 0 || info[0].data && info[0].data.length == 0) {
history.push('/construction');
return
}
// 改变activeName时跳转菜单栏的第一个路由
if (info[0].data) {
history.push(`${info[0].data[0].url}`)
} else {
history.push(`${info[0].url}`)
}
}}>
{item.name}
</div>
</div>
})
}
{/* 二级导航标题 */}
<div className={styles.nav_warp_sub}>
<div>{activeName}</div>
</div>
</div>
{/* 主体内容 */}
<div className={styles.main_warp}>
{/* 侧菜单栏 */}
{/* 左侧菜单栏 */}
<div className={styles.left_menu}>
{leftMenuType[activeName]}
</div>
{/* 侧内容 */}
{/* 右侧内容 */}
<div className={styles.right_warp}>
<div className={styles.right_route}
style={{
@ -273,15 +278,17 @@ export default function Layout() {
{/* GQL管理整个模块页面底部的配置信息 */}
{selectVal == 1 && <BottomConfigInfo />}
{/* 底部信息 */}
<div className={styles.footer}>
<div>0.0.0</div>
<div>admin</div>
<div style={{ minWidth: 180, textAlign: 'right' }}>{routeName}</div>
</div>
</div>
</div>
{/* 底部信息 */}
<div className={styles.footer}>
<div></div>
<div>xxx</div>
<div>user</div>
<div style={{ minWidth: 180, textAlign: 'right' }}></div>
</div>
</div>
</ConfigProvider>
</ConfigProvider >
);
}
Loading…
Cancel
Save