11import dash
22from dash .dependencies import Input , Output , State
3+ from dash .exceptions import PreventUpdate
34import feffery_antd_components as fac
45from jsonpath_ng import parse
56from flask import json
1112
1213
1314@app .callback (
14- [Output ('tabs-container' , 'items' ),
15- Output ('tabs-container' , 'activeKey' )],
15+ [Output ('tabs-container' , 'items' , allow_duplicate = True ),
16+ Output ('tabs-container' , 'activeKey' , allow_duplicate = True )],
1617 [Input ('index-side-menu' , 'currentKey' ),
1718 Input ('tabs-container' , 'latestDeletePane' )],
1819 [State ('tabs-container' , 'items' ),
@@ -37,6 +38,7 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
3738 # 先前已更新为非None的按钮的nClicks误触发通知弹出回调
3839 parser = parse ('$..nClicks' )
3940 origin_items = parser .update (origin_items , None )
41+ new_items = dash .Patch ()
4042
4143 if trigger_id == 'index-side-menu' :
4244
@@ -57,27 +59,58 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
5759 # 判断当前选中的菜单栏项是否存在module,如果有,则动态导入module,否则返回404页面
5860 menu_modules = find_modules_by_key (menu_info .get ('menu_info' ), currentKey )
5961
62+ for index , item in enumerate (origin_items ):
63+ if {'key' : '关闭右侧' , 'label' : '关闭右侧' , 'icon' : 'antd-arrow-right' } not in item ['contextMenu' ]:
64+ item ['contextMenu' ].insert (- 1 , {
65+ 'key' : '关闭右侧' ,
66+ 'label' : '关闭右侧' ,
67+ 'icon' : 'antd-arrow-right'
68+ })
69+ new_items [index ]['contextMenu' ] = item ['contextMenu' ]
70+ context_menu = [
71+ {
72+ 'key' : '刷新页面' ,
73+ 'label' : '刷新页面' ,
74+ 'icon' : 'antd-reload'
75+ },
76+ {
77+ 'key' : '关闭当前' ,
78+ 'label' : '关闭当前' ,
79+ 'icon' : 'antd-close'
80+ },
81+ {
82+ 'key' : '关闭其他' ,
83+ 'label' : '关闭其他' ,
84+ 'icon' : 'antd-close-circle'
85+ },
86+ {
87+ 'key' : '全部关闭' ,
88+ 'label' : '全部关闭' ,
89+ 'icon' : 'antd-close-circle'
90+ }
91+ ]
92+ if len (origin_items ) != 1 :
93+ context_menu .insert (- 1 , {
94+ 'key' : '关闭左侧' ,
95+ 'label' : '关闭左侧' ,
96+ 'icon' : 'antd-arrow-left'
97+ })
6098 if menu_modules :
6199 if menu_modules == 'link' :
62- return [ dash . no_update ] * 2
100+ raise PreventUpdate
63101 else :
64102 # 否则追加子项返回
65103 # 其中若各标签页内元素类似,则推荐配合模式匹配构建交互逻辑
66- return [
67- [
68- * origin_items ,
69- {
70- 'label' : menu_title ,
71- 'key' : currentKey ,
72- 'children' : eval ('views.' + menu_modules + '.render(button_perms)' ),
73- }
74- ],
75- currentKey
76- ]
77-
78- return [
79- [
80- * origin_items ,
104+ new_items .append (
105+ {
106+ 'label' : menu_title ,
107+ 'key' : currentKey ,
108+ 'children' : eval ('views.' + menu_modules + '.render(button_perms)' ),
109+ 'contextMenu' : context_menu
110+ }
111+ )
112+ else :
113+ new_items .append (
81114 {
82115 'label' : menu_title ,
83116 'key' : currentKey ,
@@ -90,34 +123,246 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
90123 'paddingTop' : 0
91124 }
92125 ),
126+ 'contextMenu' : context_menu
93127 }
94- ],
128+ )
129+
130+ return [
131+ new_items ,
95132 currentKey
96133 ]
97134
98135 elif trigger_id == 'tabs-container' :
99136
100- # 若要删除的是当前正激活的标签页
101- if latestDeletePane == activeKey :
137+ # 如果删除的是当前标签页则回到最后新增的标签页,否则保持当前标签页不变
138+ for index , item in enumerate (origin_items ):
139+ if item ['key' ] == latestDeletePane :
140+ context_menu = [
141+ {
142+ 'key' : '刷新页面' ,
143+ 'label' : '刷新页面' ,
144+ 'icon' : 'antd-reload'
145+ },
146+ {
147+ 'key' : '关闭其他' ,
148+ 'label' : '关闭其他' ,
149+ 'icon' : 'antd-close-circle'
150+ },
151+ {
152+ 'key' : '全部关闭' ,
153+ 'label' : '全部关闭' ,
154+ 'icon' : 'antd-close-circle'
155+ }
156+ ]
157+ if index == 1 and len (origin_items ) == 2 :
158+ new_items [0 ]['contextMenu' ] = context_menu
159+ elif len (origin_items ) == 3 :
160+ context_menu .insert (1 , {
161+ 'key' : '关闭当前' ,
162+ 'label' : '关闭当前' ,
163+ 'icon' : 'antd-close'
164+ })
165+ if index == 1 :
166+ new_items [2 ]['contextMenu' ] = context_menu
167+ if index == 2 :
168+ new_items [1 ]['contextMenu' ] = context_menu
169+ else :
170+ if index == len (origin_items ) - 1 :
171+ new_items [index - 1 ]['contextMenu' ] = item ['contextMenu' ]
172+ new_items .remove (item )
173+ break
174+ new_origin_items = [
175+ item for item in
176+ origin_items if item ['key' ] != latestDeletePane
177+ ]
178+
179+ return [
180+ new_items ,
181+ new_origin_items [- 1 ]['key' ] if activeKey == latestDeletePane else activeKey
182+ ]
183+
184+ raise PreventUpdate
185+
186+
187+ @app .callback (
188+ [Output ('tabs-container' , 'items' , allow_duplicate = True ),
189+ Output ('tabs-container' , 'activeKey' , allow_duplicate = True ),
190+ Output ('trigger-reload-output' , 'reload' , allow_duplicate = True )],
191+ Input ('tabs-container' , 'clickedContextMenu' ),
192+ [State ('tabs-container' , 'items' ),
193+ State ('tabs-container' , 'activeKey' )],
194+ prevent_initial_call = True
195+ )
196+ def handle_via_context_menu (clickedContextMenu , origin_items , activeKey ):
197+ """
198+ 基于标签页标题右键菜单的额外标签页控制
199+ """
200+ if clickedContextMenu ['menuKey' ] == '刷新页面' :
201+
202+ return [
203+ dash .no_update ,
204+ dash .no_update ,
205+ True
206+ ]
207+
208+ if '关闭' in clickedContextMenu ['menuKey' ]:
209+ new_items = dash .Patch ()
210+ if clickedContextMenu ['menuKey' ] == '关闭当前' :
211+ for index , item in enumerate (origin_items ):
212+ if item ['key' ] == clickedContextMenu ['tabKey' ]:
213+ context_menu = [
214+ {
215+ 'key' : '刷新页面' ,
216+ 'label' : '刷新页面' ,
217+ 'icon' : 'antd-reload'
218+ },
219+ {
220+ 'key' : '关闭其他' ,
221+ 'label' : '关闭其他' ,
222+ 'icon' : 'antd-close-circle'
223+ },
224+ {
225+ 'key' : '全部关闭' ,
226+ 'label' : '全部关闭' ,
227+ 'icon' : 'antd-close-circle'
228+ }
229+ ]
230+ if index == 1 and len (origin_items ) == 2 :
231+ new_items [0 ]['contextMenu' ] = context_menu
232+ elif len (origin_items ) == 3 :
233+ context_menu .insert (1 , {
234+ 'key' : '关闭当前' ,
235+ 'label' : '关闭当前' ,
236+ 'icon' : 'antd-close'
237+ })
238+ if index == 1 :
239+ new_items [2 ]['contextMenu' ] = context_menu
240+ if index == 2 :
241+ new_items [1 ]['contextMenu' ] = context_menu
242+ else :
243+ if index == len (origin_items ) - 1 :
244+ new_items [index - 1 ]['contextMenu' ] = item ['contextMenu' ]
245+ new_items .remove (item )
246+ break
247+ new_origin_items = [
248+ item for item in
249+ origin_items if item ['key' ] != clickedContextMenu ['tabKey' ]
250+ ]
251+
102252 return [
103- [
104- item
105- for item in origin_items
106- if item ['key' ] != latestDeletePane
107- ],
108- '首页'
253+ new_items ,
254+ new_origin_items [- 1 ]['key' ] if activeKey == clickedContextMenu ['tabKey' ] else activeKey ,
255+ dash .no_update
109256 ]
110257
111- # 否则保持当前激活的标签页子项不变,删去目标子项
258+ elif clickedContextMenu ['menuKey' ] == '关闭其他' :
259+ for item in origin_items :
260+ if item ['key' ] != clickedContextMenu ['tabKey' ] and item ['key' ] != '首页' :
261+ new_items .remove (item )
262+ context_menu = [
263+ {
264+ 'key' : '刷新页面' ,
265+ 'label' : '刷新页面' ,
266+ 'icon' : 'antd-reload'
267+ },
268+ {
269+ 'key' : '关闭其他' ,
270+ 'label' : '关闭其他' ,
271+ 'icon' : 'antd-close-circle'
272+ },
273+ {
274+ 'key' : '全部关闭' ,
275+ 'label' : '全部关闭' ,
276+ 'icon' : 'antd-close-circle'
277+ }
278+ ]
279+ if clickedContextMenu ['tabKey' ] == '首页' :
280+ new_items [0 ]['contextMenu' ] = context_menu
281+ else :
282+ context_menu .insert (1 , {
283+ 'key' : '关闭当前' ,
284+ 'label' : '关闭当前' ,
285+ 'icon' : 'antd-close'
286+ })
287+ new_items [1 ]['contextMenu' ] = context_menu
288+
289+ return [
290+ new_items ,
291+ clickedContextMenu ['tabKey' ],
292+ dash .no_update
293+ ]
294+
295+ elif clickedContextMenu ['menuKey' ] == '关闭左侧' :
296+ current_index = 0
297+ for index , item in enumerate (origin_items ):
298+ if item ['key' ] == clickedContextMenu ['tabKey' ]:
299+ current_index = index
300+ item ['contextMenu' ].remove ({
301+ 'key' : '关闭左侧' ,
302+ 'label' : '关闭左侧' ,
303+ 'icon' : 'antd-arrow-left'
304+ })
305+ new_items [index ]['contextMenu' ] = item ['contextMenu' ]
306+ break
307+ for item in origin_items [1 :current_index ]:
308+ new_items .remove (item )
309+
310+ return [
311+ new_items ,
312+ clickedContextMenu ['tabKey' ],
313+ dash .no_update
314+ ]
315+
316+ elif clickedContextMenu ['menuKey' ] == '关闭右侧' :
317+ current_index = 0
318+ for index , item in enumerate (origin_items ):
319+ if item ['key' ] == clickedContextMenu ['tabKey' ]:
320+ current_index = index
321+ item ['contextMenu' ].remove ({
322+ 'key' : '关闭右侧' ,
323+ 'label' : '关闭右侧' ,
324+ 'icon' : 'antd-arrow-right'
325+ })
326+ new_items [index ]['contextMenu' ] = item ['contextMenu' ]
327+ break
328+ for item in origin_items [current_index + 1 :]:
329+ new_items .remove (item )
330+
331+ return [
332+ new_items ,
333+ clickedContextMenu ['tabKey' ],
334+ dash .no_update
335+ ]
336+
337+ for item in origin_items :
338+ if item ['key' ] != '首页' :
339+ new_items .remove (item )
340+ new_items [0 ]['contextMenu' ] = [
341+ {
342+ 'key' : '刷新页面' ,
343+ 'label' : '刷新页面' ,
344+ 'icon' : 'antd-reload'
345+ },
346+ {
347+ 'key' : '关闭其他' ,
348+ 'label' : '关闭其他' ,
349+ 'icon' : 'antd-close-circle'
350+ },
351+ {
352+ 'key' : '全部关闭' ,
353+ 'label' : '全部关闭' ,
354+ 'icon' : 'antd-close-circle'
355+ }
356+ ]
357+ # 否则则为全部关闭
112358 return [
113- [
114- item
115- for item in origin_items
116- if item ['key' ] != latestDeletePane
117- ],
359+ new_items ,
360+ '首页' ,
118361 dash .no_update
119362 ]
120363
364+ raise PreventUpdate
365+
121366
122367# 页首面包屑和hash回调
123368@app .callback (
@@ -175,4 +420,4 @@ def get_current_breadcrumbs(active_key, menu_info):
175420 current_href
176421 ]
177422
178- return dash . no_update
423+ raise PreventUpdate
0 commit comments