Skip to content

Commit cb91f9b

Browse files
committed
feat: init
1 parent 5312653 commit cb91f9b

17 files changed

+576
-169
lines changed

src/app/layout/StandAloneLayout.tsx

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import {Outlet, RouteObject, useAuth, useMatches, useNavigate} from "@trionesdev/commons-react";
2+
import {Avatar, Button, Dropdown, Layout, Menu, Space, Tag} from "antd";
3+
import styles from "./standalone-layout.module.less"
4+
import {
5+
ApartmentOutlined,
6+
DatabaseOutlined,
7+
DesktopOutlined, HomeOutlined,
8+
LogoutOutlined,
9+
MenuFoldOutlined, MenuUnfoldOutlined,
10+
UserOutlined
11+
} from "@ant-design/icons";
12+
import {RouteConstants} from "../../router/route.constants.ts";
13+
import {useEffect, useRef, useState} from "react";
14+
import {routeMatch} from "../../router";
15+
import _ from "lodash";
16+
17+
const TagsMenu = () => {
18+
const tagsMenuRef = useRef<any>()
19+
const tagsMenuWrapperRef = useRef<any>()
20+
const navigate = useNavigate()
21+
const [routeObjects, setRouteObjects] = useState<RouteObject[]>([])
22+
const [activeRouteObject, setActiveRouteObject] = useState<RouteObject | undefined>()
23+
const [translateX, setTranslateX] = useState<number>(0)
24+
const matches = useMatches()
25+
26+
27+
useEffect(() => {
28+
const routeId = matches[matches.length - 1].id;
29+
const currentRouteObject = routeObjects.find(item => item.id === routeId);
30+
if (currentRouteObject) {
31+
setActiveRouteObject(currentRouteObject)
32+
} else {
33+
const matchRoute = routeMatch(routeId)
34+
if (matchRoute) {
35+
setRouteObjects([...routeObjects, matchRoute])
36+
setActiveRouteObject(matchRoute)
37+
}
38+
}
39+
}, [matches])
40+
41+
return <div ref={tagsMenuRef} className={styles.standaloneLayoutTagsMenu} onWheel={(e) => {
42+
const tagsMenuEl = tagsMenuRef.current
43+
const tagsMenuWrapperEl = tagsMenuWrapperRef.current
44+
if (tagsMenuEl.clientWidth >= tagsMenuWrapperEl.clientWidth) {
45+
return
46+
} else {
47+
const maxTranslateX = tagsMenuWrapperEl.clientWidth - tagsMenuEl.clientWidth
48+
let nextTranslateX = translateX
49+
if (e.deltaY < 0) { //向左
50+
if (translateX + e.deltaY < -maxTranslateX) {
51+
nextTranslateX = -maxTranslateX;
52+
} else {
53+
nextTranslateX = translateX + e.deltaY;
54+
}
55+
} else if (e.deltaY > 0) { //向右
56+
if (translateX + e.deltaY > 0) {
57+
nextTranslateX = 0;
58+
} else {
59+
nextTranslateX = translateX + e.deltaY;
60+
}
61+
}
62+
setTranslateX(nextTranslateX)
63+
tagsMenuWrapperEl.style.transform = `translateX(${nextTranslateX}px)`
64+
}
65+
}}>
66+
<div ref={tagsMenuWrapperRef} className={styles.standaloneLayoutTagsMenuWrapper}>
67+
<Tag icon={<HomeOutlined/>}>首页</Tag>
68+
{routeObjects.map((item: RouteObject) => <Tag key={item.id}
69+
className={item?.id === activeRouteObject?.id ? 'active-tag' : ''}
70+
closable={true}
71+
onClose={() => {
72+
const newRouteObjects = routeObjects.filter(route => route.id !== item?.id)
73+
setRouteObjects(newRouteObjects)
74+
}}
75+
onClick={() => {
76+
const path = _.isFunction(item.path) ? item.path() : item.path
77+
navigate(path!)
78+
}}
79+
>{item.label}</Tag>)}
80+
</div>
81+
</div>
82+
}
83+
84+
export const StandAloneLayout = () => {
85+
const navigate = useNavigate()
86+
87+
const {actor, signOut} = useAuth()
88+
const [collapsed, setCollapsed] = useState(false)
89+
90+
const menuItems: any[] = [
91+
{
92+
key: 'org',
93+
label: '组织管理',
94+
icon: <ApartmentOutlined/>,
95+
children: [
96+
{
97+
key: RouteConstants.ORG.MEMBERS.id,
98+
label: RouteConstants.ORG.MEMBERS.label,
99+
onClick: () => navigate(RouteConstants.ORG.MEMBERS.path!())
100+
},
101+
{
102+
key: RouteConstants.ORG.DEPARTMENTS.id,
103+
label: RouteConstants.ORG.DEPARTMENTS.label,
104+
onClick: () => navigate(RouteConstants.ORG.DEPARTMENTS.path!())
105+
},
106+
{
107+
key: RouteConstants.ORG.ORG_STRUCTURE.id,
108+
label: RouteConstants.ORG.ORG_STRUCTURE.label,
109+
onClick: () => navigate(RouteConstants.ORG.ORG_STRUCTURE.path())
110+
},
111+
{
112+
key: RouteConstants.ORG.ROLES.id,
113+
label: RouteConstants.ORG.ROLES.label,
114+
onClick: () => navigate(RouteConstants.ORG.ROLES.path())
115+
}
116+
]
117+
},
118+
{
119+
key: 'dic',
120+
label: '数据字典',
121+
icon: <DatabaseOutlined/>,
122+
children: [
123+
{
124+
key: RouteConstants.DIC.DICTIONARIES.id,
125+
label: RouteConstants.DIC.DICTIONARIES.label,
126+
onClick: () => navigate(RouteConstants.DIC.DICTIONARIES.path!())
127+
},
128+
{
129+
key: RouteConstants.DIC.COUNTRIES.id,
130+
label: RouteConstants.DIC.COUNTRIES.label,
131+
onClick: () => navigate(RouteConstants.DIC.COUNTRIES.path!())
132+
},
133+
{
134+
key: RouteConstants.DIC.DISTRICTS.id,
135+
label: RouteConstants.DIC.DISTRICTS.label,
136+
onClick: () => navigate(RouteConstants.DIC.DISTRICTS.path!())
137+
},
138+
]
139+
},
140+
{
141+
key: 'sys',
142+
label: '系统管理',
143+
icon: <DesktopOutlined/>,
144+
children: [
145+
{
146+
key: RouteConstants.BOSS.PERM.FUNCTIONAL_RESOURCES.id,
147+
label: RouteConstants.BOSS.PERM.FUNCTIONAL_RESOURCES.label,
148+
onClick: () => navigate(RouteConstants.BOSS.PERM.FUNCTIONAL_RESOURCES.path!())
149+
},
150+
{
151+
key: RouteConstants.LOG.OPERATION_LOGS.id,
152+
label: RouteConstants.LOG.OPERATION_LOGS.label,
153+
onClick: () => navigate(RouteConstants.LOG.OPERATION_LOGS.path!())
154+
}
155+
]
156+
}
157+
]
158+
159+
return <Layout className={styles.standaloneLayout}>
160+
<Layout.Sider collapsed={collapsed}>
161+
<div className={styles.standaloneLayoutSiderWraper}>
162+
<div className={styles.logo}>TrionesDev</div>
163+
<div className={styles.menu}>
164+
<Menu mode="inline" theme={'dark'} items={menuItems}/>
165+
</div>
166+
</div>
167+
</Layout.Sider>
168+
<Layout>
169+
<Layout.Header className={styles.standaloneLayoutHeader}>
170+
<Button type={'text'} icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
171+
onClick={() => setCollapsed(!collapsed)}/>
172+
<Space>
173+
<Dropdown menu={{
174+
items: [
175+
{
176+
key: `profile`,
177+
label: `个人中心`,
178+
icon: <UserOutlined/>,
179+
onClick: () => {
180+
navigate(RouteConstants.USER_CENTER.PROFILE.path!())
181+
}
182+
},
183+
{
184+
key: `logout`,
185+
label: `退出登录`,
186+
icon: <LogoutOutlined/>,
187+
onClick: () => {
188+
signOut?.()
189+
}
190+
}
191+
]
192+
}}>
193+
<Space style={{cursor: "default"}}><Avatar icon={<UserOutlined/>} src={actor?.avatar}/>
194+
<span>{actor?.nickname}</span>
195+
</Space>
196+
</Dropdown>
197+
</Space>
198+
</Layout.Header>
199+
<TagsMenu/>
200+
<Layout.Content style={{overflowY: "auto", padding: 4}}>
201+
<Outlet/>
202+
</Layout.Content>
203+
</Layout>
204+
</Layout>
205+
}

