import dash import time import uuid from dash.dependencies import Input, Output, State, ALL from dash.exceptions import PreventUpdate import feffery_antd_components as fac import feffery_utils_components as fuc from server import app from utils.tree_tool import list_to_tree from views.system.menu.components import content_type, menu_type, button_type from api.menu import get_menu_tree_api, get_menu_tree_for_edit_option_api, get_menu_list_api, delete_menu_api, get_menu_detail_api @app.callback( output=dict( menu_table_data=Output('menu-list-table', 'data', allow_duplicate=True), menu_table_key=Output('menu-list-table', 'key'), menu_table_defaultexpandedrowkeys=Output('menu-list-table', 'defaultExpandedRowKeys'), api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True), fold_click=Output('menu-fold', 'nClicks') ), inputs=dict( search_click=Input('menu-search', 'nClicks'), refresh_click=Input('menu-refresh', 'nClicks'), operations=Input('menu-operations-store', 'data'), fold_click=Input('menu-fold', 'nClicks') ), state=dict( menu_name=State('menu-menu_name-input', 'value'), status_select=State('menu-status-select', 'value'), in_default_expanded_row_keys=State('menu-list-table', 'defaultExpandedRowKeys'), button_perms=State('menu-button-perms-container', 'data') ), prevent_initial_call=True ) def get_menu_table_data(search_click, refresh_click, operations, fold_click, menu_name, status_select, in_default_expanded_row_keys, button_perms): """ 获取菜单表格数据回调(进行表格相关增删查改操作后均会触发此回调) """ query_params = dict( menu_name=menu_name, status=status_select ) if search_click or refresh_click or operations or fold_click: table_info = get_menu_list_api(query_params) default_expanded_row_keys = [] if table_info['code'] == 200: table_data = table_info['data']['rows'] for item in table_data: default_expanded_row_keys.append(str(item['menu_id'])) item['key'] = str(item['menu_id']) item['icon'] = [ { 'type': 'link', 'icon': item['icon'], 'disabled': True, 'style': { 'color': 'rgba(0, 0, 0, 0.8)' } }, ] if item['status'] == '1': item['operation'] = [ { 'content': '修改', 'type': 'link', 'icon': 'antd-edit' } if 'system:menu:edit' in button_perms else {}, { 'content': '删除', 'type': 'link', 'icon': 'antd-delete' } if 'system:menu:remove' in button_perms else {}, ] else: item['operation'] = [ { 'content': '修改', 'type': 'link', 'icon': 'antd-edit' } if 'system:menu:edit' in button_perms else {}, { 'content': '新增', 'type': 'link', 'icon': 'antd-plus' } if 'system:menu:add' in button_perms else {}, { 'content': '删除', 'type': 'link', 'icon': 'antd-delete' } if 'system:menu:remove' in button_perms else {}, ] if item['status'] == '0': item['status'] = dict(tag='正常', color='blue') else: item['status'] = dict(tag='停用', color='volcano') table_data_new = list_to_tree(table_data, 'menu_id', 'parent_id') if fold_click: if not in_default_expanded_row_keys: return dict( menu_table_data=table_data_new, menu_table_key=str(uuid.uuid4()), menu_table_defaultexpandedrowkeys=default_expanded_row_keys, api_check_token_trigger={'timestamp': time.time()}, fold_click=None ) return dict( menu_table_data=table_data_new, menu_table_key=str(uuid.uuid4()), menu_table_defaultexpandedrowkeys=[], api_check_token_trigger={'timestamp': time.time()}, fold_click=None ) return dict( menu_table_data=dash.no_update, menu_table_key=dash.no_update, menu_table_defaultexpandedrowkeys=dash.no_update, api_check_token_trigger={'timestamp': time.time()}, fold_click=None ) return dict( menu_table_data=dash.no_update, menu_table_key=dash.no_update, menu_table_defaultexpandedrowkeys=dash.no_update, api_check_token_trigger=dash.no_update, fold_click=None ) # 重置菜单搜索表单数据回调 app.clientside_callback( ''' (reset_click) => { if (reset_click) { return [null, null, {'type': 'reset'}] } return window.dash_clientside.no_update; } ''', [Output('menu-menu_name-input', 'value'), Output('menu-status-select', 'value'), Output('menu-operations-store', 'data')], Input('menu-reset', 'nClicks'), prevent_initial_call=True ) # 隐藏/显示菜单搜索表单回调 app.clientside_callback( ''' (hidden_click, hidden_status) => { if (hidden_click) { return [ !hidden_status, hidden_status ? '隐藏搜索' : '显示搜索' ] } return window.dash_clientside.no_update; } ''', [Output('menu-search-form-container', 'hidden'), Output('menu-hidden-tooltip', 'title')], Input('menu-hidden', 'nClicks'), State('menu-search-form-container', 'hidden'), prevent_initial_call=True ) @app.callback( [Output('menu-icon', 'value'), Output('menu-icon', 'prefix')], Input('icon-category', 'value'), prevent_initial_call=True ) def get_select_icon(icon): """ 获取新增或编辑表单中选择的icon回调 """ if icon: return [ icon, fac.AntdIcon(icon=icon) ] raise PreventUpdate @app.callback( output=dict( modal=dict(visible=Output('menu-modal', 'visible', allow_duplicate=True), title=Output('menu-modal', 'title')), form_value=dict( parent_tree=Output('menu-parent_id', 'treeData'), parent_id=Output('menu-parent_id', 'value'), menu_type=Output('menu-menu_type', 'value'), icon=Output('menu-icon', 'value', allow_duplicate=True), icon_prefix=Output('menu-icon', 'prefix', allow_duplicate=True), icon_category=Output('icon-category', 'value'), menu_name=Output('menu-menu_name', 'value'), order_num=Output('menu-order_num', 'value') ), form_validate=[ Output('menu-parent_id-form-item', 'validateStatus', allow_duplicate=True), Output('menu-menu_name-form-item', 'validateStatus', allow_duplicate=True), Output('menu-order_num-form-item', 'validateStatus', allow_duplicate=True), Output('menu-parent_id-form-item', 'help', allow_duplicate=True), Output('menu-menu_name-form-item', 'help', allow_duplicate=True), Output('menu-order_num-form-item', 'help', allow_duplicate=True) ], other=dict( api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True), edit_row_info=Output('menu-edit-id-store', 'data'), modal_type=Output('menu-operations-store-bk', 'data') ) ), inputs=dict( operation_click=Input({'type': 'menu-operation-button', 'index': ALL}, 'nClicks'), button_click=Input('menu-list-table', 'nClicksButton') ), state=dict( clicked_content=State('menu-list-table', 'clickedContent'), recently_button_clicked_row=State('menu-list-table', 'recentlyButtonClickedRow') ), prevent_initial_call=True ) def add_edit_menu_modal(operation_click, button_click, clicked_content, recently_button_clicked_row): """ 显示新增或编辑菜单弹窗回调 """ trigger_id = dash.ctx.triggered_id if trigger_id == {'index': 'add', 'type': 'menu-operation-button'} or (trigger_id == 'menu-list-table' and clicked_content != '删除'): menu_params = dict(menu_name='') if clicked_content == '修改': tree_info = get_menu_tree_for_edit_option_api(menu_params) else: tree_info = get_menu_tree_api(menu_params) if tree_info['code'] == 200: tree_data = tree_info['data'] if trigger_id == {'index': 'add', 'type': 'menu-operation-button'}: return dict( modal=dict(visible=True, title='新增菜单'), form_value=dict( parent_tree=tree_data, parent_id='0', menu_type='M', icon=None, icon_prefix=None, icon_category=None, menu_name=None, order_num=None ), form_validate=[None] * 6, other=dict( api_check_token_trigger={'timestamp': time.time()}, edit_row_info=None, modal_type={'type': 'add'} ) ) elif trigger_id == 'menu-list-table' and clicked_content == '新增': return dict( modal=dict(visible=True, title='新增菜单'), form_value=dict( parent_tree=tree_data, parent_id=str(recently_button_clicked_row['key']), menu_type='M', icon=None, icon_prefix=None, icon_category=None, menu_name=None, order_num=None ), form_validate=[None] * 6, other=dict( api_check_token_trigger={'timestamp': time.time()}, edit_row_info=None, modal_type={'type': 'add'} ) ) elif trigger_id == 'menu-list-table' and clicked_content == '修改': menu_id = int(recently_button_clicked_row['key']) menu_info_res = get_menu_detail_api(menu_id=menu_id) if menu_info_res['code'] == 200: menu_info = menu_info_res['data'] return dict( modal=dict(visible=True, title='编辑菜单'), form_value=dict( parent_tree=tree_data, parent_id=str(menu_info.get('parent_id')), menu_type=menu_info.get('menu_type'), icon=menu_info.get('icon'), icon_prefix=fac.AntdIcon(icon=menu_info.get('icon')), icon_category=menu_info.get('icon'), menu_name=menu_info.get('menu_name'), order_num=menu_info.get('order_num') ), form_validate=[None] * 6, other=dict( api_check_token_trigger={'timestamp': time.time()}, edit_row_info=menu_info, modal_type={'type': 'edit'} ) ) return dict( modal=dict(visible=dash.no_update, title=dash.no_update), form_value=dict( parent_tree=dash.no_update, parent_id=dash.no_update, menu_type=dash.no_update, icon=dash.no_update, icon_prefix=dash.no_update, icon_category=dash.no_update, menu_name=dash.no_update, order_num=dash.no_update ), form_validate=[dash.no_update] * 6, other=dict( api_check_token_trigger={'timestamp': time.time()}, edit_row_info=None, modal_type=None ) ) raise PreventUpdate @app.callback( [Output('content-by-menu-type', 'children'), Output('content-by-menu-type', 'key'), Output('menu-modal-menu-type-store', 'data')], Input('menu-menu_type', 'value'), prevent_initial_call=True ) def get_bottom_content(menu_value): """ 根据不同菜单类型渲染不同的子区域 """ if menu_value == 'M': return [content_type.render(), str(uuid.uuid4()), {'type': 'M'}] elif menu_value == 'C': return [menu_type.render(), str(uuid.uuid4()), {'type': 'C'}] elif menu_value == 'F': return [button_type.render(), str(uuid.uuid4()), {'type': 'F'}] raise PreventUpdate @app.callback( [Output('menu-modal-M-trigger', 'data'), Output('menu-modal-C-trigger', 'data'), Output('menu-modal-F-trigger', 'data')], Input('menu-modal', 'okCounts'), State('menu-modal-menu-type-store', 'data'), ) def modal_confirm_trigger(confirm, menu_type): """ 增加触发器,根据不同菜单类型触发不同的回调,解决组件不存在回调异常的问题 """ if confirm: if menu_type.get('type') == 'M': return [ {'timestamp': time.time()}, dash.no_update, dash.no_update ] if menu_type.get('type') == 'C': return [ dash.no_update, {'timestamp': time.time()}, dash.no_update ] if menu_type.get('type') == 'F': return [ dash.no_update, dash.no_update, {'timestamp': time.time()} ] raise PreventUpdate @app.callback( [Output('menu-delete-text', 'children'), Output('menu-delete-confirm-modal', 'visible'), Output('menu-delete-ids-store', 'data')], [Input('menu-list-table', 'nClicksButton')], [State('menu-list-table', 'clickedContent'), State('menu-list-table', 'recentlyButtonClickedRow')], prevent_initial_call=True ) def menu_delete_modal(button_click, clicked_content, recently_button_clicked_row): """ 显示删除菜单二次确认弹窗回调 """ if button_click: if clicked_content == '删除': menu_ids = recently_button_clicked_row['key'] else: return dash.no_update return [ f'是否确认删除菜单编号为{menu_ids}的菜单?', True, {'menu_ids': menu_ids} ] raise PreventUpdate @app.callback( [Output('menu-operations-store', 'data', allow_duplicate=True), Output('api-check-token', 'data', allow_duplicate=True), Output('global-message-container', 'children', allow_duplicate=True)], Input('menu-delete-confirm-modal', 'okCounts'), State('menu-delete-ids-store', 'data'), prevent_initial_call=True ) def menu_delete_confirm(delete_confirm, menu_ids_data): """ 删除菜单弹窗确认回调,实现删除操作 """ if delete_confirm: params = menu_ids_data delete_button_info = delete_menu_api(params) if delete_button_info['code'] == 200: return [ {'type': 'delete'}, {'timestamp': time.time()}, fuc.FefferyFancyMessage('删除成功', type='success') ] return [ dash.no_update, {'timestamp': time.time()}, fuc.FefferyFancyMessage('删除失败', type='error') ] raise PreventUpdate