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
+ }
0 commit comments