From 3f3a3b2b1e5e62fa0f7d8427e6b095b0b592b60e Mon Sep 17 00:00:00 2001
From: xlf <3055204202@qq.com>
Date: Sun, 24 Sep 2023 12:50:08 +0800
Subject: [PATCH 1/5] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E6=A0=87=E7=AD=BE?=
=?UTF-8?q?=E9=A1=B5=E5=8F=B3=E9=94=AE=E5=BF=AB=E6=8D=B7=E8=8F=9C=E5=8D=95?=
=?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=88fac>=3D0.2.10rc18=EF=BC=89=20#I838PN?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../callbacks/layout_c/head_c.py | 2 +-
.../callbacks/layout_c/index_c.py | 313 ++++++++++++++++--
.../views/layout/components/content.py | 19 +-
3 files changed, 298 insertions(+), 36 deletions(-)
diff --git a/dash-fastapi-frontend/callbacks/layout_c/head_c.py b/dash-fastapi-frontend/callbacks/layout_c/head_c.py
index e0e4353..f462f7c 100644
--- a/dash-fastapi-frontend/callbacks/layout_c/head_c.py
+++ b/dash-fastapi-frontend/callbacks/layout_c/head_c.py
@@ -72,7 +72,7 @@ app.clientside_callback(
return true;
}
''',
- Output('trigger-reload-output', 'reload'),
+ Output('trigger-reload-output', 'reload', allow_duplicate=True),
Input('index-reload', 'nClicks'),
prevent_initial_call=True
)
diff --git a/dash-fastapi-frontend/callbacks/layout_c/index_c.py b/dash-fastapi-frontend/callbacks/layout_c/index_c.py
index 3241995..376a569 100644
--- a/dash-fastapi-frontend/callbacks/layout_c/index_c.py
+++ b/dash-fastapi-frontend/callbacks/layout_c/index_c.py
@@ -1,5 +1,6 @@
import dash
from dash.dependencies import Input, Output, State
+from dash.exceptions import PreventUpdate
import feffery_antd_components as fac
from jsonpath_ng import parse
from flask import json
@@ -11,8 +12,8 @@ from utils.tree_tool import find_title_by_key, find_modules_by_key, find_href_by
@app.callback(
- [Output('tabs-container', 'items'),
- Output('tabs-container', 'activeKey')],
+ [Output('tabs-container', 'items', allow_duplicate=True),
+ Output('tabs-container', 'activeKey', allow_duplicate=True)],
[Input('index-side-menu', 'currentKey'),
Input('tabs-container', 'latestDeletePane')],
[State('tabs-container', 'items'),
@@ -37,6 +38,7 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
# 先前已更新为非None的按钮的nClicks误触发通知弹出回调
parser = parse('$..nClicks')
origin_items = parser.update(origin_items, None)
+ new_items = dash.Patch()
if trigger_id == 'index-side-menu':
@@ -57,27 +59,58 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
# 判断当前选中的菜单栏项是否存在module,如果有,则动态导入module,否则返回404页面
menu_modules = find_modules_by_key(menu_info.get('menu_info'), currentKey)
+ for index, item in enumerate(origin_items):
+ if {'key': '关闭右侧', 'label': '关闭右侧', 'icon': 'antd-arrow-right'} not in item['contextMenu']:
+ item['contextMenu'].insert(-1, {
+ 'key': '关闭右侧',
+ 'label': '关闭右侧',
+ 'icon': 'antd-arrow-right'
+ })
+ new_items[index]['contextMenu'] = item['contextMenu']
+ context_menu = [
+ {
+ 'key': '刷新页面',
+ 'label': '刷新页面',
+ 'icon': 'antd-reload'
+ },
+ {
+ 'key': '关闭当前',
+ 'label': '关闭当前',
+ 'icon': 'antd-close'
+ },
+ {
+ 'key': '关闭其他',
+ 'label': '关闭其他',
+ 'icon': 'antd-close-circle'
+ },
+ {
+ 'key': '全部关闭',
+ 'label': '全部关闭',
+ 'icon': 'antd-close-circle'
+ }
+ ]
+ if len(origin_items) != 1:
+ context_menu.insert(-1, {
+ 'key': '关闭左侧',
+ 'label': '关闭左侧',
+ 'icon': 'antd-arrow-left'
+ })
if menu_modules:
if menu_modules == 'link':
- return [dash.no_update] * 2
+ raise PreventUpdate
else:
# 否则追加子项返回
# 其中若各标签页内元素类似,则推荐配合模式匹配构建交互逻辑
- return [
- [
- *origin_items,
- {
- 'label': menu_title,
- 'key': currentKey,
- 'children': eval('views.' + menu_modules + '.render(button_perms)'),
- }
- ],
- currentKey
- ]
-
- return [
- [
- *origin_items,
+ new_items.append(
+ {
+ 'label': menu_title,
+ 'key': currentKey,
+ 'children': eval('views.' + menu_modules + '.render(button_perms)'),
+ 'contextMenu': context_menu
+ }
+ )
+ else:
+ new_items.append(
{
'label': menu_title,
'key': currentKey,
@@ -90,34 +123,246 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
'paddingTop': 0
}
),
+ 'contextMenu': context_menu
}
- ],
+ )
+
+ return [
+ new_items,
currentKey
]
elif trigger_id == 'tabs-container':
- # 若要删除的是当前正激活的标签页
- if latestDeletePane == activeKey:
+ # 如果删除的是当前标签页则回到最后新增的标签页,否则保持当前标签页不变
+ for index, item in enumerate(origin_items):
+ if item['key'] == latestDeletePane:
+ context_menu = [
+ {
+ 'key': '刷新页面',
+ 'label': '刷新页面',
+ 'icon': 'antd-reload'
+ },
+ {
+ 'key': '关闭其他',
+ 'label': '关闭其他',
+ 'icon': 'antd-close-circle'
+ },
+ {
+ 'key': '全部关闭',
+ 'label': '全部关闭',
+ 'icon': 'antd-close-circle'
+ }
+ ]
+ if index == 1 and len(origin_items) == 2:
+ new_items[0]['contextMenu'] = context_menu
+ elif len(origin_items) == 3:
+ context_menu.insert(1, {
+ 'key': '关闭当前',
+ 'label': '关闭当前',
+ 'icon': 'antd-close'
+ })
+ if index == 1:
+ new_items[2]['contextMenu'] = context_menu
+ if index == 2:
+ new_items[1]['contextMenu'] = context_menu
+ else:
+ if index == len(origin_items) - 1:
+ new_items[index - 1]['contextMenu'] = item['contextMenu']
+ new_items.remove(item)
+ break
+ new_origin_items = [
+ item for item in
+ origin_items if item['key'] != latestDeletePane
+ ]
+
+ return [
+ new_items,
+ new_origin_items[-1]['key'] if activeKey == latestDeletePane else activeKey
+ ]
+
+ raise PreventUpdate
+
+
+@app.callback(
+ [Output('tabs-container', 'items', allow_duplicate=True),
+ Output('tabs-container', 'activeKey', allow_duplicate=True),
+ Output('trigger-reload-output', 'reload', allow_duplicate=True)],
+ Input('tabs-container', 'clickedContextMenu'),
+ [State('tabs-container', 'items'),
+ State('tabs-container', 'activeKey')],
+ prevent_initial_call=True
+)
+def handle_via_context_menu(clickedContextMenu, origin_items, activeKey):
+ """
+ 基于标签页标题右键菜单的额外标签页控制
+ """
+ if clickedContextMenu['menuKey'] == '刷新页面':
+
+ return [
+ dash.no_update,
+ dash.no_update,
+ True
+ ]
+
+ if '关闭' in clickedContextMenu['menuKey']:
+ new_items = dash.Patch()
+ if clickedContextMenu['menuKey'] == '关闭当前':
+ for index, item in enumerate(origin_items):
+ if item['key'] == clickedContextMenu['tabKey']:
+ context_menu = [
+ {
+ 'key': '刷新页面',
+ 'label': '刷新页面',
+ 'icon': 'antd-reload'
+ },
+ {
+ 'key': '关闭其他',
+ 'label': '关闭其他',
+ 'icon': 'antd-close-circle'
+ },
+ {
+ 'key': '全部关闭',
+ 'label': '全部关闭',
+ 'icon': 'antd-close-circle'
+ }
+ ]
+ if index == 1 and len(origin_items) == 2:
+ new_items[0]['contextMenu'] = context_menu
+ elif len(origin_items) == 3:
+ context_menu.insert(1, {
+ 'key': '关闭当前',
+ 'label': '关闭当前',
+ 'icon': 'antd-close'
+ })
+ if index == 1:
+ new_items[2]['contextMenu'] = context_menu
+ if index == 2:
+ new_items[1]['contextMenu'] = context_menu
+ else:
+ if index == len(origin_items) - 1:
+ new_items[index - 1]['contextMenu'] = item['contextMenu']
+ new_items.remove(item)
+ break
+ new_origin_items = [
+ item for item in
+ origin_items if item['key'] != clickedContextMenu['tabKey']
+ ]
+
return [
- [
- item
- for item in origin_items
- if item['key'] != latestDeletePane
- ],
- '首页'
+ new_items,
+ new_origin_items[-1]['key'] if activeKey == clickedContextMenu['tabKey'] else activeKey,
+ dash.no_update
]
- # 否则保持当前激活的标签页子项不变,删去目标子项
+ elif clickedContextMenu['menuKey'] == '关闭其他':
+ for item in origin_items:
+ if item['key'] != clickedContextMenu['tabKey'] and item['key'] != '首页':
+ new_items.remove(item)
+ context_menu = [
+ {
+ 'key': '刷新页面',
+ 'label': '刷新页面',
+ 'icon': 'antd-reload'
+ },
+ {
+ 'key': '关闭其他',
+ 'label': '关闭其他',
+ 'icon': 'antd-close-circle'
+ },
+ {
+ 'key': '全部关闭',
+ 'label': '全部关闭',
+ 'icon': 'antd-close-circle'
+ }
+ ]
+ if clickedContextMenu['tabKey'] == '首页':
+ new_items[0]['contextMenu'] = context_menu
+ else:
+ context_menu.insert(1, {
+ 'key': '关闭当前',
+ 'label': '关闭当前',
+ 'icon': 'antd-close'
+ })
+ new_items[1]['contextMenu'] = context_menu
+
+ return [
+ new_items,
+ clickedContextMenu['tabKey'],
+ dash.no_update
+ ]
+
+ elif clickedContextMenu['menuKey'] == '关闭左侧':
+ current_index = 0
+ for index, item in enumerate(origin_items):
+ if item['key'] == clickedContextMenu['tabKey']:
+ current_index = index
+ item['contextMenu'].remove({
+ 'key': '关闭左侧',
+ 'label': '关闭左侧',
+ 'icon': 'antd-arrow-left'
+ })
+ new_items[index]['contextMenu'] = item['contextMenu']
+ break
+ for item in origin_items[1:current_index]:
+ new_items.remove(item)
+
+ return [
+ new_items,
+ clickedContextMenu['tabKey'],
+ dash.no_update
+ ]
+
+ elif clickedContextMenu['menuKey'] == '关闭右侧':
+ current_index = 0
+ for index, item in enumerate(origin_items):
+ if item['key'] == clickedContextMenu['tabKey']:
+ current_index = index
+ item['contextMenu'].remove({
+ 'key': '关闭右侧',
+ 'label': '关闭右侧',
+ 'icon': 'antd-arrow-right'
+ })
+ new_items[index]['contextMenu'] = item['contextMenu']
+ break
+ for item in origin_items[current_index+1:]:
+ new_items.remove(item)
+
+ return [
+ new_items,
+ clickedContextMenu['tabKey'],
+ dash.no_update
+ ]
+
+ for item in origin_items:
+ if item['key'] != '首页':
+ new_items.remove(item)
+ new_items[0]['contextMenu'] = [
+ {
+ 'key': '刷新页面',
+ 'label': '刷新页面',
+ 'icon': 'antd-reload'
+ },
+ {
+ 'key': '关闭其他',
+ 'label': '关闭其他',
+ 'icon': 'antd-close-circle'
+ },
+ {
+ 'key': '全部关闭',
+ 'label': '全部关闭',
+ 'icon': 'antd-close-circle'
+ }
+ ]
+ # 否则则为全部关闭
return [
- [
- item
- for item in origin_items
- if item['key'] != latestDeletePane
- ],
+ new_items,
+ '首页',
dash.no_update
]
+ raise PreventUpdate
+
# 页首面包屑和hash回调
@app.callback(
@@ -175,4 +420,4 @@ def get_current_breadcrumbs(active_key, menu_info):
current_href
]
- return dash.no_update
+ raise PreventUpdate
diff --git a/dash-fastapi-frontend/views/layout/components/content.py b/dash-fastapi-frontend/views/layout/components/content.py
index 454dbd8..c0f577d 100644
--- a/dash-fastapi-frontend/views/layout/components/content.py
+++ b/dash-fastapi-frontend/views/layout/components/content.py
@@ -16,7 +16,24 @@ def render_main_content():
'label': '首页',
'key': '首页',
'closable': False,
- 'children': render_dashboard()
+ 'children': render_dashboard(),
+ 'contextMenu': [
+ {
+ 'key': '刷新页面',
+ 'label': '刷新页面',
+ 'icon': 'antd-reload'
+ },
+ {
+ 'key': '关闭其他',
+ 'label': '关闭其他',
+ 'icon': 'antd-close-circle'
+ },
+ {
+ 'key': '全部关闭',
+ 'label': '全部关闭',
+ 'icon': 'antd-close-circle'
+ }
+ ]
}
],
id='tabs-container',
--
Gitee
From d71f10b71c058686eef7d4cb2deca3360ed83452 Mon Sep 17 00:00:00 2001
From: xlf <3055204202@qq.com>
Date: Sun, 24 Sep 2023 22:30:55 +0800
Subject: [PATCH 2/5] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E6=A0=87=E7=AD=BE?=
=?UTF-8?q?=E9=A1=B5=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD=E7=9A=84bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../callbacks/layout_c/index_c.py | 62 +++++++++++--------
1 file changed, 37 insertions(+), 25 deletions(-)
diff --git a/dash-fastapi-frontend/callbacks/layout_c/index_c.py b/dash-fastapi-frontend/callbacks/layout_c/index_c.py
index 376a569..cfcb6d9 100644
--- a/dash-fastapi-frontend/callbacks/layout_c/index_c.py
+++ b/dash-fastapi-frontend/callbacks/layout_c/index_c.py
@@ -28,7 +28,7 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
具体策略:
1.当左侧某个菜单项被新选中,且右侧标签页子项尚未包含此项时,新建并切换
2.当左侧某个菜单项被新选中,且右侧标签页子项已包含此项时,切换
- 3.当右侧标签页子项某项被删除时,销毁对应标签页的同时切换回主标签页
+ 3.当右侧标签页子项某项被删除时,销毁对应标签页的同时切换回最后新增的标签页
"""
trigger_id = dash.ctx.triggered_id
@@ -154,18 +154,24 @@ def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, act
'icon': 'antd-close-circle'
}
]
- if index == 1 and len(origin_items) == 2:
- new_items[0]['contextMenu'] = context_menu
- elif len(origin_items) == 3:
- context_menu.insert(1, {
- 'key': '关闭当前',
- 'label': '关闭当前',
- 'icon': 'antd-close'
- })
- if index == 1:
- new_items[2]['contextMenu'] = context_menu
- if index == 2:
- new_items[1]['contextMenu'] = context_menu
+ if index == 1:
+ if len(origin_items) == 2:
+ new_items[0]['contextMenu'] = context_menu
+ else:
+ origin_items[2]['contextMenu'].remove({
+ 'key': '关闭左侧',
+ 'label': '关闭左侧',
+ 'icon': 'antd-arrow-left'
+ })
+ new_items[2]['contextMenu'] = origin_items[2]['contextMenu']
+ elif index == 2:
+ if len(origin_items) == 3:
+ origin_items[1]['contextMenu'].remove({
+ 'key': '关闭右侧',
+ 'label': '关闭右侧',
+ 'icon': 'antd-arrow-right'
+ })
+ new_items[1]['contextMenu'] = origin_items[1]['contextMenu']
else:
if index == len(origin_items) - 1:
new_items[index - 1]['contextMenu'] = item['contextMenu']
@@ -227,18 +233,24 @@ def handle_via_context_menu(clickedContextMenu, origin_items, activeKey):
'icon': 'antd-close-circle'
}
]
- if index == 1 and len(origin_items) == 2:
- new_items[0]['contextMenu'] = context_menu
- elif len(origin_items) == 3:
- context_menu.insert(1, {
- 'key': '关闭当前',
- 'label': '关闭当前',
- 'icon': 'antd-close'
- })
- if index == 1:
- new_items[2]['contextMenu'] = context_menu
- if index == 2:
- new_items[1]['contextMenu'] = context_menu
+ if index == 1:
+ if len(origin_items) == 2:
+ new_items[0]['contextMenu'] = context_menu
+ else:
+ origin_items[2]['contextMenu'].remove({
+ 'key': '关闭左侧',
+ 'label': '关闭左侧',
+ 'icon': 'antd-arrow-left'
+ })
+ new_items[2]['contextMenu'] = origin_items[2]['contextMenu']
+ elif index == 2:
+ if len(origin_items) == 3:
+ origin_items[1]['contextMenu'].remove({
+ 'key': '关闭右侧',
+ 'label': '关闭右侧',
+ 'icon': 'antd-arrow-right'
+ })
+ new_items[1]['contextMenu'] = origin_items[1]['contextMenu']
else:
if index == len(origin_items) - 1:
new_items[index - 1]['contextMenu'] = item['contextMenu']
--
Gitee
From e790b03d0d7f7c378988c781178e37b2159c64a4 Mon Sep 17 00:00:00 2001
From: xlf <3055204202@qq.com>
Date: Wed, 4 Oct 2023 10:53:18 +0800
Subject: [PATCH 3/5] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0=E4=BE=9D=E8=B5=96f?=
=?UTF-8?q?effery-utils-components=E7=89=88=E6=9C=AC=E4=B8=BA0.2.0b1?=
=?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=BA=95=E5=B1=82bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements.txt b/requirements.txt
index f6cbbfe..362cfa2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -25,7 +25,7 @@ fastapi==0.95.1
feffery-antd-charts==0.0.1rc17
feffery-antd-components==0.2.10rc17
feffery-markdown-components==0.2.10
-feffery-utils-components==0.1.28
+feffery-utils-components==0.2.0b1
Flask==2.2.5
Flask-Compress==1.13
greenlet==2.0.2
--
Gitee
From 7e8e35878f7409a604e74e2748ee8fbd90c47bc8 Mon Sep 17 00:00:00 2001
From: xlf <3055204202@qq.com>
Date: Wed, 4 Oct 2023 10:53:46 +0800
Subject: [PATCH 4/5] =?UTF-8?q?docs:=E6=9B=B4=E6=96=B0README=E6=96=87?=
=?UTF-8?q?=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 4420359..1771e4c 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-Dash-FastAPI-Admin v1.0.5
+Dash-FastAPI-Admin v1.0.6
基于Dash+FastAPI前后端分离的纯Python快速开发框架
-
+
-
+
@@ -24,7 +24,7 @@ Dash-FastAPI-Admin是一套全部开源的快速开发平台,毫无保留给
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
-2. 角色管理:角色菜单权限分配。
+2. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
3. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
4. 部门管理:配置系统组织机构(公司、部门、小组)。
5. 岗位管理:配置系统用户所属担任职务。
--
Gitee
From c37d86e5ad0f180c9ff38835d309ef30bb6fe20e Mon Sep 17 00:00:00 2001
From: xlf <3055204202@qq.com>
Date: Wed, 4 Oct 2023 10:54:48 +0800
Subject: [PATCH 5/5] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE?=
=?UTF-8?q?=E6=9D=83=E9=99=90=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=86=85=E7=BD=AE?=
=?UTF-8?q?=E4=BA=94=E7=A7=8D=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90=20#I848B?=
=?UTF-8?q?G?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../module_admin/aspect/data_scope.py | 35 +++
.../controller/role_controller.py | 18 ++
.../controller/user_controller.py | 5 +-
.../module_admin/dao/role_dao.py | 34 ++-
.../module_admin/dao/user_dao.py | 8 +-
.../module_admin/entity/vo/role_vo.py | 20 ++
.../module_admin/service/role_service.py | 36 +++
.../module_admin/service/user_service.py | 5 +-
dash-fastapi-frontend/api/role.py | 5 +
.../callbacks/system_c/role_c/data_scope_c.py | 251 ++++++++++++++++++
.../callbacks/system_c/role_c/role_c.py | 2 +-
.../views/system/role/__init__.py | 18 +-
.../views/system/role/data_scope.py | 175 ++++++++++++
13 files changed, 600 insertions(+), 12 deletions(-)
create mode 100644 dash-fastapi-backend/module_admin/aspect/data_scope.py
create mode 100644 dash-fastapi-frontend/callbacks/system_c/role_c/data_scope_c.py
create mode 100644 dash-fastapi-frontend/views/system/role/data_scope.py
diff --git a/dash-fastapi-backend/module_admin/aspect/data_scope.py b/dash-fastapi-backend/module_admin/aspect/data_scope.py
new file mode 100644
index 0000000..8afe135
--- /dev/null
+++ b/dash-fastapi-backend/module_admin/aspect/data_scope.py
@@ -0,0 +1,35 @@
+from fastapi import Depends
+from module_admin.entity.vo.user_vo import CurrentUserInfoServiceResponse
+from module_admin.service.login_service import get_current_user
+from typing import Optional
+
+
+class GetDataScope:
+ """
+ 获取当前用户数据权限对应的查询sql语句
+ """
+ def __init__(self, query_alias: Optional[str] = '', db_alias: Optional[str] = 'db'):
+ self.query_alias = query_alias
+ self.db_alias = db_alias
+
+ def __call__(self, current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
+ user_id = current_user.user.user_id
+ dept_id = current_user.user.dept_id
+ role_datascope_list = [dict(role_id=item.role_id, data_scope=int(item.data_scope)) for item in current_user.role]
+ max_data_scope_dict = min(role_datascope_list, key=lambda x: x['data_scope'])
+ max_role_id = max_data_scope_dict['role_id']
+ max_data_scope = max_data_scope_dict['data_scope']
+ if self.query_alias == '' or max_data_scope == 1 or user_id == 1:
+ param_sql = '1 == 1'
+ elif max_data_scope == 2:
+ param_sql = f'{self.query_alias}.dept_id.in_({self.db_alias}.query(SysRoleDept.dept_id).filter(SysRoleDept.role_id == {max_role_id}))'
+ elif max_data_scope == 3:
+ param_sql = f'{self.query_alias}.dept_id == {dept_id}'
+ elif max_data_scope == 4:
+ param_sql = f'{self.query_alias}.dept_id.in_({self.db_alias}.query(SysDept.dept_id).filter(or_(SysDept.dept_id == {dept_id}, func.find_in_set({dept_id}, SysDept.ancestors))))'
+ elif max_data_scope == 5:
+ param_sql = f'{self.query_alias}.user_id == {user_id}'
+ else:
+ param_sql = '1 == 0'
+
+ return param_sql
diff --git a/dash-fastapi-backend/module_admin/controller/role_controller.py b/dash-fastapi-backend/module_admin/controller/role_controller.py
index 9f0658d..55e68fd 100644
--- a/dash-fastapi-backend/module_admin/controller/role_controller.py
+++ b/dash-fastapi-backend/module_admin/controller/role_controller.py
@@ -75,6 +75,24 @@ async def edit_system_role(request: Request, edit_role: AddRoleModel, query_db:
except Exception as e:
logger.exception(e)
return response_500(data="", message=str(e))
+
+
+@roleController.patch("/role/dataScope", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
+@log_decorator(title='角色管理', business_type=4)
+async def edit_system_role_datascope(request: Request, role_data_scope: RoleDataScopeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
+ try:
+ role_data_scope.update_by = current_user.user.user_name
+ role_data_scope.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ role_data_scope_result = RoleService.role_datascope_services(query_db, role_data_scope)
+ if role_data_scope_result.is_success:
+ logger.info(role_data_scope_result.message)
+ return response_200(data=role_data_scope_result, message=role_data_scope_result.message)
+ else:
+ logger.warning(role_data_scope_result.message)
+ return response_400(data="", message=role_data_scope_result.message)
+ except Exception as e:
+ logger.exception(e)
+ return response_500(data="", message=str(e))
@roleController.post("/role/delete", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:remove'))])
diff --git a/dash-fastapi-backend/module_admin/controller/user_controller.py b/dash-fastapi-backend/module_admin/controller/user_controller.py
index a4c9b75..c7f9a30 100644
--- a/dash-fastapi-backend/module_admin/controller/user_controller.py
+++ b/dash-fastapi-backend/module_admin/controller/user_controller.py
@@ -12,6 +12,7 @@ from utils.response_util import *
from utils.log_util import *
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
+from module_admin.aspect.data_scope import GetDataScope
from module_admin.annotation.log_annotation import log_decorator
@@ -19,11 +20,11 @@ userController = APIRouter(dependencies=[Depends(get_current_user)])
@userController.post("/user/get", response_model=UserPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))])
-async def get_system_user_list(request: Request, user_page_query: UserPageObject, query_db: Session = Depends(get_db)):
+async def get_system_user_list(request: Request, user_page_query: UserPageObject, query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))):
try:
user_query = UserQueryModel(**user_page_query.dict())
# 获取全量数据
- user_query_result = UserService.get_user_list_services(query_db, user_query)
+ user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql)
# 分页操作
user_page_query_result = get_page_obj(user_query_result, user_page_query.page_num, user_page_query.page_size)
logger.info('获取成功')
diff --git a/dash-fastapi-backend/module_admin/dao/role_dao.py b/dash-fastapi-backend/module_admin/dao/role_dao.py
index 52f13f6..8ed51b4 100644
--- a/dash-fastapi-backend/module_admin/dao/role_dao.py
+++ b/dash-fastapi-backend/module_admin/dao/role_dao.py
@@ -1,8 +1,9 @@
from sqlalchemy import and_, desc
from sqlalchemy.orm import Session
-from module_admin.entity.do.role_do import SysRole, SysRoleMenu
+from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
+from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.menu_do import SysMenu
-from module_admin.entity.vo.role_vo import RoleModel, RoleMenuModel, RoleQueryModel, RoleDetailModel
+from module_admin.entity.vo.role_vo import RoleModel, RoleMenuModel, RoleDeptModel, RoleQueryModel, RoleDetailModel
from utils.time_format_util import list_format_datetime, object_format_datetime
from datetime import datetime, time
@@ -74,9 +75,15 @@ class RoleDao:
.outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
.outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0)) \
.distinct().all()
+ query_role_dept_info = db.query(SysDept).select_from(SysRole) \
+ .filter(SysRole.del_flag == 0, SysRole.role_id == role_id) \
+ .outerjoin(SysRoleDept, SysRole.role_id == SysRoleDept.role_id) \
+ .outerjoin(SysDept, and_(SysRoleDept.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
+ .distinct().all()
results = dict(
role=object_format_datetime(query_role_basic_info),
menu=list_format_datetime(query_role_menu_info),
+ dept=list_format_datetime(query_role_dept_info),
)
return RoleDetailModel(**results)
@@ -177,3 +184,26 @@ class RoleDao:
db.query(SysRoleMenu) \
.filter(SysRoleMenu.role_id == role_menu.role_id) \
.delete()
+
+ @classmethod
+ def add_role_dept_dao(cls, db: Session, role_dept: RoleDeptModel):
+ """
+ 新增角色部门关联信息数据库操作
+ :param db: orm对象
+ :param role_dept: 用户角色部门关联对象
+ :return:
+ """
+ db_role_dept = SysRoleDept(**role_dept.dict())
+ db.add(db_role_dept)
+
+ @classmethod
+ def delete_role_dept_dao(cls, db: Session, role_dept: RoleDeptModel):
+ """
+ 删除角色部门关联信息数据库操作
+ :param db: orm对象
+ :param role_dept: 角色部门关联对象
+ :return:
+ """
+ db.query(SysRoleDept) \
+ .filter(SysRoleDept.role_id == role_dept.role_id) \
+ .delete()
diff --git a/dash-fastapi-backend/module_admin/dao/user_dao.py b/dash-fastapi-backend/module_admin/dao/user_dao.py
index 19ea73a..74ee023 100644
--- a/dash-fastapi-backend/module_admin/dao/user_dao.py
+++ b/dash-fastapi-backend/module_admin/dao/user_dao.py
@@ -1,7 +1,7 @@
from sqlalchemy import and_, or_, desc, func
from sqlalchemy.orm import Session
from module_admin.entity.do.user_do import SysUser, SysUserRole, SysUserPost
-from module_admin.entity.do.role_do import SysRole, SysRoleMenu
+from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.post_do import SysPost
from module_admin.entity.do.menu_do import SysMenu
@@ -139,11 +139,12 @@ class UserDao:
return CurrentUserInfo(**results)
@classmethod
- def get_user_list(cls, db: Session, query_object: UserQueryModel):
+ def get_user_list(cls, db: Session, query_object: UserQueryModel, data_scope_sql: str):
"""
根据查询参数获取用户列表信息
:param db: orm对象
:param query_object: 查询参数对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
:return: 用户列表信息对象
"""
user_list = db.query(SysUser, SysDept) \
@@ -160,7 +161,8 @@ class UserDao:
SysUser.create_time.between(
datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
- if query_object.create_time_start and query_object.create_time_end else True
+ if query_object.create_time_start and query_object.create_time_end else True,
+ eval(data_scope_sql)
) \
.outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
.distinct().all()
diff --git a/dash-fastapi-backend/module_admin/entity/vo/role_vo.py b/dash-fastapi-backend/module_admin/entity/vo/role_vo.py
index 6532c58..0f94d43 100644
--- a/dash-fastapi-backend/module_admin/entity/vo/role_vo.py
+++ b/dash-fastapi-backend/module_admin/entity/vo/role_vo.py
@@ -1,6 +1,7 @@
from pydantic import BaseModel
from typing import Union, Optional, List
from module_admin.entity.vo.user_vo import RoleModel
+from module_admin.entity.vo.dept_vo import DeptModel
from module_admin.entity.vo.menu_vo import MenuModel
@@ -15,6 +16,17 @@ class RoleMenuModel(BaseModel):
orm_mode = True
+class RoleDeptModel(BaseModel):
+ """
+ 角色和部门关联表对应pydantic模型
+ """
+ role_id: Optional[int]
+ dept_id: Optional[int]
+
+ class Config:
+ orm_mode = True
+
+
class RoleQueryModel(RoleModel):
"""
角色管理不分页查询模型
@@ -65,6 +77,13 @@ class AddRoleModel(RoleModel):
type: Optional[str]
+class RoleDataScopeModel(RoleModel):
+ """
+ 角色数据权限模型
+ """
+ dept_id: Optional[str]
+
+
class DeleteRoleModel(BaseModel):
"""
删除角色模型
@@ -80,3 +99,4 @@ class RoleDetailModel(BaseModel):
"""
role: Union[RoleModel, None]
menu: List[Union[MenuModel, None]]
+ dept: List[Union[DeptModel, None]]
diff --git a/dash-fastapi-backend/module_admin/service/role_service.py b/dash-fastapi-backend/module_admin/service/role_service.py
index 06a9867..7690255 100644
--- a/dash-fastapi-backend/module_admin/service/role_service.py
+++ b/dash-fastapi-backend/module_admin/service/role_service.py
@@ -100,6 +100,42 @@ class RoleService:
return CrudRoleResponse(**result)
+ @classmethod
+ def role_datascope_services(cls, result_db: Session, page_object: RoleDataScopeModel):
+ """
+ 分配角色数据权限service
+ :param result_db: orm对象
+ :param page_object: 角色数据权限对象
+ :return: 分配角色数据权限结果
+ """
+ edit_role = page_object.dict(exclude_unset=True)
+ del edit_role['dept_id']
+ role_info = cls.detail_role_services(result_db, edit_role.get('role_id'))
+ if role_info:
+ if role_info.role.role_name != page_object.role_name:
+ role = RoleDao.get_role_by_info(result_db, RoleModel(**dict(role_name=page_object.role_name)))
+ if role:
+ result = dict(is_success=False, message='角色名称已存在')
+ return CrudRoleResponse(**result)
+ try:
+ RoleDao.edit_role_dao(result_db, edit_role)
+ role_id_dict = dict(role_id=page_object.role_id)
+ RoleDao.delete_role_dept_dao(result_db, RoleDeptModel(**role_id_dict))
+ if page_object.dept_id and page_object.data_scope == '2':
+ dept_id_list = page_object.dept_id.split(',')
+ for dept in dept_id_list:
+ dept_dict = dict(role_id=page_object.role_id, dept_id=dept)
+ RoleDao.add_role_dept_dao(result_db, RoleDeptModel(**dept_dict))
+ result_db.commit()
+ result = dict(is_success=True, message='分配成功')
+ except Exception as e:
+ result_db.rollback()
+ result = dict(is_success=False, message=str(e))
+ else:
+ result = dict(is_success=False, message='角色不存在')
+
+ return CrudRoleResponse(**result)
+
@classmethod
def delete_role_services(cls, result_db: Session, page_object: DeleteRoleModel):
"""
diff --git a/dash-fastapi-backend/module_admin/service/user_service.py b/dash-fastapi-backend/module_admin/service/user_service.py
index 4f45111..1d8260e 100644
--- a/dash-fastapi-backend/module_admin/service/user_service.py
+++ b/dash-fastapi-backend/module_admin/service/user_service.py
@@ -10,14 +10,15 @@ class UserService:
"""
@classmethod
- def get_user_list_services(cls, result_db: Session, query_object: UserQueryModel):
+ def get_user_list_services(cls, result_db: Session, query_object: UserQueryModel, data_scope_sql: str):
"""
获取用户列表信息service
:param result_db: orm对象
:param query_object: 查询参数对象
+ :param data_scope_sql: 数据权限对应的查询sql语句
:return: 用户列表信息对象
"""
- user_list_result = UserDao.get_user_list(result_db, query_object)
+ user_list_result = UserDao.get_user_list(result_db, query_object, data_scope_sql)
return user_list_result
diff --git a/dash-fastapi-frontend/api/role.py b/dash-fastapi-frontend/api/role.py
index 97f9fc3..2ddbeba 100644
--- a/dash-fastapi-frontend/api/role.py
+++ b/dash-fastapi-frontend/api/role.py
@@ -21,6 +21,11 @@ def edit_role_api(page_obj: dict):
return api_request(method='patch', url='/system/role/edit', is_headers=True, json=page_obj)
+def role_datascope_api(page_obj: dict):
+
+ return api_request(method='patch', url='/system/role/dataScope', is_headers=True, json=page_obj)
+
+
def delete_role_api(page_obj: dict):
return api_request(method='post', url='/system/role/delete', is_headers=True, json=page_obj)
diff --git a/dash-fastapi-frontend/callbacks/system_c/role_c/data_scope_c.py b/dash-fastapi-frontend/callbacks/system_c/role_c/data_scope_c.py
new file mode 100644
index 0000000..808dab9
--- /dev/null
+++ b/dash-fastapi-frontend/callbacks/system_c/role_c/data_scope_c.py
@@ -0,0 +1,251 @@
+import dash
+import time
+from dash.dependencies import Input, Output, State, ALL
+from dash.exceptions import PreventUpdate
+import feffery_utils_components as fuc
+
+from server import app
+from api.role import get_role_detail_api, role_datascope_api
+from api.dept import get_dept_tree_api, get_dept_list_api
+
+
+@app.callback(
+ Output('role-dept-perms', 'expandedKeys'),
+ Input('role-dept-perms-radio-fold-unfold', 'checked'),
+ State('role-dept-store', 'data'),
+ prevent_initial_call=True
+)
+def fold_unfold_role_dept(fold_unfold, dept_info):
+ """
+ 数据权限表单中展开/折叠checkbox回调
+ """
+ if dept_info:
+ default_expanded_keys = []
+ for item in dept_info:
+ if fold_unfold:
+ default_expanded_keys.append(str(item.get('dept_id')))
+ else:
+ if item.get('parent_id') == 0:
+ default_expanded_keys.append(str(item.get('dept_id')))
+
+ return default_expanded_keys
+
+ return dash.no_update
+
+
+@app.callback(
+ Output('role-dept-perms', 'checkedKeys', allow_duplicate=True),
+ Input('role-dept-perms-radio-all-none', 'checked'),
+ State('role-dept-store', 'data'),
+ prevent_initial_call=True
+)
+def all_none_role_dept_mode(all_none, dept_info):
+ """
+ 数据权限表单中全选/全不选checkbox回调
+ """
+ if dept_info:
+ default_expanded_keys = []
+ for item in dept_info:
+ if item.get('parent_id') == 0:
+ default_expanded_keys.append(str(item.get('dept_id')))
+
+ if all_none:
+ return [str(item.get('dept_id')) for item in dept_info]
+ else:
+ return []
+
+ return dash.no_update
+
+
+@app.callback(
+ [Output('role-dept-perms', 'checkStrictly'),
+ Output('role-dept-perms', 'checkedKeys', allow_duplicate=True)],
+ Input('role-dept-perms-radio-parent-children', 'checked'),
+ State('current-role-dept-store', 'data'),
+ prevent_initial_call=True
+)
+def change_role_dept_mode(parent_children, current_role_dept):
+ """
+ 数据权限表单中父子联动checkbox回调
+ """
+ checked_dept = []
+ if parent_children:
+ if current_role_dept:
+ for item in current_role_dept:
+ has_children = False
+ for other_item in current_role_dept:
+ if other_item['parent_id'] == item['dept_id']:
+ has_children = True
+ break
+ if not has_children:
+ checked_dept.append(str(item.get('dept_id')))
+ return [False, checked_dept]
+ else:
+ if current_role_dept:
+ checked_dept = [str(item.get('dept_id')) for item in current_role_dept if item] or []
+ return [True, checked_dept]
+
+
+@app.callback(
+ output=dict(
+ dept_div=Output('role-dept-perms-div', 'hidden'),
+ dept_perms_tree=Output('role-dept-perms', 'treeData'),
+ dept_perms_expanded_check=Output('role-dept-perms-radio-fold-unfold', 'checked'),
+ dept_perms_checkedkeys=Output('role-dept-perms', 'checkedKeys', allow_duplicate=True),
+ dept_perms_halfcheckedkeys=Output('role-dept-perms', 'halfCheckedKeys', allow_duplicate=True),
+ role_dept=Output('role-dept-store', 'data'),
+ current_role_dept=Output('current-role-dept-store', 'data')
+ ),
+ inputs=dict(
+ data_scope=Input({'type': 'datascope-form-value', 'index': 'data_scope'}, 'value'),
+ ),
+ state=dict(
+ role_info=State('role-edit-id-store', 'data')
+ ),
+ prevent_initial_call=True
+)
+def get_role_dept_info(data_scope, role_info):
+ if data_scope == '2':
+ tree_info = get_dept_tree_api({})
+ dept_list_info = get_dept_list_api({})
+ if tree_info.get('code') == 200 and dept_list_info.get('code') == 200:
+ tree_data = tree_info['data']
+ dept_list = [item for item in dept_list_info['data']['rows'] if item.get('status') == '0']
+ checked_dept = []
+ checked_dept_all = []
+ if role_info.get('dept')[0]:
+ for item in role_info.get('dept'):
+ checked_dept_all.append(str(item.get('dept_id')))
+ has_children = False
+ for other_item in role_info.get('dept'):
+ if other_item['parent_id'] == item['dept_id']:
+ has_children = True
+ break
+ if not has_children:
+ checked_dept.append(str(item.get('dept_id')))
+ half_checked_dept = [x for x in checked_dept_all if x not in checked_dept]
+
+ return dict(
+ dept_div=False,
+ dept_perms_tree=tree_data,
+ dept_perms_expanded_check=True,
+ dept_perms_checkedkeys=checked_dept,
+ dept_perms_halfcheckedkeys=half_checked_dept,
+ role_dept=dept_list,
+ current_role_dept=role_info.get('dept')
+ )
+
+ return dict(
+ dept_div=False,
+ dept_perms_tree=dash.no_update,
+ dept_perms_expanded_check=dash.no_update,
+ dept_perms_checkedkeys=dash.no_update,
+ dept_perms_halfcheckedkeys=dash.no_update,
+ role_dept=dash.no_update,
+ current_role_dept=dash.no_update
+ )
+
+ return dict(
+ dept_div=True,
+ dept_perms_tree=dash.no_update,
+ dept_perms_expanded_check=dash.no_update,
+ dept_perms_checkedkeys=dash.no_update,
+ dept_perms_halfcheckedkeys=dash.no_update,
+ role_dept=dash.no_update,
+ current_role_dept=dash.no_update
+ )
+
+
+@app.callback(
+ output=dict(
+ modal_visible=Output('role-datascope-modal', 'visible', allow_duplicate=True),
+ form_value=Output({'type': 'datascope-form-value', 'index': ALL}, 'value'),
+ api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
+ edit_row_info=Output('role-edit-id-store', 'data', allow_duplicate=True)
+ ),
+ inputs=dict(
+ button_click=Input({'type': 'role-operation-table', 'operation': ALL, 'index': ALL}, 'nClicks')
+ ),
+ prevent_initial_call=True
+)
+def edit_role_datascope_modal(button_click):
+ """
+ 显示角色数据权限弹窗回调
+ """
+ trigger_id = dash.ctx.triggered_id
+ if trigger_id.operation == 'datascope':
+ # 获取所有输出表单项对应value的index
+ form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[1]]
+ role_id = int(trigger_id.index)
+ role_info_res = get_role_detail_api(role_id=role_id)
+ if role_info_res['code'] == 200:
+ role_info = role_info_res['data']
+ return dict(
+ modal_visible=True,
+ form_value=[role_info.get('role').get(k) for k in form_value_list],
+ api_check_token_trigger={'timestamp': time.time()},
+ edit_row_info=role_info
+ )
+
+ return dict(
+ modal_visible=dash.no_update,
+ form_value=[dash.no_update] * len(form_value_list),
+ api_check_token_trigger={'timestamp': time.time()},
+ edit_row_info=None
+ )
+
+ raise PreventUpdate
+
+
+@app.callback(
+ output=dict(
+ modal_visible=Output('role-datascope-modal', 'visible'),
+ operations=Output('role-operations-store', 'data', allow_duplicate=True),
+ api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
+ global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
+ ),
+ inputs=dict(
+ confirm_trigger=Input('role-datascope-modal', 'okCounts')
+ ),
+ state=dict(
+ edit_row_info=State('role-edit-id-store', 'data'),
+ form_value=State({'type': 'datascope-form-value', 'index': ALL}, 'value'),
+ dept_checked_keys=State('role-dept-perms', 'checkedKeys'),
+ dept_half_checked_keys=State('role-dept-perms', 'halfCheckedKeys'),
+ parent_checked=State('role-dept-perms-radio-parent-children', 'checked')
+ ),
+ prevent_initial_call=True
+)
+def role_datascope_confirm(confirm_trigger, edit_row_info, form_value, dept_checked_keys, dept_half_checked_keys, parent_checked):
+ """
+ 角色数据权限弹窗确认回调,实现分配数据权限的操作
+ """
+ if confirm_trigger:
+ # 获取所有输入表单项对应的value
+ form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[1]}
+ dept_half_checked_keys = dept_half_checked_keys if dept_half_checked_keys else []
+ dept_checked_keys = dept_checked_keys if dept_checked_keys else []
+ if parent_checked:
+ dept_perms = dept_half_checked_keys + dept_checked_keys
+ else:
+ dept_perms = dept_checked_keys
+ params_datascope = form_value_state
+ params_datascope['dept_id'] = ','.join(dept_perms) if dept_perms else None
+ params_datascope['role_id'] = edit_row_info.get('role').get('role_id') if edit_row_info else None
+ api_res = role_datascope_api(params_datascope)
+ if api_res.get('code') == 200:
+ return dict(
+ modal_visible=False,
+ operations={'type': 'datascope'},
+ api_check_token_trigger={'timestamp': time.time()},
+ global_message_container=fuc.FefferyFancyMessage('分配成功', type='success')
+ )
+
+ return dict(
+ modal_visible=dash.no_update,
+ operations=dash.no_update,
+ api_check_token_trigger={'timestamp': time.time()},
+ global_message_container=fuc.FefferyFancyMessage('分配失败', type='error')
+ )
+
+ raise PreventUpdate
diff --git a/dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py b/dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py
index de81624..e382657 100644
--- a/dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py
+++ b/dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py
@@ -167,7 +167,7 @@ def get_role_table_data(search_click, refresh_click, pagination, operations, rol
direction='vertical'
),
placement='bottomRight'
- )
+ ) if 'system:role:edit' in button_perms else []
]
)
diff --git a/dash-fastapi-frontend/views/system/role/__init__.py b/dash-fastapi-frontend/views/system/role/__init__.py
index 212fce1..27fba42 100644
--- a/dash-fastapi-frontend/views/system/role/__init__.py
+++ b/dash-fastapi-frontend/views/system/role/__init__.py
@@ -2,7 +2,7 @@ from dash import dcc, html
import feffery_antd_components as fac
import callbacks.system_c.role_c.role_c
-from . import allocate_user
+from . import data_scope, allocate_user
from api.role import get_role_list_api
@@ -109,7 +109,7 @@ def render(button_perms):
direction='vertical'
),
placement='bottomRight'
- )
+ ) if 'system:role:edit' in button_perms else []
]
)
@@ -129,6 +129,9 @@ def render(button_perms):
# 角色管理模块菜单权限存储容器
dcc.Store(id='role-menu-store'),
dcc.Store(id='current-role-menu-store'),
+ # 角色管理模块数据权限存储容器
+ dcc.Store(id='role-dept-store'),
+ dcc.Store(id='current-role-dept-store'),
fac.AntdRow(
[
fac.AntdCol(
@@ -685,6 +688,17 @@ def render(button_perms):
centered=True
),
+ # 数据权限modal
+ fac.AntdModal(
+ data_scope.render(),
+ id='role-datascope-modal',
+ title='数据权限',
+ mask=False,
+ width=600,
+ renderFooter=True,
+ okClickClose=False
+ ),
+
# 分配用户modal
fac.AntdModal(
allocate_user.render(button_perms),
diff --git a/dash-fastapi-frontend/views/system/role/data_scope.py b/dash-fastapi-frontend/views/system/role/data_scope.py
new file mode 100644
index 0000000..7322e79
--- /dev/null
+++ b/dash-fastapi-frontend/views/system/role/data_scope.py
@@ -0,0 +1,175 @@
+from dash import html
+import feffery_antd_components as fac
+
+import callbacks.system_c.role_c.data_scope_c
+
+
+def render():
+ return [
+ fac.AntdForm(
+ [
+ fac.AntdFormItem(
+ fac.AntdInput(
+ id={
+ 'type': 'datascope-form-value',
+ 'index': 'role_name'
+ },
+ placeholder='请输入角色名称',
+ allowClear=True,
+ disabled=True,
+ style={
+ 'width': 350
+ }
+ ),
+ label='角色名称',
+ id={
+ 'type': 'datascope-form-label',
+ 'index': 'role_name'
+ },
+ labelCol={
+ 'span': 6
+ },
+ wrapperCol={
+ 'span': 18
+ }
+ ),
+ fac.AntdFormItem(
+ fac.AntdInput(
+ id={
+ 'type': 'datascope-form-value',
+ 'index': 'role_key'
+ },
+ placeholder='请输入权限字符',
+ allowClear=True,
+ disabled=True,
+ style={
+ 'width': 350
+ }
+ ),
+ label='权限字符',
+ id={
+ 'type': 'datascope-form-label',
+ 'index': 'role_key'
+ },
+ labelCol={
+ 'span': 6
+ },
+ wrapperCol={
+ 'span': 18
+ }
+ ),
+ fac.AntdFormItem(
+ fac.AntdSelect(
+ id={
+ 'type': 'datascope-form-value',
+ 'index': 'data_scope'
+ },
+ options=[
+ {
+ 'label': '全部数据权限',
+ 'value': '1'
+ },
+ {
+ 'label': '自定义数据权限',
+ 'value': '2'
+ },
+ {
+ 'label': '本部门数据权限',
+ 'value': '3'
+ },{
+ 'label': '本部门及以下数据权限',
+ 'value': '4'
+ },
+ {
+ 'label': '仅本人数据权限',
+ 'value': '5'
+ }
+ ],
+ placeholder='请选择权限范围',
+ style={
+ 'width': 350
+ }
+ ),
+ label='权限范围',
+ id={
+ 'type': 'datascope-form-label',
+ 'index': 'data_scope'
+ },
+ labelCol={
+ 'span': 6
+ },
+ wrapperCol={
+ 'span': 18
+ }
+ ),
+ html.Div(
+ fac.AntdFormItem(
+ [
+ fac.AntdRow(
+ [
+ fac.AntdCol(
+ fac.AntdCheckbox(
+ id='role-dept-perms-radio-fold-unfold',
+ label='展开/折叠'
+ ),
+ span=7,
+ ),
+ fac.AntdCol(
+ fac.AntdCheckbox(
+ id='role-dept-perms-radio-all-none',
+ label='全选/全不选'
+ ),
+ span=8,
+ ),
+ fac.AntdCol(
+ fac.AntdCheckbox(
+ id='role-dept-perms-radio-parent-children',
+ label='父子联动',
+ checked=True
+ ),
+ span=6,
+ ),
+ ],
+ style={
+ 'paddingTop': '6px'
+ }
+ ),
+ fac.AntdRow(
+ fac.AntdCol(
+ html.Div(
+ [
+ fac.AntdTree(
+ id='role-dept-perms',
+ treeData=[],
+ multiple=True,
+ checkable=True,
+ showLine=False,
+ selectable=False
+ )
+ ],
+ style={
+ 'border': 'solid 1px rgba(0, 0, 0, 0.2)',
+ 'border-radius': '5px',
+ 'width': 350
+ }
+ )
+ ),
+ style={
+ 'paddingTop': '6px'
+ }
+ ),
+ ],
+ label='数据权限',
+ id='role-dept-perms-form-item',
+ labelCol={
+ 'span': 6
+ },
+ wrapperCol={
+ 'span': 18
+ }
+ ),
+ id='role-dept-perms-div'
+ )
+ ]
+ )
+ ]
--
Gitee