diff --git a/README.zh.md b/README.zh.md index 511caf9..dea0106 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,214 +1,214 @@ -# Django-Vue3-Admin - -[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/liqianglog/django-vue-admin/badge/star.svg?theme=dark)](https://gitee.com/liqianglog/django-vue-admin) - -[预 览](https://demo.dvadmin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin) - - - -💡 **「关于」** - -我们是一群热爱代码的青年,在这个炙热的时代下,我们希望静下心来通过Code带来一点我们的色彩和颜色。 - -因为热爱,所以拥抱未来! - - -## 平台简介 - -💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) 是一套全部开源的快速开发平台,毫无保留给个人免费使用、团体授权使用。 - django-vue3-admin 基于RBAC模型的权限控制的一整套基础开发平台,权限粒度达到列级别,前后端分离,后端采用django + django-rest-framework,前端采用基于 vue3 + CompositionAPI + typescript + vite + element plus - - - - -* 🧑‍🤝‍🧑前端采用 Vue3+TS+pinia+fastcrud(感谢[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)) -* 👭后端采用 Python 语言 Django 框架以及强大的 [Django REST Framework](https://pypi.org/project/djangorestframework)。 -* 👫权限认证使用[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),支持多终端认证系统。 -* 👬支持加载动态权限菜单,多方式轻松权限控制。 -* 👬全新的列权限管控,粒度细化到每一列。 -* 💏特别鸣谢:[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)。 -* 💡特别感谢[jetbrains](https://www.jetbrains.com/) 为本开源项目提供免费的 IntelliJ IDEA 授权。 - -#### 🏭 环境支持 - -| Edge | Firefox | Chrome | Safari | -| --------- | ------------ | ----------- | ----------- | -| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 | - -> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。 - - - -## 在线体验 - -👩‍👧‍👦演示地址:[https://demo.dvadmin.com](https://demo.dvadmin.com) - -- 账号:superadmin - -- 密码:admin123456 - -👩‍👦‍👦文档地址:[coding](https://dvadmin-private.coding.net/share/km/cec69f3d-30fe-47d5-bd97-e9e851f0b776/K-2) - - - -## 交流 - -- 交流社区:[戳我](https://bbs.django-vue-admin.com)👩‍👦‍👦 - -- 插件市场:[戳我](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 - -- django-vue-admin交流01群(已满):812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi) -- django-vue-admin交流02群(已满):687252418 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=4jJN4IjWGfxJ8YJXbb_gTsuWjR34WLdc&jump_from=webapi) -- django-vue-admin交流03群:442108213 [点击链接加入群聊](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=wsm5oSz3K8dElBYUDtLTcQSEPhINFkl8&authKey=M6sbER0z59ZakgBr5erFeZyFZU15CI52bErNZa%2FxSvvGIuVAbY0N5866v89hm%2FK4&noverify=0&group_code=442108213) - -- 二维码 - - - -## 源码地址 - -gitee地址(主推):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - -github地址:[https://github.com/huge-dream/django-vue3-admin](https://github.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - - -## 内置功能 - -1. 👨‍⚕️菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。 -2. 🧑‍⚕️部门管理:配置系统组织机构(公司、部门、角色)。 -3. 👩‍⚕️角色管理:角色菜单权限分配、数据权限分配、设置角色按部门进行数据范围权限划分。 -4. 🧑‍🎓按钮权限控制:授权角色的按钮权限和接口权限,可做到每一个接口都能授权数据范围。 -5. 🧑‍🎓字段列权限控制:授权页面的字段显示权限,具体到某一列的显示权限。 -7. 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。 -8. 👬接口白名单:配置不需要进行权限校验的接口。 -9. 🧑‍🔧字典管理:对系统中经常使用的一些较为固定的数据进行维护。 -10. 🧑‍🔧地区管理:对省市县区域进行管理。 -11. 📁附件管理:对平台上所有文件、图片等进行统一管理。 -12. 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 -13. 🔌[插件市场 ](https://bbs.django-vue-admin.com/plugMarket.html):基于Django-Vue-Admin框架开发的应用和插件。 - -## 插件市场 🔌 -更新中... - -## 仓库分支说明 💈 -主分支:master(稳定版本) -开发分支:develop - - -## 准备工作 -~~~ -Python >= 3.11.0 (最低3.9+版本) -nodejs >= 16.0 -Mysql >= 8.0 (可选,默认数据库sqlite3,支持5.7+,推荐8.0版本) -Redis (可选,最新版) -~~~ - -## 前端♝ - -```bash -# 克隆项目 -git clone https://gitee.com/huge-dream/django-vue3-admin.git - -# 进入项目目录 -cd web - -# 安装依赖 -npm install yarn -yarn install --registry=https://registry.npm.taobao.org - -# 启动服务 -yarn build -# 浏览器访问 http://localhost:8080 -# .env.development 文件中可配置启动端口等参数 -# 构建生产环境 -# yarn run build -``` - - - -## 后端💈 - -~~~bash -1. 进入项目目录 cd backend -2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py -3. 在 env.py 中配置数据库信息 - mysql数据库版本建议:8.0 - mysql数据库字符集:utf8mb4 -4. 安装依赖环境 - pip3 install -r requirements.txt -5. 执行迁移命令: - python3 manage.py makemigrations - python3 manage.py migrate -6. 初始化数据 - python3 manage.py init -7. 初始化省市县数据: - python3 manage.py init_area -8. 启动项目 - python3 manage.py runserver 0.0.0.0:8000 -或使用 uvicorn : - uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 -~~~ -## 开发建议 -前后端backend与web各自单独一个窗口打开进行开发 - -### 访问项目 - -- 访问地址:[http://localhost:8080](http://localhost:8080) (默认为此地址,如有修改请按照配置文件) -- 账号:`superadmin` 密码:`admin123456` - - - - - -### docker-compose 运行 - -~~~shell -# 先安装docker-compose (自行百度安装),执行此命令等待安装,如有使用celery插件请打开docker-compose.yml中celery 部分注释 -docker-compose up -d -# 初始化后端数据(第一次执行即可) -docker exec -ti dvadmin3-django bash -python manage.py makemigrations -python manage.py migrate -python manage.py init_area -python manage.py init -exit - -前端地址:http://127.0.0.1:8080 -后端地址:http://127.0.0.1:8080/api -# 在服务器上请把127.0.0.1 换成自己公网ip -账号:superadmin 密码:admin123456 - -# docker-compose 停止 -docker-compose down -# docker-compose 重启 -docker-compose restart -# docker-compose 启动时重新进行 build -docker-compose up -d --build -~~~ - - - -## 演示图✅ - -![image-01](https://foruda.gitee.com/images/1701348994587355489/1bc749e7_5074988.png) - -![image-02](https://foruda.gitee.com/images/1701349037811908960/80d361db_5074988.png) - -![image-03](https://foruda.gitee.com/images/1701349224478845203/954f0a7b_5074988.png) - -![image-04](https://foruda.gitee.com/images/1701349248928658877/64926724_5074988.png) - -![image-05](https://foruda.gitee.com/images/1701349259068943299/1306ba40_5074988.png) - -![image-06](https://foruda.gitee.com/images/1701349294894429495/e3b3a8cf_5074988.png) - -![image-07](https://foruda.gitee.com/images/1701350432536247561/3b26685e_5074988.png) - -![image-08](https://foruda.gitee.com/images/1701350455264771992/b364c57f_5074988.png) - -![image-09](https://foruda.gitee.com/images/1701350479266000753/e4e4f7c5_5074988.png) - -![image-10](https://foruda.gitee.com/images/1701350501421625746/f8dd215e_5074988.png) - - - +# Django-Vue3-Admin + +[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/liqianglog/django-vue-admin/badge/star.svg?theme=dark)](https://gitee.com/liqianglog/django-vue-admin) + +[预 览](https://demo.dvadmin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin) + + + +💡 **「关于」** + +我们是一群热爱代码的青年,在这个炙热的时代下,我们希望静下心来通过Code带来一点我们的色彩和颜色。 + +因为热爱,所以拥抱未来! + + +## 平台简介 + +💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) 是一套全部开源的快速开发平台,毫无保留给个人免费使用、团体授权使用。 + django-vue3-admin 基于RBAC模型的权限控制的一整套基础开发平台,权限粒度达到列级别,前后端分离,后端采用django + django-rest-framework,前端采用基于 vue3 + CompositionAPI + typescript + vite + element plus + + + + +* 🧑‍🤝‍🧑前端采用 Vue3+TS+pinia+fastcrud(感谢[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)) +* 👭后端采用 Python 语言 Django 框架以及强大的 [Django REST Framework](https://pypi.org/project/djangorestframework)。 +* 👫权限认证使用[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),支持多终端认证系统。 +* 👬支持加载动态权限菜单,多方式轻松权限控制。 +* 👬全新的列权限管控,粒度细化到每一列。 +* 💏特别鸣谢:[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)。 +* 💡特别感谢[jetbrains](https://www.jetbrains.com/) 为本开源项目提供免费的 IntelliJ IDEA 授权。 + +#### 🏭 环境支持 + +| Edge | Firefox | Chrome | Safari | +| --------- | ------------ | ----------- | ----------- | +| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 | + +> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。 + + + +## 在线体验 + +👩‍👧‍👦演示地址:[https://demo.dvadmin.com](https://demo.dvadmin.com) + +- 账号:superadmin + +- 密码:admin123456 + +👩‍👦‍👦文档地址:[DVAdmin官网](https://www.django-vue-admin.com) + + + +## 交流 + +- 交流社区:[戳我](https://bbs.django-vue-admin.com)👩‍👦‍👦 + +- 插件市场:[戳我](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 + +- django-vue-admin交流01群(已满):812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi) +- django-vue-admin交流02群(已满):687252418 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=4jJN4IjWGfxJ8YJXbb_gTsuWjR34WLdc&jump_from=webapi) +- django-vue-admin交流03群:442108213 [点击链接加入群聊](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=wsm5oSz3K8dElBYUDtLTcQSEPhINFkl8&authKey=M6sbER0z59ZakgBr5erFeZyFZU15CI52bErNZa%2FxSvvGIuVAbY0N5866v89hm%2FK4&noverify=0&group_code=442108213) + +- 二维码 + + + +## 源码地址 + +gitee地址(主推):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 + +github地址:[https://github.com/huge-dream/django-vue3-admin](https://github.com/huge-dream/django-vue3-admin)👩‍👦‍👦 + + +## 内置功能 + +1. 👨‍⚕️菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。 +2. 🧑‍⚕️部门管理:配置系统组织机构(公司、部门、角色)。 +3. 👩‍⚕️角色管理:角色菜单权限分配、数据权限分配、设置角色按部门进行数据范围权限划分。 +4. 🧑‍🎓按钮权限控制:授权角色的按钮权限和接口权限,可做到每一个接口都能授权数据范围。 +5. 🧑‍🎓字段列权限控制:授权页面的字段显示权限,具体到某一列的显示权限。 +7. 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。 +8. 👬接口白名单:配置不需要进行权限校验的接口。 +9. 🧑‍🔧字典管理:对系统中经常使用的一些较为固定的数据进行维护。 +10. 🧑‍🔧地区管理:对省市县区域进行管理。 +11. 📁附件管理:对平台上所有文件、图片等进行统一管理。 +12. 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 +13. 🔌[插件市场 ](https://bbs.django-vue-admin.com/plugMarket.html):基于Django-Vue-Admin框架开发的应用和插件。 + +## 插件市场 🔌 +更新中... + +## 仓库分支说明 💈 +主分支:master(稳定版本) +开发分支:develop + + +## 准备工作 +~~~ +Python >= 3.11.0 (最低3.9+版本) +nodejs >= 16.0 +Mysql >= 8.0 (可选,默认数据库sqlite3,支持5.7+,推荐8.0版本) +Redis (可选,最新版) +~~~ + +## 前端♝ + +```bash +# 克隆项目 +git clone https://gitee.com/huge-dream/django-vue3-admin.git + +# 进入项目目录 +cd web + +# 安装依赖 +npm install yarn +yarn install --registry=https://registry.npm.taobao.org + +# 启动服务 +yarn build +# 浏览器访问 http://localhost:8080 +# .env.development 文件中可配置启动端口等参数 +# 构建生产环境 +# yarn run build +``` + + + +## 后端💈 + +~~~bash +1. 进入项目目录 cd backend +2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py +3. 在 env.py 中配置数据库信息 + mysql数据库版本建议:8.0 + mysql数据库字符集:utf8mb4 +4. 安装依赖环境 + pip3 install -r requirements.txt +5. 执行迁移命令: + python3 manage.py makemigrations + python3 manage.py migrate +6. 初始化数据 + python3 manage.py init +7. 初始化省市县数据: + python3 manage.py init_area +8. 启动项目 + python3 manage.py runserver 0.0.0.0:8000 +或使用 uvicorn : + uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 +~~~ +## 开发建议 +前后端backend与web各自单独一个窗口打开进行开发 + +### 访问项目 + +- 访问地址:[http://localhost:8080](http://localhost:8080) (默认为此地址,如有修改请按照配置文件) +- 账号:`superadmin` 密码:`admin123456` + + + + + +### docker-compose 运行 + +~~~shell +# 先安装docker-compose (自行百度安装),执行此命令等待安装,如有使用celery插件请打开docker-compose.yml中celery 部分注释 +docker-compose up -d +# 初始化后端数据(第一次执行即可) +docker exec -ti dvadmin3-django bash +python manage.py makemigrations +python manage.py migrate +python manage.py init_area +python manage.py init +exit + +前端地址:http://127.0.0.1:8080 +后端地址:http://127.0.0.1:8080/api +# 在服务器上请把127.0.0.1 换成自己公网ip +账号:superadmin 密码:admin123456 + +# docker-compose 停止 +docker-compose down +# docker-compose 重启 +docker-compose restart +# docker-compose 启动时重新进行 build +docker-compose up -d --build +~~~ + + + +## 演示图✅ + +![image-01](https://foruda.gitee.com/images/1701348994587355489/1bc749e7_5074988.png) + +![image-02](https://foruda.gitee.com/images/1701349037811908960/80d361db_5074988.png) + +![image-03](https://foruda.gitee.com/images/1701349224478845203/954f0a7b_5074988.png) + +![image-04](https://foruda.gitee.com/images/1701349248928658877/64926724_5074988.png) + +![image-05](https://foruda.gitee.com/images/1701349259068943299/1306ba40_5074988.png) + +![image-06](https://foruda.gitee.com/images/1701349294894429495/e3b3a8cf_5074988.png) + +![image-07](https://foruda.gitee.com/images/1701350432536247561/3b26685e_5074988.png) + +![image-08](https://foruda.gitee.com/images/1701350455264771992/b364c57f_5074988.png) + +![image-09](https://foruda.gitee.com/images/1701350479266000753/e4e4f7c5_5074988.png) + +![image-10](https://foruda.gitee.com/images/1701350501421625746/f8dd215e_5074988.png) + + + diff --git a/backend/dvadmin/system/views/menu_button.py b/backend/dvadmin/system/views/menu_button.py index 2a6c742..f91839a 100644 --- a/backend/dvadmin/system/views/menu_button.py +++ b/backend/dvadmin/system/views/menu_button.py @@ -80,4 +80,29 @@ class MenuButtonViewSet(CustomModelViewSet): else: role_id = request.user.role.values_list('id', flat=True) queryset = RoleMenuButtonPermission.objects.filter(role__in=role_id).values_list('menu_button__value',flat=True).distinct() - return DetailResponse(data=queryset) \ No newline at end of file + return DetailResponse(data=queryset) + + @action(methods=['post'], detail=False, permission_classes=[IsAuthenticated]) + def batch_create(self, request, *args, **kwargs): + """ + 批量创建菜单“增删改查查”权限 + 创建的数据来源于菜单,需要规范创建菜单参数 + value:菜单的component_name:method + api:菜单的web_path增加'/api'前缀,并根据method增加{id} + """ + menu_obj = Menu.objects.filter(id=request.data['menu']).first() + result_list = [ + {'menu': menu_obj.id, 'name': '新增', 'value': f'{menu_obj.component_name}:Create', 'api': f'/api{menu_obj.web_path}/', + 'method': 1}, + {'menu': menu_obj.id, 'name': '删除', 'value': f'{menu_obj.component_name}:Delete', 'api': f'/api{menu_obj.web_path}/{{id}}/', + 'method': 3}, + {'menu': menu_obj.id, 'name': '修改', 'value': f'{menu_obj.component_name}:Update', 'api': f'/api{menu_obj.web_path}/{{id}}/', + 'method': 2}, + {'menu': menu_obj.id, 'name': '查询', 'value': f'{menu_obj.component_name}:Search', 'api': f'/api{menu_obj.web_path}/', + 'method': 0}, + {'menu': menu_obj.id, 'name': '详情', 'value': f'{menu_obj.component_name}:Retrieve', 'api': f'/api{menu_obj.web_path}/{{id}}/', + 'method': 0}] + serializer = self.get_serializer(data=result_list, many=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return SuccessResponse(serializer.data, msg="批量创建成功") diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index b86952c..604ebe2 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -23,13 +23,13 @@ class RoleMenuButtonPermissionSerializer(CustomModelSerializer): """ 菜单按钮-序列化器 """ + class Meta: model = RoleMenuButtonPermission fields = "__all__" read_only_fields = ["id"] - class RoleMenuButtonPermissionCreateUpdateSerializer(CustomModelSerializer): """ 初始化菜单按钮-序列化器 @@ -67,17 +67,17 @@ class RoleButtonPermissionSerializer(CustomModelSerializer): return None return obj.data_range - class Meta: model = MenuButton - fields = ['id','name','value','isCheck','data_range'] + fields = ['id', 'name', 'value', 'isCheck', 'data_range'] + class RoleFieldPermissionSerializer(CustomModelSerializer): - class Meta: model = FieldPermission fields = "__all__" + class RoleMenuFieldSerializer(CustomModelSerializer): is_query = serializers.SerializerMethodField() is_create = serializers.SerializerMethodField() @@ -103,24 +103,38 @@ class RoleMenuFieldSerializer(CustomModelSerializer): if queryset: return queryset.is_update return False + class Meta: model = MenuField - fields = ['id','field_name','title','is_query','is_create','is_update'] + fields = ['id', 'field_name', 'title', 'is_query', 'is_create', 'is_update'] + + +class RoleMenuSerializer(CustomModelSerializer): + menus = serializers.SerializerMethodField() + + def get_menus(self, instance): + menu_list = Menu.objects.filter(parent=instance['id']).values('id', 'name') + serializer = RoleMenuPermissionSerializer(menu_list, many=True, request=self.request) + return serializer.data + + class Meta: + model = Menu + fields = ['id', 'name', 'menus'] class RoleMenuPermissionSerializer(CustomModelSerializer): """ 菜单和按钮权限 """ - name = serializers.SerializerMethodField() + # name = serializers.SerializerMethodField() isCheck = serializers.SerializerMethodField() btns = serializers.SerializerMethodField() columns = serializers.SerializerMethodField() - def get_name(self, instance): - parent_list = Menu.get_all_parent(instance['id']) - names = [d["name"] for d in parent_list] - return "/".join(names) + # def get_name(self, instance): + # parent_list = Menu.get_all_parent(instance['id']) + # names = [d["name"] for d in parent_list] + # return "/".join(names) def get_isCheck(self, instance): params = self.request.query_params return RoleMenuPermission.objects.filter( @@ -130,19 +144,18 @@ class RoleMenuPermissionSerializer(CustomModelSerializer): def get_btns(self, instance): btn_list = MenuButton.objects.filter(menu__id=instance['id']).values('id', 'name', 'value') - serializer = RoleButtonPermissionSerializer(btn_list,many=True,request=self.request) - return serializer.data + serializer = RoleButtonPermissionSerializer(btn_list, many=True, request=self.request) + return serializer.data def get_columns(self, instance): col_list = MenuField.objects.filter(menu=instance['id']) - serializer = RoleMenuFieldSerializer(col_list,many=True,request=self.request) + serializer = RoleMenuFieldSerializer(col_list, many=True, request=self.request) return serializer.data - - class Meta: model = Menu - fields = ['id','name','isCheck','btns','columns'] + fields = ['id', 'name', 'isCheck', 'btns', 'columns'] + class RoleMenuButtonPermissionViewSet(CustomModelViewSet): """ @@ -167,54 +180,54 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): :return: menu,btns,columns """ params = request.query_params - role = params.get('role',None) - if role is None: + role = params.get('role', None) + if role is None: return ErrorResponse(msg="未获取到角色信息") is_superuser = request.user.is_superuser - # if is_superuser: - # queryset = Menu.objects.filter(status=1,is_catalog=False).values('name', 'id').all() - # else: - # role_id = request.user.role.values_list('id', flat=True) - # menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id',flat=True) - # queryset = Menu.objects.filter(status=1, is_catalog=False,id__in=menu_list).values('name', 'id').all() - # serializer = RoleMenuPermissionSerializer(queryset,many=True,request=request) - # data = serializer.data - # return DetailResponse(data=data) - data = [] if is_superuser: - queryset = Menu.objects.filter(status=1,is_catalog=False).values('name', 'id').all() + queryset = Menu.objects.filter(status=1, is_catalog=True).values('name', 'id').all() else: role_id = request.user.role.values_list('id', flat=True) - menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id',flat=True) - queryset = Menu.objects.filter(status=1, is_catalog=False,id__in=menu_list).values('name', 'id') - for item in queryset: - parent_list = Menu.get_all_parent(item['id']) - names = [d["name"] for d in parent_list] - completeName = "/".join(names) - isCheck = RoleMenuPermission.objects.filter( - menu__id=item['id'], - role__id=role, - ).exists() - mbCheck = RoleMenuButtonPermission.objects.filter( - menu_button=OuterRef("pk"), - role__id=role, - ) - btns = MenuButton.objects.filter( - menu__id=item['id'], - ).annotate(isCheck=Exists(mbCheck)).values('id', 'name', 'value', 'isCheck', - data_range=F('menu_button_permission__data_range')) - dicts = { - 'name': completeName, - 'id': item['id'], - 'isCheck': isCheck, - 'btns': btns, - - } - data.append(dicts) + menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id', flat=True) + queryset = Menu.objects.filter(status=1, is_catalog=True, id__in=menu_list).values('name', 'id').all() + serializer = RoleMenuSerializer(queryset, many=True, request=request) + data = serializer.data return DetailResponse(data=data) + # data = [] + # if is_superuser: + # queryset = Menu.objects.filter(status=1, is_catalog=False).values('name', 'id').all() + # else: + # role_id = request.user.role.values_list('id', flat=True) + # menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id', flat=True) + # queryset = Menu.objects.filter(status=1, is_catalog=False, id__in=menu_list).values('name', 'id') + # for item in queryset: + # parent_list = Menu.get_all_parent(item['id']) + # names = [d["name"] for d in parent_list] + # completeName = "/".join(names) + # isCheck = RoleMenuPermission.objects.filter( + # menu__id=item['id'], + # role__id=role, + # ).exists() + # mbCheck = RoleMenuButtonPermission.objects.filter( + # menu_button=OuterRef("pk"), + # role__id=role, + # ) + # btns = MenuButton.objects.filter( + # menu__id=item['id'], + # ).annotate(isCheck=Exists(mbCheck)).values('id', 'name', 'value', 'isCheck', + # data_range=F('menu_button_permission__data_range')) + # dicts = { + # 'name': completeName, + # 'id': item['id'], + # 'isCheck': isCheck, + # 'btns': btns, + # + # } + # data.append(dicts) + # return DetailResponse(data=data) @action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated]) - def set_role_premission(self,request,pk): + def set_role_premission(self, request, pk): """ 对角色的菜单和按钮及按钮范围授权: :param request: @@ -224,29 +237,30 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): body = request.data RoleMenuPermission.objects.filter(role=pk).delete() RoleMenuButtonPermission.objects.filter(role=pk).delete() - for menu in body: - if menu.get('isCheck'): - menu_parent = Menu.get_all_parent(menu.get('id')) - role_menu_permission_list = [] - for d in menu_parent: - role_menu_permission_list.append(RoleMenuPermission(role_id=pk, menu_id=d["id"])) - RoleMenuPermission.objects.bulk_create(role_menu_permission_list) - # RoleMenuPermission.objects.create(role_id=pk, menu_id=menu.get('id')) - for btn in menu.get('btns', []): - if btn.get('isCheck'): - data_range = btn.get('data_range',0) or 0 - instance = RoleMenuButtonPermission.objects.create(role_id=pk, menu_button_id=btn.get('id'),data_range=data_range) - instance.dept.set(btn.get('dept',[])) - for col in menu.get('columns', []): - FieldPermission.objects.update_or_create(role_id=pk,field_id=col.get('id'),defaults={ - "is_query":col.get('is_query'), - "is_create":col.get('is_create'), - "is_update":col.get('is_update') - }) + for item in body: + for menu in item["menus"]: + if menu.get('isCheck'): + menu_parent = Menu.get_all_parent(menu.get('id')) + role_menu_permission_list = [] + for d in menu_parent: + role_menu_permission_list.append(RoleMenuPermission(role_id=pk, menu_id=d["id"])) + RoleMenuPermission.objects.bulk_create(role_menu_permission_list) + # RoleMenuPermission.objects.create(role_id=pk, menu_id=menu.get('id')) + for btn in menu.get('btns'): + if btn.get('isCheck'): + data_range = btn.get('data_range', 0) or 0 + instance = RoleMenuButtonPermission.objects.create(role_id=pk, menu_button_id=btn.get('id'), + data_range=data_range) + instance.dept.set(btn.get('dept', [])) + for col in menu.get('columns'): + FieldPermission.objects.update_or_create(role_id=pk, field_id=col.get('id'), + defaults={ + 'is_query': col.get('is_query'), + 'is_create': col.get('is_create'), + 'is_update': col.get('is_update') + }) return DetailResponse(msg="授权成功") - - @action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated]) def role_menu_get_button(self, request): """ diff --git a/backend/extra-hooks/hooks-uvicorn.py b/backend/extra-hooks/hooks-uvicorn.py deleted file mode 100644 index 51c77d7..0000000 --- a/backend/extra-hooks/hooks-uvicorn.py +++ /dev/null @@ -1,6 +0,0 @@ -# extra-hooks/hooks-uvicorn.py -from PyInstaller.utils.hooks import get_package_paths, collect_submodules - -datas = [(get_package_paths('uvicorn')[1], 'uvicorn'), (get_package_paths('whitenoise')[1], 'whitenoise')] - -hiddenimports = collect_submodules('whitenoise') diff --git a/docker_env/web/DockerfileBuild b/docker_env/web/DockerfileBuild index 284221c..1f725ca 100644 --- a/docker_env/web/DockerfileBuild +++ b/docker_env/web/DockerfileBuild @@ -1,4 +1,4 @@ FROM node:16.19-alpine WORKDIR / COPY ./web/package.json . -RUN yarn install --registry=https://registry.npm.taobao.org +RUN yarn install --registry=https://registry.npmmirror.com diff --git a/web/src/utils/columnPermission.ts b/web/src/utils/columnPermission.ts index e35030d..4a9d9f1 100644 --- a/web/src/utils/columnPermission.ts +++ b/web/src/utils/columnPermission.ts @@ -47,6 +47,8 @@ export const handleColumnPermission = async (func: Function, crudOptions: any,ex continue } else if(item.field_name === col) { columns[col].column.show = item['is_query'] + // 如果列表不可见,则禁止在列设置中选择 + if(!item['is_query'])columns[col].column.columnSetDisabled = true columns[col].addForm = { show: item['is_create'] } diff --git a/web/src/views/system/menu/components/MenuButtonCom/api.ts b/web/src/views/system/menu/components/MenuButtonCom/api.ts index bc473fc..d91f920 100644 --- a/web/src/views/system/menu/components/MenuButtonCom/api.ts +++ b/web/src/views/system/menu/components/MenuButtonCom/api.ts @@ -39,3 +39,12 @@ export function DelObj(id: DelReq) { data: { id }, }); } + +export function BatchAdd(obj: AddReq) { + return request({ + url: apiPrefix + 'batch_create/', + method: 'post', + data: obj, + }); +} + diff --git a/web/src/views/system/menu/components/MenuButtonCom/crud.tsx b/web/src/views/system/menu/components/MenuButtonCom/crud.tsx index c1a7a1f..908cf2a 100644 --- a/web/src/views/system/menu/components/MenuButtonCom/crud.tsx +++ b/web/src/views/system/menu/components/MenuButtonCom/crud.tsx @@ -2,6 +2,8 @@ import {AddReq, DelReq, EditReq, dict, CreateCrudOptionsRet, CreateCrudOptionsPr import * as api from './api'; import {auth} from '/@/utils/authFunction' import {request} from '/@/utils/service'; +import { successNotification } from '/@/utils/message'; +import { ElMessage } from 'element-plus'; //此处为crudOptions配置 export const createCrudOptions = function ({crudExpose, context}: CreateCrudOptionsProps): CreateCrudOptionsRet { const pageRequest = async () => { @@ -40,6 +42,22 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti add: { show: auth('btn:Create') }, + batchAdd: { + show: true, + type: 'primary', + text: '批量生成', + click: async () => { + if (context!.selectOptions.value.id == undefined) { + ElMessage.error('请选择菜单'); + return; + } + const result = await api.BatchAdd({ menu: context!.selectOptions.value.id }); + if (result.code == 2000) { + successNotification(result.msg); + crudExpose.doRefresh(); + } + }, + }, }, }, rowHandle: { diff --git a/web/src/views/system/menu/components/MenuFieldCom/index.vue b/web/src/views/system/menu/components/MenuFieldCom/index.vue index ec3822b..435ce3d 100644 --- a/web/src/views/system/menu/components/MenuFieldCom/index.vue +++ b/web/src/views/system/menu/components/MenuFieldCom/index.vue @@ -4,8 +4,15 @@
已选择:{{ props.model }}
+ +
-
+ +
{{ item.app + '--' + item.title + '(' + item.key + ')' }} @@ -29,7 +36,7 @@