src/app/MainLayout.tsx renamed to src/app/layout/TrionesLayout.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import {AppToolbar, Layout} from "@trionesdev/antd-react-ext";
22
import {Outlet, useAuth, useNavigate} from "@trionesdev/commons-react";
33
import {Avatar, Dropdown, Space} from "antd";
44
import {LogoutOutlined, UserOutlined} from "@ant-design/icons";
5-
import {RouteConstants} from "../router/route.constants.ts";
5+
import {RouteConstants} from "../../router/route.constants.ts";
66

7-
export const MainLayout = () => {
7+
export const TrionesLayout = () => {
88
const navigate = useNavigate()
99
const {actor, signOut} = useAuth()
1010
return <Layout direction={`vertical`}>

src/app/layout/index.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {StandAloneLayout} from "@app/layout/StandAloneLayout.tsx";
2+
import {useAppConfig} from "../../commponents/app-config";
3+
import {TrionesLayout} from "@app/layout/TrionesLayout.tsx";
4+
5+
6+
export const AppLayout = () => {
7+
const appConfig = useAppConfig()
8+
return <>
9+
{appConfig.subApp ? <TrionesLayout/> : <StandAloneLayout/>}
10+
</>
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
.standaloneLayout {
2+
height: 100%;
3+
background-color: rgb(48, 65, 86);
4+
5+
&SiderWraper {
6+
height: 100%;
7+
display: flex;
8+
flex-direction: column;
9+
background-color: rgb(48, 65, 86);
10+
11+
.logo {
12+
height: 60px;
13+
flex-shrink: 0;
14+
color: white;
15+
display: flex;
16+
align-items: center;
17+
padding: 4px;
18+
}
19+
20+
.menu {
21+
flex: auto;
22+
overflow-y: auto;
23+
background-color: rgb(48, 65, 86);
24+
25+
:global {
26+
.ant-menu-dark {
27+
background-color: rgb(48, 65, 86);
28+
29+
.ant-menu-sub.ant-menu-inline {
30+
background-color: #1f2d3d;
31+
}
32+
}
33+
}
34+
}
35+
}
36+
37+
&Header {
38+
background-color: white;
39+
height: 60px;
40+
padding: 8px;
41+
display: flex;
42+
box-sizing: border-box;
43+
align-items: center;
44+
justify-content: space-between;
45+
border-bottom: 1px solid #0000000d;
46+
}
47+
48+
&TagsMenu {
49+
box-sizing: border-box;
50+
background-color: white;
51+
border-bottom: 1px solid #0000000d;
52+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px 0 rgba(0, 0, 0, 0.02);
53+
overflow-x: hidden;
54+
flex-shrink: 0;
55+
56+
&Wrapper {
57+
display: flex;
58+
padding: 8px 4px;
59+
width: fit-content;
60+
}
61+
62+
:global {
63+
.ant-tag {
64+
padding: 0px 4px;
65+
border-radius: 0;
66+
cursor: pointer;
67+
}
68+
69+
.active-tag {
70+
background-color: #1677FF;
71+
border: 1px solid #1677FF;
72+
color: white;
73+
74+
.anticon {
75+
color: white;
76+
}
77+
}
78+
}
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {Outlet} from "@trionesdev/commons-react";
2+
3+
export const StandAloneLayout = () => {
4+
return <Outlet/>
5+
}

0 commit comments

Comments
 (0)