135 Commits
Author SHA1 Message Date
zhengxinonlyandGitee dc1e9afe0c !39 修复账号多角色时菜单重复,子菜单ID小于父菜单ID时无法显示的问题
Merge pull request !39 from waizking/N/A
2022-06-16 08:44:53 +00:00
就眠儀式andGitee 6220f979bf !42 update Dockerfile.
Merge pull request !42 from kyqv/N/A
2022-06-16 04:09:10 +00:00
kyqvandGitee 6ec05fd3f9 update Dockerfile.
阿里镜像源问题更新 导致docker 无法安装
2022-06-16 03:45:10 +00:00
不胜舟andGitee 8c3e43b85b !41 修复mysql连接中密码包含@特殊字符时的错误
Merge pull request !41 from stevenhu/N/A
2022-06-02 07:11:05 +00:00
stevenhuandGitee 248145b821 修复mysql连接中密码包含@特殊字符时的错误 2022-06-02 05:08:36 +00:00
就眠儀式andGitee 261a983678 update README.md. 2022-05-13 06:52:00 +00:00
sunshine 1a7e457054 解决bug 2022-05-08 21:29:44 +08:00
不胜舟andGitee 7ae59415e8 update applications/view/passport/__init__.py. 2022-04-12 11:16:54 +00:00
sunshine c50d5e7921 重写sqlalchemy的query,增加序列化方法 2022-04-12 16:55:27 +08:00
sunshine af873bf45e 升级依赖 2022-04-12 16:50:48 +08:00
waizkingandGitee 4384a3000c 修复账号多角色时菜单重复,子菜单ID小于父菜单ID时无法显示的问题 2022-03-21 03:15:25 +00:00
不胜舟andGitee 72a951a2b6 !34 修改数据库的字符集
Merge pull request !34 from zebra/N/A
2022-02-08 10:20:02 +00:00
zebraandGitee 38a5136e69 修改数据库的字符集 2022-02-08 10:18:47 +00:00
不胜舟andGitee 96dbeb0cbd !33 【轻量级 PR】:update applications/configs/config.py.
Merge pull request !33 from VincentFu/N/A
2022-01-25 07:14:04 +00:00
VincentFuandGitee 936c37c65c update applications/configs/config.py.
数据库编码设置为utf8mb4
2022-01-25 07:12:58 +00:00
resonate e51c86c83f reset 2022-01-14 12:46:40 +08:00
resonate 71a25f4774 session存入redis 2022-01-14 12:39:42 +08:00
杭州-伍个一andGitee 56563657e8 update applications/common/helper.py.
修正between查询的类型
2021-12-15 03:18:23 +00:00
mkg 6bfb10e4a4 增加自动序列化(根据orm模型)方法,增加软(逻辑)删除 2021-12-07 15:39:56 +08:00
mkg 70cb8eb35b 更新依赖 2021-11-22 14:13:08 +08:00
mkg cdea48c7d4 加入webargs验证扩展,分区用于验证的inschema和用户序列化的outschema 2021-11-17 18:07:36 +08:00
mkg fba22bbcbf 构造多级菜单树(来源mini分支) 2021-11-17 16:02:51 +08:00
不胜舟andGitee 14f96abdb3 !28 回退 'Pull Request !25 : 【轻量级 PR】:update applications/common/admin.py.'
Merge pull request !28 from 不胜舟/revert-merge-25-master
2021-11-17 02:42:15 +00:00
不胜舟andGitee 28ff95bf0d 回退 'Pull Request !25 : 【轻量级 PR】:update applications/common/admin.py.' 2021-11-17 02:41:27 +00:00
mkg 6cee5a6104 合并配置文件,解决logo打印多次问题 2021-11-16 23:18:20 +08:00
不胜舟andGitee 3add95e910 update requirement/requirement-dev.txt. 2021-11-16 09:41:18 +00:00
mkg 1db1064c11 图片上传数据库存储改为相对路径 2021-11-14 22:40:44 +08:00
mkg f75928119e 修改目录 2021-11-14 20:15:26 +08:00
mkg 9051e8138c 修改环境配置文件 2021-11-14 18:36:08 +08:00
mkg 8396f49105 简化requirement 2021-11-10 23:02:28 +08:00
不胜舟andGitee c664c70bbc !26 update applications/view/department/__init__.py.
Merge pull request !26 from sys/N/A
2021-11-09 13:30:33 +00:00
不胜舟andGitee 7a503cd94a !27 update applications/templates/admin/dept/edit.html.
Merge pull request !27 from sys/N/A
2021-11-09 13:30:20 +00:00
sysandGitee 7a9aeec8db update applications/templates/admin/dept/edit.html. 2021-11-09 06:33:20 +00:00
sysandGitee 2c57bc36ea update applications/view/department/__init__.py. 2021-11-09 06:17:05 +00:00
mkg c0fc70425b 删除.env 2021-11-08 21:39:39 +08:00
mkg f95944a781 Merge branch 'master' of https://gitee.com/pear-admin/pear-admin-flask into master 2021-11-08 18:52:46 +08:00
mkg 589e0c8ce7 提交docker-compose.yml 2021-11-08 18:52:36 +08:00
不胜舟andGitee 6e0ff64893 !25 update applications/common/admin.py.
Merge pull request !25 from sys/N/A
2021-11-06 17:26:18 +00:00
sysandGitee 044e432eb7 update applications/common/admin.py. 2021-11-06 17:22:05 +00:00
不胜舟andGitee 5887079ade !24 update applications/view/admin/user.py.
Merge pull request !24 from sys/N/A
2021-11-06 12:34:50 +00:00
sysandGitee 724e0817a5 update applications/view/admin/user.py. 2021-11-06 12:31:52 +00:00
不胜舟andGitee 9caa7a22e5 update applications/configs/config.py. 2021-11-06 09:17:49 +00:00
mkgandGitee c2042d5f80 update start.sh. 2021-10-28 07:56:41 +00:00
mkg 51f17c9539 提交Dockerfile 2021-10-28 13:12:18 +08:00
mkg 27ed99ee27 增加gunicorn启动脚本 2021-10-28 10:00:21 +08:00
mkg 7eb41e9754 修补日志的xss漏洞 2021-10-13 19:38:28 +08:00
mkg c7f266a482 Merge branch 'master' of https://gitee.com/pear-admin/pear-admin-flask into master 2021-10-08 22:58:28 +08:00
mkg f2d444b877 修补iconPicker -> icon 导致的bug 2021-10-08 22:58:14 +08:00
mkgandGitee 9e59035300 !23 update applications/view/admin/dict.py.
Merge pull request !23 from VincentFu/N/A
2021-10-06 08:23:54 +00:00
VincentFuandGitee b0ed2100a2 update applications/view/admin/dict.py.
修复字典类型更新
2021-10-05 05:53:05 +00:00
mkgandGitee 6f0c86fba6 !18 update applications/view/department/__init__.py.
Merge pull request !18 from VincentFu/N/A
2021-10-04 00:42:32 +00:00
VincentFuandGitee c71562a12f update applications/view/department/__init__.py.
这个地方也需要相应的修改,和power.py一样。
2021-10-03 17:31:39 +00:00
mkg a7fa415545 修改权限管理为post 2021-10-02 23:06:20 +08:00
mkg 24d2523385 更新依赖pearadminlayui 2021-10-02 16:11:07 +08:00
Rabbitandmkg d30a3d79de !17 修改角色状态值
* 角色状态值修复
2021-09-23 02:35:20 +00:00
mkgandGitee 860d5778cc !16 简化分页取count
Merge pull request !16 from Rabbit/master
2021-09-23 02:09:34 +00:00
xz 23dbe9ee9c 简化分页取count 2021-09-23 09:57:13 +08:00
就眠儀式andGitee 9114964ef2 update README.md. 2021-09-21 18:36:34 +00:00
mkgandGitee 56615528b9 !15 新增字典数据405
Merge pull request !15 from yanyuan/master
2021-09-09 09:54:12 +00:00
yanyuanandGitee 1b5c54daf9 update applications/view/admin/dict.py.
新增字典数据有误
2021-09-09 09:50:05 +00:00
Rabbitandmkg c0a3f5243b !14 【轻量级 PR】:update applications/view/admin/user.py.
Merge pull request !14 from Rabbit/N/A
2021-09-09 02:29:22 +00:00
Rabbitandmkg d22eb22f74 !13 【轻量级 PR】:update applications/view/admin/role.py.
Merge pull request !13 from Rabbit/N/A
2021-09-09 02:29:04 +00:00
就眠儀式andGitee 7eeb4c2489 !12 update applications/templates/admin/user/main.html.
Merge pull request !12 from Rabbit/N/A
2021-09-08 14:29:40 +00:00
RabbitandGitee d691c444ae update applications/templates/admin/user/main.html.
312行:table.reload('role-table') -> table.reload('user-table')
2021-09-08 14:27:39 +00:00
mkgandGitee 83c23ea276 !10 update applications/view/admin/power.py.
Merge pull request !10 from Rabbit/N/A
2021-09-06 11:23:43 +00:00
mkgandGitee cff67d152f !11 update applications/view/admin/user.py.
Merge pull request !11 from Rabbit/N/A
2021-09-06 11:23:25 +00:00
mkgandGitee ad67c289d3 !9 update applications/view/admin/role.py.
Merge pull request !9 from Rabbit/N/A
2021-09-06 11:23:05 +00:00
RabbitandGitee 4a4d8f763d update applications/view/admin/user.py.
多对多删除简化
2021-09-06 10:24:05 +00:00
RabbitandGitee dfc734c13a update applications/view/admin/power.py.
多对多关系删除简化
2021-09-06 10:21:51 +00:00
RabbitandGitee 654f441d05 update applications/view/admin/role.py. 2021-09-06 10:19:02 +00:00
mkg 48eebc1c95 定时任务(部分) 2021-08-26 15:31:36 +08:00
mkg 3c1764e58d 图形验证码 2021-08-26 12:12:39 +08:00
mkg 233241c962 优化验证码字符 2021-08-26 12:06:27 +08:00
mkg 30f9774ab1 Merge branch 'master' of https://gitee.com/pear-admin/pear-admin-flask into master 2021-08-25 21:17:33 +08:00
mkg a255c746d4 解决验证码可绕过安全问题 2021-08-25 21:17:16 +08:00
mkgandGitee eac2a1c941 update requirement/requirement-dev.txt. 2021-07-19 13:51:58 +00:00
mkg 110a5faaff 回退依赖版本解决不能上传问题 2021-07-19 21:40:03 +08:00
mkg f3cfdcf580 _id和id混乱导致的role编辑bug 2021-07-19 15:55:32 +08:00
mkg 34a1066cd0 user中_id改为id 2021-07-18 20:16:49 +08:00
mkg 67d05c39f3 修复过滤后count数量 2021-07-18 14:45:07 +08:00
mkg fa49130c4f 更新readme 2021-07-16 17:48:08 +08:00
mkg 5b76973d9c 删除因去除curd层复制错误导致的多余代码 2021-07-16 17:45:09 +08:00
mkg 0d57a2a8f2 更新md 2021-07-16 13:10:01 +08:00
mkg 78b7dae880 增加命令行新建空白视图功能 2021-07-16 13:04:49 +08:00
mkg 04a89144ba 扩充过滤器 2021-07-12 14:38:11 +08:00
mkg 3b3eecc1d1 删除curd层 2021-07-12 13:04:16 +08:00
mkg ca92d949dd 增加layui表格分页器layui_paginate() 2021-07-12 12:13:29 +08:00
mkg 663c7925e3 Merge branch 'master' of https://gitee.com/pear-admin/pear-admin-flask into master 2021-07-12 11:28:34 +08:00
mkg 76b2ad6f58 增加多条件查询构造器,删除部分curd层 2021-07-12 11:28:21 +08:00
mkgandGitee fd8dcfee40 update README.md. 2021-07-07 23:55:21 +00:00
mkg 0a60c1960d 更新layui,修复数据库初始化路径问题 2021-07-07 13:54:37 +08:00
mkg 28021df2ab 更新文档 2021-07-04 17:17:14 +08:00
mkg 6d42b6d56c 更新文档 2021-07-04 17:15:51 +08:00
mkgandGitee 3b467f42fe update requirement/requirement-dev.txt. 2021-07-04 09:01:37 +00:00
mkg e37749c669 Merge branch 'v2' of https://gitee.com/pear-admin/pear-admin-flask into v2 2021-06-24 13:17:45 +08:00
mkg 2cbbf25060 增加定时任务前端(部分) 2021-06-24 13:17:34 +08:00
mkgandGitee 986ae4e4bc update requirement/requirement-dev.txt. 2021-06-17 22:00:40 +08:00
mkg 103aaf4911 增加定时任务 2021-06-17 21:59:28 +08:00
mkgandGitee 7038ce64a6 update requirement/requirement-dev.txt. 2021-06-17 16:54:53 +08:00
mkgandGitee 9b8525ca1e update requirement/requirement-dev.txt. 2021-06-17 13:06:55 +08:00
mkgandGitee 254ce53387 update requirement/requirement-dev.txt. 2021-06-16 15:53:10 +08:00
mkgandGitee 0ff0c456b0 update .env. 2021-06-16 15:44:50 +08:00
mkg 57a54090bb Merge branch 'v2' of https://gitee.com/pear-admin/pear-admin-flask into v2 2021-06-16 15:40:48 +08:00
mkg e3d3424ee1 增加邮箱功能 2021-06-16 15:34:39 +08:00
mkgandGitee 99b2f611ec add .env. 2021-06-13 16:39:01 +08:00
mkgandGitee a675d78b82 !6 解决冲突
Merge pull request !6 from mkg/pr_5
2021-06-13 16:16:43 +08:00
mkg 62bd10cc60 Merge branch 'v2' into pr_5
# Conflicts:
#	applications/view/__init__.py
2021-06-13 16:14:23 +08:00
zhengxinonly 9e8a5357be 将权限与部门的逻辑分包 2021-06-13 15:07:40 +08:00
zhengxinonly 5faf88283f pycharm下格式化模板文件,统一风格 2021-06-13 12:45:11 +08:00
mkg 14a32e6d3c 增加数据库迁移命令和数据库初始化命令,分离schemas 2021-06-12 23:05:30 +08:00
mkgandGitee 5e08f3e49b !4 调整目录结构
Merge pull request !4 from zhengxinonly/v2
2021-06-12 21:26:24 +08:00
zhengxinonly d9a5bcb0de 调整目录结构 2021-06-12 21:20:13 +08:00
mkg 48633729fb 更新依赖,使用flask新路由定义方法 2021-06-12 12:59:56 +08:00
mkgandGitee f274e060f4 !3 删除弃用代码,解决user.css丢失
Merge pull request !3 from mkg/master
2021-06-12 12:25:26 +08:00
mkg 9784f7816a 删除弃用代码,解决user.css丢失 2021-06-12 12:22:31 +08:00
mkg f93a36c094 加入marshmallow数据验证 2021-06-07 22:57:21 +08:00
mkg 375d3a10b2 更新依赖pear-admin-layui到3.8.3,增加前端渲染配置接口 2021-06-05 13:10:29 +08:00
mkg a1fbf5a0ae 增加xss过滤 2021-06-03 14:38:48 +08:00
mkg 10035cc158 修改部分命名 2021-06-03 11:33:40 +08:00
mkg 3b1150ad1f 优化curd函数在view的引入方式,优化权限与日志的装饰器 2021-06-03 11:26:24 +08:00
mkg 73222e854c 增加日志页面的权限 2021-06-03 11:24:40 +08:00
mkg 3db99422b7 误删除监控页样式 2021-06-03 10:40:26 +08:00
mkg d876c8309f 模板引入优化 2021-06-03 10:37:12 +08:00
mkg e0738aa5b2 完成部门管理,增加.flaskenv配置,抽基本js和css作为引入模板 2021-06-01 17:54:13 +08:00
mkg f051693690 完成主机监控动态图表 2021-05-29 13:00:55 +08:00
mkg c6ffbbc062 删除无用代码 2021-05-13 12:40:37 +08:00
mkg 2836fc1139 修补数据库安装脚本未指定端口问题 2021-05-13 11:35:45 +08:00
mkg e111f4a763 统一table,成功,失败返回响应,封装模型序列化函数 2021-04-29 19:01:18 +08:00
mkg 3d5e4b9e9b 序列化类移到models里,简化代码 2021-04-29 13:09:10 +08:00
mkg 8f4054e576 分解models 2021-04-27 11:58:21 +08:00
mkg 3ea4edd117 修改菜单类型(框架和签页) 2021-04-23 15:48:58 +08:00
mkg 2f0df0a700 增加数据字典 2021-04-16 14:08:13 +08:00
就眠仪式andGitee a709095dc2 !1 Flask_Uploads报错替代方案
Merge pull request !1 from bingyun/master
2021-04-02 17:01:54 +08:00
bingyunandGitee 4a8ac0ac83 update requirement.txt.
使用Flask_Reuploaded==0.5.0代替-e git+https://github.com/maxcountryman/flask-uploads.git@f66d7dc93e684fa0a3a4350a38e41ae00483a796#egg=Flask_Uploads
修复Flask_Uploads报错的问题
2021-04-02 14:56:36 +08:00
mkg b9301046da 更新layui,新增个人信息修改 2021-04-01 23:51:41 +08:00
1069 changed files with 203124 additions and 89729 deletions
+28 -2
View File
@@ -1,3 +1,29 @@
FLASK_APP=main.py # flask配置
FLASK_APP=app.py
FLASK_ENV=development FLASK_ENV=development
FLASK_DEBUG=1 FLASK_DEBUG=1
FLASK_RUN_HOST = 127.0.0.1
FLASK_RUN_PORT = 5000
# pear admin flask配置
SYSTEM_NAME = Pear Admin
# MySql配置信息
MYSQL_HOST=127.0.0.1
# MYSQL_HOST=dbserver
MYSQL_PORT=3306
MYSQL_DATABASE=PearAdminFlask
MYSQL_USERNAME=root
MYSQL_PASSWORD=123456
# Redis 配置
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
# 密钥配置(记得改)
SECRET_KEY='pear-admin-flask'
# 邮箱配置
MAIL_SERVER='smtp.qq.com'
MAIL_USERNAME='123@qq.com'
MAIL_PASSWORD='XXXXX' # 生成的授权码
+25
View File
@@ -0,0 +1,25 @@
FROM python:3.7-alpine
RUN apk update \
&& apk --update add --no-cache gcc \
&& apk --update add --no-cache g++ \
&& apk --update add --no-cache tzdata \
&& apk --update add --no-cache libffi-dev \
&& apk --update add --no-cache libxslt-dev \
&& apk --update add --no-cache jpeg-dev
ENV TIME_ZONE Asia/Shanghai
ENV PIPURL "https://pypi.tuna.tsinghua.edu.cn/simple"
RUN echo "${TIME_ZONE}" > /etc/timezone \
&& ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime
COPY . /app
WORKDIR /app
RUN pip --no-cache-dir install -i ${PIPURL} --upgrade pip \
&& pip --no-cache-dir install -i ${PIPURL} -r requirement/requirement-dev.txt \
&& pip --no-cache-dir install -i ${PIPURL} gunicorn \
&& chmod +x start.sh
CMD ./start.sh
+69 -117
View File
@@ -1,7 +1,6 @@
<div align="center"> <div align="center">
<br/> <br/>
<br/> <br/>
<img src="https://gitee.com/pear-admin/Pear-Admin-Layui/raw/master/admin/images/logo.png" width="90px" style="margin-top:30px;"/>
<h1 align="center"> <h1 align="center">
Pear Admin Flask Pear Admin Flask
</h1> </h1>
@@ -9,7 +8,7 @@
开 箱 即 用 的 Flask 快 速 开 发 平 台 开 箱 即 用 的 Flask 快 速 开 发 平 台
</h4> </h4>
[预 览](http://flask.pearadmin.com) | [官 网](http://www.pearadmin.com/) | [群聊](https://jq.qq.com/?_wv=1027&k=5OdSmve) | [社区](http://forum.pearadmin.com/) [预 览](http://flask.pearadmin.com) | [官 网](http://www.pearadmin.com/) | [群聊](https://jq.qq.com/?_wv=1027&k=TkLAKUxC) | [社区](http://forum.pearadmin.com/)
<p align="center"> <p align="center">
@@ -34,13 +33,27 @@
> >
>项目旨在为python开发者提供一个后台管理系统的模板,成为您构建信息管理系统,物联网后台....等等应用时灵活,简单的工具 >项目旨在为python开发者提供一个后台管理系统的模板,成为您构建信息管理系统,物联网后台....等等应用时灵活,简单的工具
> >
>各位Python爱好者多多指教 >众人拾柴火焰高,欢迎pythoner参与项目~
Pear Admin Flask 分为 Common / Simple 两个版本: Pear Admin Flask 有以下几个版本:
[** Common 通用版本 **](https://gitee.com/pear-admin/pear-admin-flask/tree/master/) [master分支版本 ](https://gitee.com/pear-admin/pear-admin-flask/tree/master/)
[** Simple 简洁版本 **](https://gitee.com/pear-admin/pear-admin-flask/tree/simple/) > flask 2.0.1 + flask-sqlalchemy + mysql + 权限验证 + Flask-APScheduler 定时任务 + marshmallow 序列化与数据验证
[Mini 分支版本 ](https://gitee.com/pear-admin/pear-admin-flask/tree/mini/)
>flask 2.0.1 + flask-sqlalchemy + sqllite + 权限验证 + Flask-RESTful 序列化与数据验证
[v1 分支版本(不再更新,仅供参考) ](https://gitee.com/pear-admin/pear-admin-flask/tree/v1/)
>flask 1.12 + flask-sqlalchemy + mysql + 权限验证 + marshmallow 序列化与数据验证
[simple 分支版本(不再更新,仅供参考) ](https://gitee.com/pear-admin/pear-admin-flask/tree/simple/)
>flask 1.12 + flask-sqlalchemy + mysql + 极简权限 + marshmallow 序列化与数据验证
>
>项目最简版本
#### 内置功能 #### 内置功能
@@ -51,100 +64,32 @@ Pear Admin Flask 分为 Common / Simple 两个版本:
- [x] 登录日志:系统登录日志记录查询包含登录异常。 - [x] 登录日志:系统登录日志记录查询包含登录异常。
- [x] 服务监控:监视当前系统CPU、内存、磁盘、python版本,运行时长等相关信息。 - [x] 服务监控:监视当前系统CPU、内存、磁盘、python版本,运行时长等相关信息。
- [x] 文件上传: 图片上传示例 - [x] 文件上传: 图片上传示例
- [x] 定时任务: 简单的定时任务
- [ ] 代码生成: 构想中.... - [ ] 代码生成: 构想中....
#### 项目结构 #### 项目结构
``` ```
Pear Admin Flask Pear Admin Flask
├─applications # 应用
├─applications │ ├─configs # 配置文件
│ │ │ │ ├─ common.py # 普通配置
├─config │ └─ config.py # 配置文件对象
│ │ ├─extensions # 注册插件
│ ├─ common.py #普通配置 │ ├─models # 数据模型
│ │ ├─static # 静态资源文件
│ └─ database.py #数据库配置 ├─templates # 静态模板文件
└─views # 视图部分
├─models ├─admin # 后台管理视图模块
└─index # 前台视图模块
│ │ └─admin.py #基本模型 ├─docs # 文档说明(占坑)
│ │ ├─migrations # 迁移文件记录
│ ├─service ├─requirement # 依赖文件
│ │ │ ├─test # 测试文件夹(占坑)
│ │ ├─admin └─.env # 项目的配置文件
│ │ │ │
│ │ │ ├─ file.py #file视图的数据操作
│ │ │ │
│ │ │ ├─ index.py #index视图的数据操作
│ │ │ │
│ │ │ ├─ power.py #power视图的数据操作
│ │ │ │
│ │ │ ├─ role.py #role视图的数据操作
│ │ │ │
│ │ │ └─ user.py #user视图的数据操作
│ │ │
│ │ ├─ admin_log.py #存储日志
│ │ │
│ │ ├─ deBug.py #deBug工具栏初始化
│ │ │
│ │ ├─ login.py #flask_login初始化
│ │ │
│ │ ├─ CaptchaTool.py #验证码
│ │ │
│ │ ├─ OriginalDb.py #原生sql查询封装
│ │ │
│ │ ├─ route_auth.py #权限
│ │ │
│ │ └─ upload.py #上传
│ │
│ └─views
│ │
│ ├─admin #前台视图
│ │ │
│ │ ├─index.py #主视图
│ │ │
│ │ ├─user.py #用户视图
│ │ │
│ │ ├─role.py #角色视图
│ │ │
│ │ ├─power.py #权限视图
│ │ │
│ │ ├─monitor.py #系统监控
│ │ │
│ │ ├─file.py #文件上传
│ │ │
│ │ ├─admin_log.py #系统日志
│ │ │
│ │ ├─context_processor.py #全局模板函数注册
│ │ │
│ │ ├─error.py #错误处理
│ │ │
│ │ └─init.py #蓝图注册
│ │
│ └─index #前台视图
├─dev #数据库初始化
├─migrations
├─readmes
├─static #静态资源
│ │
│ └─upload #文件上传地址
└─templates
├─admin #前台模板
├─errors #错误模板
└─index #前台模板
``` ```
#### 项目安装 #### 项目安装
```bash ```bash
@@ -152,61 +97,68 @@ Pear Admin Flask
git clone https://gitee.com/pear-admin/pear-admin-flask git clone https://gitee.com/pear-admin/pear-admin-flask
# 安 装 # 安 装
pip install -r requirement.txt pip install -r requirement\requirement-dev.txt
# 配 置 # 配 置
applications\config\database.py .env
``` ```
#### 修改配置 #### 修改配置
```python ```python
# 主 机 .env
HOST = '127.0.0.1' # MySql配置信息
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=PearAdminFlask
MYSQL_USERNAME=root
MYSQL_PASSWORD=root
# 端 口 # Redis 配置
PORT = '3306' REDIS_HOST=127.0.0.1
REDIS_PORT=6379
# 数 据 库 # 密钥配置
DATABASE = 'PearAdminFlask' SECRET_KEY='pear-admin-flask'
# 账 户
USERNAME = 'root'
# 密 码
PASSWORD = 'root'
# 邮箱配置
MAIL_SERVER='smtp.qq.com'
MAIL_USERNAME='123@qq.com'
MAIL_PASSWORD='XXXXX' # 生成的授权码
``` ```
#### Venv 安装 #### Venv 安装
```bash ```bash
python -m venv venv python -m venv venv
``` ```
#### 运行项目 #### 运行项目
```bash ```bash
# 进 入 目 录
cd dev
# 初 始 化 数 据 库 # 初 始 化 数 据 库
python initDb.py
# 如 果 报 模 块 路 径 错 误
python dev/initDb.py
flask init
``` ```
执行 flask run 命令启动项目 执行 flask run 命令启动项目
#### 命令行创建视图
```bash
# 示例
flask new --type view --name test/a
# 自动注册蓝图
# 访问http://127.0.0.1:5000/test/a/
```
#### 预览项目 #### 预览项目
| | | | | |
|---------------------|---------------------| |---------------------|---------------------|
| ![](readmes/1.jpg) | ![](readmes/2.jpg) | | ![](docs/assets/1.jpg) | ![](docs/assets/2.jpg) |
| ![](readmes/3.jpg)| ![](readmes/4.jpg) | | ![](docs/assets/3.jpg)| ![](docs/assets/4.jpg) |
| ![](readmes/5.jpg) | ![](readmes/6.jpg) | | ![](docs/assets/5.jpg) | ![](docs/assets/6.jpg) |
+10
View File
@@ -0,0 +1,10 @@
from applications import create_app
from flask_migrate import Migrate
from applications.extensions import db
app = create_app()
migrate = Migrate(app, db)
if __name__ == '__main__':
app.run()
+44
View File
@@ -0,0 +1,44 @@
import os
from flask import Flask
from applications.common.script import init_script
from applications.extensions import init_plugs
from applications.view import init_view
from applications.configs import config
def create_app(config_name=None):
app = Flask(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
if not config_name:
# 尝试从本地环境中读取
config_name = os.getenv('FLASK_CONFIG', 'development')
# 引入数据库配置
app.config.from_object(config[config_name])
# 注册各种插件
init_plugs(app)
# 注册路由
init_view(app)
# 注册命令
init_script(app)
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
logo()
return app
def logo():
print('''
_____ _ _ ______ _ _
| __ \ /\ | | (_) | ____| | | |
| |__) |__ __ _ _ __ / \ __| |_ __ ___ _ _ __ | |__ | | __ _ ___| | __
| ___/ _ \/ _` | '__| / /\ \ / _` | '_ ` _ \| | '_ \ | __| | |/ _` / __| |/ /
| | | __/ (_| | | / ____ \ (_| | | | | | | | | | | | | | | (_| \__ \ <
|_| \___|\__,_|_| /_/ \_\__,_|_| |_| |_|_|_| |_| |_| |_|\__,_|___/_|\_\\
''')
+145
View File
@@ -0,0 +1,145 @@
import copy
from collections import OrderedDict
from io import BytesIO
from flask import session, make_response, current_app
from flask_login import current_user
from applications.common.utils.gen_captcha import gen_captcha
from applications.schemas import PowerOutSchema
# 授权路由存入session
def add_auth_session():
role = current_user.role
user_power = []
for i in role:
if i.enable == 0:
continue
for p in i.power:
if p.enable == 0:
continue
user_power.append(p.code)
session['permissions'] = user_power
# 生成菜单树
def make_menu_tree():
role = current_user.role
powers = []
for i in role:
# 如果角色没有被启用就直接跳过
if i.enable == 0:
continue
# 变量角色用户的权限
for p in i.power:
# 如果权限关闭了就直接跳过
if p.enable == 0:
continue
# 一二级菜单
if int(p.type) in [0,1] and p not in powers:
powers.append(p)
power_schema = PowerOutSchema(many=True) # 用已继承 ma.ModelSchema 类的自定制类生成序列化类
power_dict = power_schema.dump(powers) # 生成可序列化对象
power_dict.sort(key=lambda x: (x['parent_id'], x['id']), reverse=True)
menu_dict = OrderedDict()
for _dict in power_dict:
if _dict['id'] in menu_dict:
# 当前节点添加子节点
_dict['children'] = copy.deepcopy(menu_dict[_dict['id']])
_dict['children'].sort(key=lambda item: item['sort'])
# 删除子节点
del menu_dict[_dict['id']]
if _dict['parent_id'] not in menu_dict:
menu_dict[_dict['parent_id']] = [_dict]
else:
menu_dict[_dict['parent_id']].append(_dict)
return sorted(menu_dict.get(0), key=lambda item: item['sort'])
# 生成验证码
def get_captcha():
code, image = gen_captcha()
out = BytesIO()
session["code"] = code
image.save(out, 'png')
out.seek(0)
resp = make_response(out.read())
resp.content_type = 'image/png'
return resp, code
def get_render_config():
# 网站配置
config = dict(logo={
# 网站名称
"title": current_app.config.get("SYSTEM_NAME"),
# 网站图标
"image": "/static/admin/admin/images/logo.png"
# 菜单配置
}, menu={
# 菜单数据来源
"data": "/rights/menu",
"collaspe": False,
# 是否同时只打开一个菜单目录
"accordion": True,
"method": "GET",
# 是否开启多系统菜单模式
"control": False,
# 顶部菜单宽度 PX
"controlWidth": 500,
# 默认选中的菜单项
"select": "0",
# 是否开启异步菜单,false 时 data 属性设置为菜单数据,false 时为 json 文件或后端接口
"async": True
}, tab={
# 是否开启多选项卡
"enable": True,
# 切换选项卡时,是否刷新页面状态
"keepState": True,
# 是否开启 Tab 记忆
"session": True,
# 最大可打开的选项卡数量
"max": 30,
"index": {
# 标识 ID , 建议与菜单项中的 ID 一致
"id": "10",
# 页面地址
"href": "/admin/welcome",
# 标题
"title": "首页"
}
}, theme={
# 默认主题色,对应 colors 配置中的 ID 标识
"defaultColor": "2",
# 默认的菜单主题 dark-theme 黑 / light-theme 白
"defaultMenu": "dark-theme",
# 是否允许用户切换主题,false 时关闭自定义主题面板
"allowCustom": True
}, colors=[{
"id": "1",
"color": "#2d8cf0"
},
{
"id": "2",
"color": "#5FB878"
},
{
"id": "3",
"color": "#1E9FFF"
}, {
"id": "4",
"color": "#FFB800"
}, {
"id": "5",
"color": "darkgray"
}
], links=current_app.config.get("SYSTEM_PANEL_LINKS"), other={
# 主页动画时长
"keepLoad": 1200,
# 布局顶部主题
"autoHead": False
}, header=False)
return config
@@ -1,16 +1,17 @@
from flask_login import current_user from flask_login import current_user
from applications.models import db from applications.common.utils.validate import xss_escape
from applications.models.admin import AdminLog from applications.extensions import db
from applications.models import AdminLog
def login_log(request,uid, is_access): def login_log(request, uid, is_access):
info = { info = {
'method': request.method, 'method': request.method,
'url': request.path, 'url': request.path,
'ip': request.remote_addr, 'ip': request.remote_addr,
'user_agent': request.headers.get('User-Agent'), 'user_agent': xss_escape(request.headers.get('User-Agent')),
'desc': request.form.get('username'), 'desc': xss_escape(request.form.get('username')),
'uid': uid, 'uid': uid,
'success': int(is_access) 'success': int(is_access)
@@ -22,7 +23,7 @@ def login_log(request,uid, is_access):
desc=info.get('desc'), desc=info.get('desc'),
uid=info.get('uid'), uid=info.get('uid'),
method=info.get('method'), method=info.get('method'),
success = info.get('success') success=info.get('success')
) )
db.session.add(log) db.session.add(log)
db.session.flush() db.session.flush()
@@ -35,10 +36,10 @@ def admin_log(request, is_access):
'method': request.method, 'method': request.method,
'url': request.path, 'url': request.path,
'ip': request.remote_addr, 'ip': request.remote_addr,
'user_agent': request.headers.get('User-Agent'), 'user_agent': xss_escape(request.headers.get('User-Agent')),
'desc': str(dict(request.values)), 'desc': xss_escape(str(dict(request.values))),
'uid': current_user.id, 'uid': current_user.id,
'success':int(is_access) 'success': int(is_access)
} }
log = AdminLog( log = AdminLog(
+94
View File
@@ -0,0 +1,94 @@
import datetime
from marshmallow import Schema
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
from applications.extensions import db, ma
class LogicalDeleteMixin(object):
"""
class Test(db.Model,LogicalDeleteMixin):
__tablename__ = 'admin_test'
id = db.Column(db.Integer, primary_key=True, comment='角色ID')
Test.query.filter_by(id=1).soft_delete()
Test.query.logic_all()
"""
create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
delete_at = db.Column(db.DateTime, comment='删除时间')
def auto_model_jsonify(data, model: db.Model):
"""
不需要建立schemas,直接使用orm的定义模型进行序列化
基本功能,待完善
示例
power_data = curd.auto_model_jsonify(model=Dept, data=dept)
"""
def get_model():
return model
class AutoSchema(SQLAlchemyAutoSchema):
class Meta(Schema):
model = get_model()
include_fk = True
include_relationships = True
load_instance = True
common_schema = AutoSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
output = common_schema.dump(data)
return output
def model_to_dicts(schema: ma.Schema, data):
"""
:param schema: schema类
:param model: sqlalchemy查询结果
:return: 返回单个查询结果
"""
# 如果是分页器返回,需要传入model.items
common_schema = schema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
output = common_schema.dump(data) # 生成可序列化对象
return output
def get_one_by_id(model: db.Model, id):
"""
:param model: 模型类
:param id: id
:return: 返回单个查询结果
"""
return model.query.filter_by(id=id).first()
def delete_one_by_id(model: db.Model, id):
"""
:param model: 模型类
:param id: id
:return: 返回单个查询结果
"""
r = model.query.filter_by(id=id).delete()
db.session.commit()
return r
# 启动状态
def enable_status(model: db.Model, id):
enable = 1
role = model.query.filter_by(id=id).update({"enable": enable})
if role:
db.session.commit()
return True
return False
# 停用状态
def disable_status(model: db.Model, id):
enable = 0
role = model.query.filter_by(id=id).update({"enable": enable})
if role:
db.session.commit()
return True
return False
+126
View File
@@ -0,0 +1,126 @@
from sqlalchemy import and_
from applications.extensions import db
class ModelFilter:
"""
orm多参数构造器
"""
filter_field = {}
filter_list = []
type_exact = "exact"
type_neq = "neq"
type_greater = "greater"
type_less = "less"
type_vague = "vague"
type_contains = "contains"
type_between = "between"
def __init__(self):
self.filter_field = {}
self.filter_list = []
def exact(self, field_name, value):
"""
准确查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": value, "type": self.type_exact}
def neq(self, field_name, value):
"""
不等于查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": value, "type": self.type_neq}
def greater(self, field_name, value):
"""
大于查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": value, "type": self.type_greater}
def less(self, field_name, value):
"""
大于查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": value, "type": self.type_less}
def vague(self, field_name, value: str):
"""
模糊查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": ('%' + value + '%'), "type": self.type_vague}
def left_vague(self, field_name, value: str):
"""
左模糊查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": ('%' + value), "type": self.type_vague}
def right_vague(self, field_name, value: str):
"""
左模糊查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": (value + '%'), "type": self.type_vague}
def contains(self, field_name, value: str):
"""
包含查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value and value != '':
self.filter_field[field_name] = {"data": value, "type": self.type_contains}
def between(self, field_name, value1, value2):
"""
范围查询字段
:param field_name: 模型字段名称
:param value: 值
"""
if value1 and value2 and value1 != '' and value2 != '':
self.filter_field[field_name] = {"data": [value1, value2], "type": self.type_between}
def get_filter(self, model: db.Model):
"""
获取过滤条件
:param model: 模型字段名称
"""
for k, v in self.filter_field.items():
if v.get("type") == self.type_vague:
self.filter_list.append(getattr(model, k).like(v.get("data")))
if v.get("type") == self.type_contains:
self.filter_list.append(getattr(model, k).contains(v.get("data")))
if v.get("type") == self.type_exact:
self.filter_list.append(getattr(model, k) == v.get("data"))
if v.get("type") == self.type_neq:
self.filter_list.append(getattr(model, k) != v.get("data"))
if v.get("type") == self.type_greater:
self.filter_list.append(getattr(model, k) > v.get("data"))
if v.get("type") == self.type_less:
self.filter_list.append(getattr(model, k) < v.get("data"))
if v.get("type") == self.type_between:
self.filter_list.append(getattr(model, k).between(v.get("data")[0], v.get("data")[1]))
return and_(*self.filter_list)
+26
View File
@@ -0,0 +1,26 @@
import os
import click
from applications.common.script.initdb import init_db
from applications.common.script.newmodular.new import NewViewModular
def init_script(app):
@app.cli.command()
def init():
init_db()
@app.cli.command()
@click.option('--type', prompt="请输入类型", help='新增的类型')
@click.option('--name', prompt="请输入新增的名称", help='新增的名称')
def new(type,name):
if type == 'view':
if name.count('/') > 1:
print("目前只支持二级目录,多级目录需要蓝图嵌套,本命令暂不支持,请手动创建")
quit()
if type == "view" and os.path.exists(f"applications/view/{name}.py"):
print(f'已经存在视图模块{name}.py')
quit()
NewViewModular(name=name).new_view()
@@ -1,12 +1,17 @@
import sys from dotenv import dotenv_values
sys.path.append('../')
import sqlparse import sqlparse
import pymysql import pymysql
from applications.config.database import HOST, USERNAME, PASSWORD, DATABASE,PORT config = dotenv_values('.flaskenv')
# MySql配置信息
HOST = config.get('MYSQL_HOST') or '127.0.0.1'
PORT = config.get('MYSQL_PORT') or 3306
DATABASE = config.get('MYSQL_DATABASE') or 'PearAdminFlask'
USERNAME = config.get('MYSQL_USERNAME') or 'root'
PASSWORD = config.get('MYSQL_PASSWORD') or '123456'
def is_exist_database(): def is_exist_database():
db = pymysql.connect(host=HOST,port = int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4') db = pymysql.connect(host=HOST, port=int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4')
cursor1 = db.cursor() cursor1 = db.cursor()
sql = "select * from information_schema.SCHEMATA WHERE SCHEMA_NAME = '%s' ; " % DATABASE sql = "select * from information_schema.SCHEMATA WHERE SCHEMA_NAME = '%s' ; " % DATABASE
res = cursor1.execute(sql) res = cursor1.execute(sql)
@@ -15,16 +20,17 @@ def is_exist_database():
def init_database(): def init_database():
db = pymysql.connect(host=HOST,port = int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4') db = pymysql.connect(host=HOST, port=int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4')
cursor1 = db.cursor() cursor1 = db.cursor()
sql = "CREATE DATABASE IF NOT EXISTS %s" % DATABASE sql = "CREATE DATABASE IF NOT EXISTS %s CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" % DATABASE
res = cursor1.execute(sql) res = cursor1.execute(sql)
db.close() db.close()
return res return res
def execute_fromfile(filename): def execute_fromfile(filename):
db = pymysql.connect(host=HOST, user=USERNAME, password=PASSWORD, database=DATABASE, charset='utf8mb4') db = pymysql.connect(host=HOST, port=int(PORT), user=USERNAME, password=PASSWORD, database=DATABASE,
charset='utf8mb4')
fd = open(filename, 'r', encoding='utf-8') fd = open(filename, 'r', encoding='utf-8')
cursor = db.cursor() cursor = db.cursor()
sqlfile = fd.read() sqlfile = fd.read()
@@ -45,13 +51,10 @@ def execute_fromfile(filename):
def init_db(): def init_db():
if is_exist_database(): if is_exist_database():
print('数据库已经存在,为防止误初始化,请动删除 %s 数据库' % str(DATABASE)) print('数据库已经存在,为防止误初始化,请动删除 %s 数据库' % str(DATABASE))
return return
if init_database(): if init_database():
print('数据库%s创建成功' % str(DATABASE)) print('数据库%s创建成功' % str(DATABASE))
execute_fromfile('pear.sql') execute_fromfile('test/pear.sql')
print('表创建成功') print('表创建成功')
print('欢迎使用pear-admin-flask,请使用 flask run 命令启动程序') print('欢迎使用pear-admin-flask,请使用 flask run 命令启动程序')
if __name__ == '__main__':
init_db()
@@ -0,0 +1,92 @@
import os
import jinja2
class NewViewModular():
def __init__(self, name):
self.name = name
def path_is_exists(self):
return os.path.exists(f"applications/view/{self.name.split('/')[0]}")
def new_dirs(self):
if not self.path_is_exists():
# 如果不存在则创建目录
# 创建目录操作函数
os.makedirs(f"applications/view/{self.name.split('/')[0]}")
print(self.name + ' 创建成功')
return True
else:
return False
def add_view(self):
# jinja渲染
templateLoader = jinja2.FileSystemLoader(searchpath='applications/common/script/newmodular/template/')
templateEnv = jinja2.Environment(loader=templateLoader)
TEMPLATE_FILE = "view"
template = templateEnv.get_template(TEMPLATE_FILE, )
templateVars = {"name": self.name}
outputText = template.render(templateVars)
with open(f"applications/view/{self.name}.py", "a", encoding='utf-8') as f:
f.write(outputText)
f.close()
def add_init(self):
# 判断__init__.py
if os.path.exists(f"applications/view/{self.name.split('/')[0]}/__init__.py"):
with open(f"applications/view/{self.name.split('/')[0]}/__init__.py", "r") as file:
lines = file.readlines()
print(lines)
file.close()
import_line_num = 0
for line in lines:
if 'def' in line:
import_line_num = lines.index(line) - 2
with open(f"applications/view/{self.name.split('/')[0]}/__init__.py", "w") as file:
lines.insert(import_line_num,
f"from applications.view.{self.name.replace('/', '.')} import {self.name.replace('/', '_')}\n")
print(lines)
lines.insert(len(lines) - 1, f" app.register_blueprint({self.name.replace('/', '_')})\n")
print(lines)
file.writelines(lines)
file.close()
return True
else:
templateLoader = jinja2.FileSystemLoader(searchpath='applications/common/script/newmodular/template/')
templateEnv = jinja2.Environment(loader=templateLoader)
TEMPLATE_FILE = "__init__"
template = templateEnv.get_template(TEMPLATE_FILE, )
templateVars = {"name": self.name}
output = template.render(templateVars)
with open(f"applications/view/{self.name.split('/')[0]}/__init__.py", "a", encoding='utf-8') as f:
f.write(output)
f.close()
self.add_root_init()
return True
def add_root_init(self):
with open(f"applications/view/__init__.py", "r") as file:
lines = file.readlines()
file.close()
import_line_num = 0
for line in lines:
if 'def' in line:
import_line_num = lines.index(line) - 2
with open(f"applications/view/__init__.py", "w") as file:
lines.insert(
import_line_num,
f"from applications.view.{self.name.split('/')[0]} import register_{self.name.split('/')[0]}_views\n")
print(lines)
lines.insert(len(lines) - 1, f" register_{self.name.split('/')[0]}_views(app)\n")
print(lines)
file.writelines(lines)
file.close()
def new_view(self):
self.new_dirs()
self.add_view()
self.add_init()
@@ -0,0 +1,6 @@
from flask import Flask
from applications.view.{{name.replace('/','.')}} import {{name.replace('/','_')}}
def register_{{name.split('/')[0]}}_views(app: Flask):
app.register_blueprint({{name.replace('/','_')}})
@@ -0,0 +1,8 @@
from flask import Blueprint
{{name.replace('/','_')}} = Blueprint('{{name.replace('/','_')}}', __name__, url_prefix='/{{name}}')
@{{name.replace('/','_')}}.route('/')
def index():
return "这是{{name}}路由"
+54
View File
@@ -0,0 +1,54 @@
from apscheduler.events import (
EVENT_JOB_ADDED,
EVENT_JOB_ERROR,
EVENT_JOB_EXECUTED,
EVENT_JOB_MISSED,
EVENT_JOB_REMOVED,
EVENT_JOB_SUBMITTED,
)
from applications.extensions.init_apscheduler import scheduler
def job_missed(event):
"""Job missed event."""
with scheduler.app.app_context():
print(event) # noqa: T001
def job_error(event):
"""Job error event."""
with scheduler.app.app_context():
print(event) # noqa: T001
def job_executed(event):
"""Job executed event."""
with scheduler.app.app_context():
print(event) # noqa: T001
def job_added(event):
"""Job added event."""
with scheduler.app.app_context():
print(event) # noqa: T001
def job_removed(event):
"""Job removed event."""
with scheduler.app.app_context():
print(event) # noqa: T001
def job_submitted(event):
"""Job scheduled to run event."""
with scheduler.app.app_context():
print(event) # noqa: T001
scheduler.add_listener(job_missed, EVENT_JOB_MISSED)
scheduler.add_listener(job_error, EVENT_JOB_ERROR)
scheduler.add_listener(job_executed, EVENT_JOB_EXECUTED)
scheduler.add_listener(job_added, EVENT_JOB_ADDED)
scheduler.add_listener(job_removed, EVENT_JOB_REMOVED)
scheduler.add_listener(job_submitted, EVENT_JOB_SUBMITTED)
+15
View File
@@ -0,0 +1,15 @@
import datetime
task_list = ['task2', 'task3', 'task4']
def task2(a, b):
print(f'定时任务_1_{a},{b},{datetime.datetime.now()}')
def task3(a, b):
print(f'定时任务_2_{a}{b}{datetime.datetime.now()}')
def task4(a, b):
print(f'定时任务_4_{a}{b}{datetime.datetime.now()}')
+14
View File
@@ -0,0 +1,14 @@
from captcha.image import ImageCaptcha
from PIL import Image
from random import choices
def gen_captcha(content='2345689abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ'):
""" 生成验证码 """
image = ImageCaptcha()
# 获取字符串
captcha_text = "".join(choices(content, k=4)).lower()
# 生成图像
captcha_image = Image.open(image.generate(captcha_text))
return captcha_text, captcha_image
+24
View File
@@ -0,0 +1,24 @@
from flask import jsonify
def success_api(msg: str = "成功"):
""" 成功响应 默认值”成功“ """
return jsonify(success=True, msg=msg)
def fail_api(msg: str = "失败"):
""" 失败响应 默认值“失败” """
return jsonify(success=False, msg=msg)
def table_api(msg: str = "", count=0, data=None, limit=10):
""" 动态表格渲染响应 """
res = {
'msg': msg,
'code': 0,
'data': data,
'count': count,
'limit': limit
}
return jsonify(res)
+13
View File
@@ -0,0 +1,13 @@
from flask_mail import Message
from applications.extensions.init_mail import mail
# send_mail(subject='title', recipients=['123@qq.com'], content='body')
def send_mail(subject, recipients, content):
try:
message = Message(subject=subject, recipients=recipients, body=content)
mail.send(message)
except Exception as e:
print('邮箱发送出错了')
raise
+25
View File
@@ -0,0 +1,25 @@
from functools import wraps
from flask import abort, request, jsonify, session
from flask_login import login_required
from applications.common.admin_log import admin_log
def authorize(power: str, log: bool = False):
def decorator(func):
@login_required
@wraps(func)
def wrapper(*args, **kwargs):
if not power in session.get('permissions'):
if log:
admin_log(request=request, is_access=False)
if request.method == 'GET':
abort(403)
else:
return jsonify(success=False, msg="权限不足!")
if log:
admin_log(request=request, is_access=True)
return func(*args, **kwargs)
return wrapper
return decorator
+36
View File
@@ -0,0 +1,36 @@
import os
from flask import current_app
from sqlalchemy import desc
from applications.extensions import db
from applications.extensions.init_upload import photos
from applications.models import Photo
from applications.schemas import PhotoOutSchema
from applications.common.curd import model_to_dicts
def get_photo(page, limit):
photo = Photo.query.order_by(desc(Photo.create_time)).paginate(page=page, per_page=limit, error_out=False)
count = Photo.query.count()
data = model_to_dicts(schema=PhotoOutSchema, data=photo.items)
return data, count
def upload_one(photo, mime):
filename = photos.save(photo)
file_url = '/_uploads/photos/'+filename
# file_url = photos.url(filename)
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
size = os.path.getsize(upload_url + '/' + filename)
photo = Photo(name=filename, href=file_url, mime=mime, size=size)
db.session.add(photo)
db.session.commit()
return file_url
def delete_photo_by_id(_id):
photo_name = Photo.query.filter_by(id=_id).first().name
photo = Photo.query.filter_by(id=_id).delete()
db.session.commit()
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
os.remove(upload_url + '/' + photo_name)
return photo
+20
View File
@@ -0,0 +1,20 @@
# xss过滤
from flask import abort, make_response, jsonify
def xss_escape(s: str):
if s is None:
return None
else:
return s.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;").replace("'", "&#39;").replace('"',
"&#34;")
def check_data(schema, data):
errors = schema.validate(data)
for k, v in errors.items():
for i in v:
# print("{}{}".format(k, i))
msg = "{}{}".format(k, i)
if errors:
abort(make_response(jsonify(result=False, msg=msg), 200))
-9
View File
@@ -1,9 +0,0 @@
# 密钥配置(flask session需要) 记得修改
import os
SECRET_KEY = 'pear-admin-flask'
UPLOADED_PHOTOS_DEST = 'static/upload'
UPLOADED_FILES_ALLOW = ['gif','jpg']
JSON_AS_ASCII = False
-13
View File
@@ -1,13 +0,0 @@
# 数据库连接
HOST = '127.0.0.1'
PORT = '3306'
DATABASE = 'PearAdminFlask'
USERNAME = 'root'
PASSWORD = 'root'
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,password=PASSWORD, host=HOST,port=PORT, db=DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = False
SQLALCHEMY_POOL_RECYCLE=8
+1
View File
@@ -0,0 +1 @@
from .config import config
+87
View File
@@ -0,0 +1,87 @@
import logging
import os
from urllib.parse import quote_plus as urlquote
class BaseConfig:
SYSTEM_NAME = os.getenv('SYSTEM_NAME', 'Pear Admin')
# 主题面板的链接列表配置
SYSTEM_PANEL_LINKS = [
{
"icon": "layui-icon layui-icon-auz",
"title": "官方网站",
"href": "http://www.pearadmin.com"
},
{
"icon": "layui-icon layui-icon-auz",
"title": "开发文档",
"href": "http://www.pearadmin.com"
},
{
"icon": "layui-icon layui-icon-auz",
"title": "开源地址",
"href": "https://gitee.com/Jmysy/Pear-Admin-Layui"
}
]
UPLOADED_PHOTOS_DEST = 'static/upload'
UPLOADED_FILES_ALLOW = ['gif', 'jpg']
# JSON配置
JSON_AS_ASCII = False
SECRET_KEY = os.getenv('SECRET_KEY', 'dev key')
# redis配置
REDIS_HOST = os.getenv('REDIS_HOST') or "127.0.0.1"
REDIS_PORT = int(os.getenv('REDIS_PORT') or 6379)
# mysql 配置
MYSQL_USERNAME = os.getenv('MYSQL_USERNAME') or "root"
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD') or "123456"
MYSQL_HOST = os.getenv('MYSQL_HOST') or "127.0.0.1"
MYSQL_PORT = int(os.getenv('MYSQL_PORT') or 3306)
MYSQL_DATABASE = os.getenv('MYSQL_DATABASE') or "PearAdminFlask"
# mysql 数据库的配置信息
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USERNAME}:{urlquote(MYSQL_PASSWORD)}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}?charset=utf8mb4"
# 默认日志等级
LOG_LEVEL = logging.WARN
#
MAIL_SERVER = os.getenv('MAIL_SERVER') or 'smtp.qq.com'
MAIL_USE_TLS = False
MAIL_USE_SSL = True
MAIL_PORT = 465
MAIL_USERNAME = os.getenv('MAIL_USERNAME') or '123@qq.com'
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD') or 'XXXXX' # 生成的授权码
# 默认发件人的邮箱,这里填写和MAIL_USERNAME一致即可
MAIL_DEFAULT_SENDER = ('pear admin', os.getenv('MAIL_USERNAME') or '123@qq.com')
class TestingConfig(BaseConfig):
""" 测试配置 """
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' # 内存数据库
class DevelopmentConfig(BaseConfig):
""" 开发配置 """
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = False
class ProductionConfig(BaseConfig):
"""生成环境配置"""
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
SQLALCHEMY_POOL_RECYCLE = 8
LOG_LEVEL = logging.ERROR
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig
}
+23
View File
@@ -0,0 +1,23 @@
from flask import Flask
from .init_sqlalchemy import db, ma, init_databases
from .init_login import init_login_manager
from .init_debug_tool import init_debug_tool
from .init_template_directives import init_template_directives
from .init_error_views import init_error_views
from .init_mail import init_mail
from .init_apscheduler import init_scheduler
from .init_upload import init_upload
from .init_dotenv import init_dotenv
def init_plugs(app: Flask) -> None:
# init_debug_tool(app)
init_login_manager(app)
init_databases(app)
init_template_directives(app)
init_error_views(app)
init_mail(app)
init_scheduler(app)
init_upload(app)
init_dotenv()
@@ -0,0 +1,12 @@
from flask import Flask
from flask_apscheduler import APScheduler
scheduler = APScheduler()
def init_scheduler(app: Flask):
scheduler.init_app(app)
with app.app_context():
from applications.common.tasks import tasks
scheduler.start()
from applications.common.tasks import events
@@ -1,6 +1,7 @@
from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension from flask_debugtoolbar import DebugToolbarExtension
def OpenDug(app): def init_debug_tool(app: Flask):
toolbar = DebugToolbarExtension() toolbar = DebugToolbarExtension()
toolbar.init_app(app) toolbar.init_app(app)
+14
View File
@@ -0,0 +1,14 @@
import os
from dotenv import load_dotenv
root_path = os.path.abspath(os.path.dirname(__file__)).split('applications')[0]
# dot_env_path = os.path.join(root_path, '.env')
flask_env_path = os.path.join(root_path, '.flaskenv')
# if os.path.exists(dot_env_path):
# load_dotenv(dot_env_path)
def init_dotenv():
if os.path.exists(flask_env_path):
load_dotenv(flask_env_path)
@@ -0,0 +1,34 @@
from flask import render_template, jsonify
def init_error_views(app):
@app.errorhandler(403)
def page_not_found(e):
return render_template('errors/403.html'), 403
@app.errorhandler(404)
def page_not_found(e):
return render_template('errors/404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('errors/500.html'), 500
# Return validation errors as JSON
@app.errorhandler(422)
@app.errorhandler(400)
def handle_error(err):
headers = err.data.get("headers", None)
messages = err.data.get("messages", ["Invalid request."]).get('json')
print(err.data.get("messages"))
print(messages.items())
msg = ''
for i in messages.items():
msg = str(i[0]) + str(i[1][0])
break
if headers:
return jsonify({"success": False, "msg": msg})
else:
return jsonify({"success": False, "msg": msg})
@@ -1,15 +1,15 @@
from flask_login import LoginManager from flask_login import LoginManager
def init_flask_login(app): def init_login_manager(app):
login_manager = LoginManager() login_manager = LoginManager()
login_manager.init_app(app) login_manager.init_app(app)
login_manager.login_view = 'adminIndex.login' login_manager.login_view = 'passport.login'
login_manager.login_message = u'请登录以访问此页面' login_manager.login_message = u'请登录以访问此页面'
@login_manager.user_loader @login_manager.user_loader
def load_user(user_id): def load_user(user_id):
from applications.models.admin import User from applications.models import User
user = User.query.get(int(user_id)) user = User.query.get(int(user_id))
return user return user
+8
View File
@@ -0,0 +1,8 @@
from flask import Flask
from flask_mail import Mail
mail = Mail()
def init_mail(app: Flask):
mail.init_app(app)
@@ -0,0 +1,93 @@
import datetime
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy, BaseQuery
from flask_marshmallow import Marshmallow
from marshmallow import fields
from marshmallow.validate import (
URL, Email, Range, Length, Equal, Regexp,
Predicate, NoneOf, OneOf, ContainsOnly
)
URL.default_message = '无效的链接'
Email.default_message = '无效的邮箱地址'
Range.message_min = '不能小于{min}'
Range.message_max = '不能小于{max}'
Range.message_all = '不能超过{min}{max}这个范围'
Length.message_min = '长度不得小于{min}'
Length.message_max = '长度不得大于{max}'
Length.message_all = '长度不能超过{min}{max}这个范围'
Length.message_equal = '长度必须等于{equal}'
Equal.default_message = '必须等于{other}'
Regexp.default_message = '非法输入'
Predicate.default_message = '非法输入'
NoneOf.default_message = '非法输入'
OneOf.default_message = '无效的选择'
ContainsOnly.default_message = '一个或多个无效的选择'
fields.Field.default_error_messages = {
"required": "缺少必要数据",
"null": "数据不能为空",
"validator_failed": "非法数据",
}
fields.Str.default_error_messages = {
'invalid': "不是合法文本"
}
fields.Int.default_error_messages = {
"invalid": "不是合法整数"
}
fields.Number.default_error_messages = {
"invalid": "不是合法数字"
}
fields.Boolean.default_error_messages = {
"invalid": "不是合法布尔值"
}
class Query(BaseQuery):
def soft_delete(self):
return self.update({"delete_at": datetime.datetime.now()})
def logic_all(self):
return self.filter_by(delete_at=None).all()
def all_json(self, schema: Marshmallow().Schema):
return schema(many=True).dump(self.all())
def layui_paginate(self):
return self.paginate(page=request.args.get('page', type=int),
per_page=request.args.get('limit', type=int),
error_out=False)
def layui_paginate_json(self, schema: Marshmallow().Schema):
"""
返回dict
"""
_res = self.paginate(
page=request.args.get('page', type=int),
per_page=request.args.get('limit', type=int),
error_out=False
)
return schema(many=True).dump(_res.items), _res.total, _res.page, _res.per_page
def layui_paginate_db_json(self):
"""
db.query(A.name).layui_paginate_db_json()
"""
_res = self.paginate(page=request.args.get('page', type=int),
per_page=request.args.get('limit', type=int),
error_out=False)
return [dict(i) for i in _res.items], _res.total
db = SQLAlchemy(query_class=Query)
ma = Marshmallow()
def init_databases(app: Flask):
db.init_app(app)
ma.init_app(app)
@@ -0,0 +1,7 @@
from flask import session
def init_template_directives(app):
@app.template_global()
def authorize(power):
return bool(power in session.get('permissions'))
+9
View File
@@ -0,0 +1,9 @@
from flask import Flask
from flask_uploads import configure_uploads
from flask_uploads import UploadSet, IMAGES
photos = UploadSet('photos', IMAGES)
def init_upload(app: Flask):
configure_uploads(app, photos)
+9
View File
@@ -0,0 +1,9 @@
from marshmallow import EXCLUDE
from webargs.flaskparser import FlaskParser
class Parser(FlaskParser):
DEFAULT_UNKNOWN_BY_LOCATION = {"query": EXCLUDE}
parser = Parser()
+9 -3
View File
@@ -1,3 +1,9 @@
from flask_sqlalchemy import SQLAlchemy from .admin_dept import Dept
from .admin_dict import DictType, DictData
db = SQLAlchemy() from .admin_log import AdminLog
from .admin_photo import Photo
from .admin_power import Power
from .admin_role import Role
from .admin_role_power import role_power
from .admin_user import User
from .admin_user_role import user_role
-93
View File
@@ -1,93 +0,0 @@
import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from applications.models import db
class User(db.Model, UserMixin):
__tablename__ = 'admin_user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='用户ID')
username = db.Column(db.String(20), comment='用户名')
realname =db.Column(db.String(20), comment='真实名字')
password_hash = db.Column(db.String(128), comment='哈希密码')
enable = db.Column(db.Integer, default=0, comment='启用')
create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
role = db.relationship('Role',secondary="admin_user_role", backref=db.backref('user'),lazy = 'dynamic')
# power = db.relationship('Power',secondary="admin_user_role", backref=db.backref('user'))
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def validate_password(self, password):
return check_password_hash(self.password_hash, password)
# 创建中间表
user_role = db.Table(
"admin_user_role", # 中间表名称
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
db.Column("user_id", db.Integer, db.ForeignKey("admin_user.id"), comment='用户编号'), # 属性 外键
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
)
class Role(db.Model):
__tablename__ = 'admin_role'
id = db.Column(db.Integer, primary_key=True, comment='角色ID')
name = db.Column(db.String(255), comment='角色名称')
code = db.Column(db.String(255), comment='角色标识')
enable = db.Column(db.Integer, comment='是否启用')
remark = db.Column(db.String(255), comment='备注')
details = db.Column(db.String(255), comment='详情')
sort = db.Column(db.Integer, comment='排序')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
power = db.relationship('Power',secondary="admin_role_power", backref=db.backref('role'))
# 创建中间表
role_power = db.Table(
"admin_role_power", # 中间表名称
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
db.Column("power_id", db.Integer, db.ForeignKey("admin_power.id"), comment='用户编号'), # 属性 外键
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
)
class Power(db.Model):
__tablename__ = 'admin_power'
id = db.Column(db.Integer, primary_key=True, comment='权限编号')
name = db.Column(db.String(255), comment='权限名称')
type = db.Column(db.String(1), comment='权限类型')
code = db.Column(db.String(30), comment='权限标识')
url = db.Column(db.String(255), comment='权限路径')
open_type = db.Column(db.String(10), comment='打开方式')
parent_id = db.Column(db.Integer, comment='父类编号')
icon = db.Column(db.String(128), comment='图标')
sort = db.Column(db.Integer, comment='排序')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
enable = db.Column(db.Integer, comment='是否开启')
class AdminLog(db.Model):
__tablename__ = 'admin_admin_log'
id = db.Column(db.Integer, primary_key=True)
method = db.Column(db.String(10))
uid = db.Column(db.Integer)
url = db.Column(db.String(255))
desc = db.Column(db.Text)
ip = db.Column(db.String(255))
success = db.Column(db.Integer)
user_agent = db.Column(db.Text)
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
class Photo(db.Model):
__tablename__ = 'admin_photo'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
href = db.Column(db.String(255))
mime = db.Column(db.CHAR(50), nullable=False)
size = db.Column(db.CHAR(30), nullable=False)
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
+18
View File
@@ -0,0 +1,18 @@
import datetime
from applications.extensions import db
class Dept(db.Model):
__tablename__ = 'admin_dept'
id = db.Column(db.Integer, primary_key=True, comment="部门ID")
parent_id = db.Column(db.Integer, comment="父级编号")
dept_name = db.Column(db.String(50), comment="部门名称")
sort = db.Column(db.Integer, comment="排序")
leader = db.Column(db.String(50), comment="负责人")
phone = db.Column(db.String(20), comment="联系方式")
email = db.Column(db.String(50), comment="邮箱")
status = db.Column(db.Integer, comment='状态(1开启,0关闭)')
remark = db.Column(db.Text, comment="备注")
address = db.Column(db.String(255), comment="详细地址")
create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
+26
View File
@@ -0,0 +1,26 @@
import datetime
from applications.extensions import db
class DictType(db.Model):
__tablename__ = 'admin_dict_type'
id = db.Column(db.Integer, primary_key=True)
type_name = db.Column(db.String(255), comment='字典类型名称')
type_code = db.Column(db.String(255), comment='字典类型标识')
description = db.Column(db.String(255), comment='字典类型描述')
enable = db.Column(db.Integer, comment='是否开启')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
class DictData(db.Model):
__tablename__ = 'admin_dict_data'
id = db.Column(db.Integer, primary_key=True)
data_label = db.Column(db.String(255), comment='字典类型名称')
data_value = db.Column(db.String(255), comment='字典类型标识')
type_code = db.Column(db.String(255), comment='字典类型描述')
is_default = db.Column(db.Integer, comment='是否默认')
enable = db.Column(db.Integer, comment='是否开启')
remark = db.Column(db.String(255), comment='备注')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
+14
View File
@@ -0,0 +1,14 @@
import datetime
from applications.extensions import db
class AdminLog(db.Model):
__tablename__ = 'admin_admin_log'
id = db.Column(db.Integer, primary_key=True)
method = db.Column(db.String(10))
uid = db.Column(db.Integer)
url = db.Column(db.String(255))
desc = db.Column(db.Text)
ip = db.Column(db.String(255))
success = db.Column(db.Integer)
user_agent = db.Column(db.Text)
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
+12
View File
@@ -0,0 +1,12 @@
import datetime
from applications.extensions import db
class Photo(db.Model):
__tablename__ = 'admin_photo'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
href = db.Column(db.String(255))
mime = db.Column(db.CHAR(50), nullable=False)
size = db.Column(db.CHAR(30), nullable=False)
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
+18
View File
@@ -0,0 +1,18 @@
import datetime
from applications.extensions import db
class Power(db.Model):
__tablename__ = 'admin_power'
id = db.Column(db.Integer, primary_key=True, comment='权限编号')
name = db.Column(db.String(255), comment='权限名称')
type = db.Column(db.String(1), comment='权限类型')
code = db.Column(db.String(30), comment='权限标识')
url = db.Column(db.String(255), comment='权限路径')
open_type = db.Column(db.String(10), comment='打开方式')
parent_id = db.Column(db.Integer, comment='父类编号')
icon = db.Column(db.String(128), comment='图标')
sort = db.Column(db.Integer, comment='排序')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
enable = db.Column(db.Integer, comment='是否开启')
+16
View File
@@ -0,0 +1,16 @@
import datetime
from applications.extensions import db
class Role(db.Model):
__tablename__ = 'admin_role'
id = db.Column(db.Integer, primary_key=True, comment='角色ID')
name = db.Column(db.String(255), comment='角色名称')
code = db.Column(db.String(255), comment='角色标识')
enable = db.Column(db.Integer, comment='是否启用')
remark = db.Column(db.String(255), comment='备注')
details = db.Column(db.String(255), comment='详情')
sort = db.Column(db.Integer, comment='排序')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
power = db.relationship('Power', secondary="admin_role_power", backref=db.backref('role'))
+9
View File
@@ -0,0 +1,9 @@
from applications.extensions import db
# 创建中间表
role_power = db.Table(
"admin_role_power", # 中间表名称
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
db.Column("power_id", db.Integer, db.ForeignKey("admin_power.id"), comment='用户编号'), # 属性 外键
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
)
+25
View File
@@ -0,0 +1,25 @@
import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from applications.extensions import db
class User(db.Model, UserMixin):
__tablename__ = 'admin_user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='用户ID')
username = db.Column(db.String(20), comment='用户名')
realname = db.Column(db.String(20), comment='真实名字')
avatar = db.Column(db.String(255), comment='头像', default="/static/admin/admin/images/avatar.jpg")
remark = db.Column(db.String(255), comment='备注')
password_hash = db.Column(db.String(128), comment='哈希密码')
enable = db.Column(db.Integer, default=0, comment='启用')
dept_id = db.Column(db.Integer, comment='部门id')
create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
role = db.relationship('Role', secondary="admin_user_role", backref=db.backref('user'), lazy='dynamic')
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def validate_password(self, password):
return check_password_hash(self.password_hash, password)
+9
View File
@@ -0,0 +1,9 @@
from applications.extensions import db
# 创建中间表
user_role = db.Table(
"admin_user_role", # 中间表名称
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
db.Column("user_id", db.Integer, db.ForeignKey("admin_user.id"), comment='用户编号'), # 属性 外键
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
)
+7
View File
@@ -0,0 +1,7 @@
from .admin_user import UserOutSchema
from .admin_role import RoleOutSchema
from .admin_power import PowerOutSchema, PowerOutSchema2
from .admin_dict import DictDataOutSchema, DictTypeOutSchema
from .admin_dept import DeptOutSchema
from .admin_log import LogOutSchema
from .admin_photo import PhotoOutSchema
+25
View File
@@ -0,0 +1,25 @@
from applications.extensions import ma
from marshmallow import fields, validate
class DeptInSchema(ma.Schema):
parentId = fields.Integer(required=True)
deptName = fields.Str(required=True)
leader = fields.Str(required=True)
phone = fields.Str(required=True)
email = fields.Str(validate=validate.Email())
address = fields.Str()
status = fields.Str(validate=validate.OneOf(["0", "1"]))
sort = fields.Integer()
class DeptOutSchema(ma.Schema):
deptId = fields.Integer(attribute="id")
parentId = fields.Integer(attribute="parent_id")
deptName = fields.Str(attribute="dept_name")
leader = fields.Str()
phone = fields.Str()
email = fields.Str(validate=validate.Email())
address = fields.Str()
status = fields.Str(validate=validate.OneOf(["0", "1"]))
sort = fields.Integer()
+21
View File
@@ -0,0 +1,21 @@
from applications.extensions import ma
from marshmallow import fields
class DictTypeOutSchema(ma.Schema):
id = fields.Str(attribute="id")
typeName = fields.Str(attribute="type_name")
typeCode = fields.Str(attribute="type_code")
description = fields.Str(attribute="description")
createTime = fields.Str(attribute="create_time")
updateName = fields.Str(attribute="update_time")
remark = fields.Str()
enable = fields.Str()
class DictDataOutSchema(ma.Schema):
dataId = fields.Str(attribute="id")
dataLabel = fields.Str(attribute="data_label")
dataValue = fields.Str(attribute="data_value")
remark = fields.Str()
enable = fields.Str()
+14
View File
@@ -0,0 +1,14 @@
from applications.extensions import ma
from marshmallow import fields
class LogOutSchema(ma.Schema):
id = fields.Integer()
method = fields.Str()
uid = fields.Str()
url = fields.Str()
desc = fields.Str()
ip = fields.Str()
user_agent = fields.Str()
success = fields.Bool()
create_time = fields.DateTime()
+12
View File
@@ -0,0 +1,12 @@
from applications.extensions import ma
from marshmallow import fields
class PhotoOutSchema(ma.Schema):
id = fields.Integer()
name = fields.Str()
href = fields.Str()
mime = fields.Str()
size = fields.Str()
ext = fields.Str()
create_time = fields.DateTime()
+32
View File
@@ -0,0 +1,32 @@
from applications.extensions import ma
from marshmallow import fields
# 权限models序列化类
class PowerOutSchema(ma.Schema):
id = fields.Integer()
title = fields.Str(attribute="name")
type = fields.Str()
code = fields.Str()
href = fields.Str(attribute="url")
openType = fields.Str(attribute="open_type")
parent_id = fields.Integer()
icon = fields.Str()
sort = fields.Integer()
create_time = fields.DateTime()
update_time = fields.DateTime()
enable = fields.Integer()
class PowerOutSchema2(ma.Schema): # 序列化类
powerId = fields.Str(attribute="id")
powerName = fields.Str(attribute="name")
powerType = fields.Str(attribute="type")
powerUrl = fields.Str(attribute="url")
openType = fields.Str(attribute="open_type")
parentId = fields.Str(attribute="parent_id")
icon = fields.Str()
sort = fields.Integer()
create_time = fields.DateTime()
update_time = fields.DateTime()
enable = fields.Integer()
+14
View File
@@ -0,0 +1,14 @@
from applications.extensions import ma
from marshmallow import fields
class RoleOutSchema(ma.Schema):
id = fields.Integer()
roleName = fields.Str(attribute="name")
roleCode = fields.Str(attribute="code")
enable = fields.Str()
remark = fields.Str()
details = fields.Str()
sort = fields.Integer()
create_at = fields.DateTime()
update_at = fields.DateTime()
+20
View File
@@ -0,0 +1,20 @@
from applications.extensions import ma
from marshmallow import fields
from applications.models import Dept
# 用户models的序列化类
class UserOutSchema(ma.Schema):
id = fields.Integer()
username = fields.Str()
realname = fields.Str()
enable = fields.Integer()
create_at = fields.DateTime()
update_at = fields.DateTime()
dept = fields.Method("get_dept")
def get_dept(self, obj):
if obj.dept_id != None:
return Dept.query.filter_by(id=obj.dept_id).first().dept_name
else:
return None
-26
View File
@@ -1,26 +0,0 @@
from captcha.image import ImageCaptcha
from PIL import Image
import random
# 定义随机方法
def random_captcha():
# 定义一个容器
captcha_text = []
for i in range(4):
# 定义验证码字符
c = random.choice(['0', '1', '2', '4', '6', '8'])
captcha_text.append(c)
# 返回一个随机生成的字符串
return ''.join(captcha_text)
# 生成验证码方法
def gen_captcha():
# 定义图片对象
image = ImageCaptcha()
# 获取字符串
captcha_text = random_captcha()
# 生成图像
captcha_image = Image.open(image.generate(captcha_text))
return captcha_text, captcha_image
-106
View File
@@ -1,106 +0,0 @@
import datetime
import json
import pymysql
from applications.config.database import HOST, USERNAME, PASSWORD, DATABASE, PORT
DB_CONFIG = {
"host": HOST,
"port": int(PORT),
"user": USERNAME,
"password": PASSWORD,
"db": DATABASE,
"charset": "utf8"
}
class SQLManager(object):
# 初始化实例方法
def __init__(self):
self.conn = None
self.cursor = None
self.connect()
# 连接数据库
def connect(self):
self.conn = pymysql.connect(
host=DB_CONFIG.get('host'),
port=DB_CONFIG.get('port'),
user=DB_CONFIG.get('user'),
passwd=DB_CONFIG.get('password'),
db=DB_CONFIG.get('db'),
)
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
# 查询多条数据
def get_list(self, sql, args=None):
self.cursor.execute(sql, args)
result = self.cursor.fetchall()
return result
# 查询多条数据后关闭
def get_list_close(self, sql, args=None):
self.cursor.execute(sql, args)
result = self.cursor.fetchall()
self.close()
return result
# 查询单条数据
def get_one(self, sql, args=None):
self.cursor.execute(sql, args)
result = self.cursor.fetchone()
return result
# 查询单条数据后关闭
def get_one_close(self, sql, args=None):
self.cursor.execute(sql, args)
result = self.cursor.fetchone()
self.close()
return result
# 执行单条SQL语句
def moddify(self, sql, args=None):
self.cursor.execute(sql, args)
self.conn.commit()
# 执行多条SQL语句
def multi_modify(self, sql, args=None):
self.cursor.executemany(sql, args)
self.conn.commit()
# 创建单条记录的语句
def create(self, sql, args=None):
self.cursor.execute(sql, args)
self.conn.commit()
last_id = self.cursor.lastrowid
return last_id
# 关闭数据库cursor和连接
def close(self):
self.cursor.close()
self.conn.close()
# 进入with语句自动执行
def __enter__(self):
return self
# 退出with语句块自动执行
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
"""
原生查询dict的datetime解析器
用法
json.dumps(dictxxx, cls=DateEncoder)
"""
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.strftime("%Y-%m-%d %H:%M:%S")
else:
return json.JSONEncoder.default(self, obj)
-47
View File
@@ -1,47 +0,0 @@
import os
from flask import current_app
from flask_marshmallow import Marshmallow
from marshmallow import fields
from sqlalchemy import desc
from applications.models import db
from applications.models.admin import Photo
from applications.service.upload import photos
ma = Marshmallow()
class PhotoSchema(ma.Schema):
id = fields.Integer()
name = fields.Str()
href = fields.Str()
mime = fields.Str()
size = fields.Str()
ext = fields.Str()
create_time = fields.DateTime()
def get_photo(page, limit):
photo = Photo.query.order_by(desc(Photo.create_time)).paginate(page=page, per_page=limit, error_out=False)
count = Photo.query.count()
role_schema = PhotoSchema(many=True)
output = role_schema.dump(photo.items)
return output, count
def upload_one(photo,mime):
filename = photos.save(photo)
file_url = photos.url(filename)
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
size = os.path.getsize(upload_url + '/' + filename)
photo = Photo(name=filename, href=file_url, mime=mime, size=size)
db.session.add(photo)
db.session.commit()
return upload_url
def delete_photo_by_id(id):
photo_name = Photo.query.filter_by(id=id).first().name
photo = Photo.query.filter_by(id=id).delete()
db.session.commit()
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
os.remove(upload_url + '/' + photo_name)
return photo
-93
View File
@@ -1,93 +0,0 @@
from io import BytesIO
from flask import session, make_response
from flask_login import current_user
from flask_marshmallow import Marshmallow
from marshmallow import fields
from applications.models.admin import Power
from applications.service.CaptchaTool import gen_captcha
ma = Marshmallow()
# 权限models序列化类
class PowerSchema(ma.Schema):
id = fields.Integer()
title = fields.Str(attribute="name")
type = fields.Str()
code = fields.Str()
href = fields.Str(attribute="url")
open_type = fields.Str()
parent_id = fields.Integer()
icon = fields.Str()
sort = fields.Integer()
create_time = fields.DateTime()
update_time = fields.DateTime()
enable = fields.Integer()
# 授权路由存入session
def add_auth_session():
role = current_user.role
user_power = []
for i in role:
if i.enable == 0:
continue
for p in i.power:
if p.enable == 0:
continue
user_power.append(p.code)
session['permissions'] = user_power
# 生成菜单树
def make_menu_tree():
# power0 = Power.query.filter(
# Power.type == 0,
# ).all()
# power1 = Power.query.filter(
# Power.type == 1
# ).all()
role = current_user.role
power0 = []
power1 = []
for i in role:
if i.enable == 0:
continue
for p in i.power:
if p.enable == 0:
continue
if int(p.type) == 0:
power0.append(p)
else:
power1.append(p)
power_schema = PowerSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
power0_dict = power_schema.dump(power0) # 生成可序列化对象
power1_dict = power_schema.dump(power1) # 生成可序列化对象
power0_dict = sorted(power0_dict, key=lambda i: i['sort'])
power1_dict = sorted(power1_dict, key=lambda i: i['sort'])
# print(power0)
# print(power1)
menu = []
for p0 in power0_dict:
for p1 in power1_dict:
if p0.get('id') == p1.get('parent_id'):
if p0.get("children") is None:
p0['children'] = []
p0['children'].append(p1)
menu.append(p0)
return menu
# 生成验证码
def get_captcha():
code, image = gen_captcha()
out = BytesIO()
session["code"] = code
image.save(out, 'png')
out.seek(0)
resp = make_response(out.read())
resp.content_type = 'image/png'
return resp, code
-130
View File
@@ -1,130 +0,0 @@
from flask_marshmallow import Marshmallow
from marshmallow import fields
from applications.models import db
from applications.models.admin import Power, Role
ma = Marshmallow()
class PowerSchema(ma.Schema): # 序列化类
powerId = fields.Str(attribute="id")
powerName = fields.Str(attribute="name")
powerType = fields.Str(attribute="type")
powerUrl = fields.Str(attribute="url")
openType = fields.Str(attribute="pen_type")
parentId = fields.Str(attribute="parent_id")
icon = fields.Str()
sort = fields.Integer()
create_time = fields.DateTime()
update_time = fields.DateTime()
enable = fields.Integer()
def get_power_dict():
power = Power.query.all()
power_schema = PowerSchema(many=True)
power_dict = power_schema.dump(power)
return power_dict
# 选择父节点
def select_parent():
power = Power.query.all()
power_schema = PowerSchema(many=True)
power_dict = power_schema.dump(power)
power_dict.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
return power_dict
# 增加权限
def save_power(req):
icon = req.get("icon")
openType = req.get("openType")
parentId = req.get("parentId")
powerCode = req.get("powerCode")
powerName = req.get("powerName")
powerType = req.get("powerType")
powerUrl = req.get("powerUrl")
sort = req.get("sort")
power = Power(
icon=icon,
open_type=openType,
parent_id=parentId,
code=powerCode,
name=powerName,
type=powerType,
url=powerUrl,
sort=sort,
enable=1
)
r = db.session.add(power)
db.session.commit()
return r
# 根据id查询权限
def get_power_by_id(id):
p = Power.query.filter_by(id=id).first()
return p
# 更新权限
def update_power(req_json):
id = req_json.get("powerId")
data = {
"icon": req_json.get("icon"),
"open_type": req_json.get("openType"),
"parent_id": req_json.get("parentId"),
"code": req_json.get("powerCode"),
"name": req_json.get("powerName"),
"type": req_json.get("powerType"),
"url": req_json.get("powerUrl"),
"sort": req_json.get("sort")
}
print(data)
power = Power.query.filter_by(id=id).update(data)
db.session.commit()
print(power)
return power
# 启动权限
def enable_status(id):
enable = 1
user = Power.query.filter_by(id=id).update({"enable": enable})
if user:
db.session.commit()
return True
return False
# 停用权限
def disable_status(id):
enable = 0
user = Power.query.filter_by(id=id).update({"enable": enable})
if user:
db.session.commit()
return True
return False
# 删除权限(目前没有判断父节点自动删除子节点)
def remove_power(id):
power = Power.query.filter_by(id=id).first()
role_id_list = []
roles = power.role
for role in roles:
role_id_list.append(role.id)
roles = Role.query.filter(Role.id.in_(role_id_list)).all()
for p in roles:
power.role.remove(p)
r = Power.query.filter_by(id=id).delete()
db.session.commit()
return r
# 批量删除权限
def batch_remove(ids):
for id in ids:
remove_power(id)
-180
View File
@@ -1,180 +0,0 @@
from flask import jsonify
from flask_marshmallow import Marshmallow
from marshmallow import fields
from sqlalchemy import and_
from applications.models import db
from applications.models.admin import Role, Power, User
ma = Marshmallow()
class RoleSchema(ma.Schema):
id = fields.Integer()
roleName = fields.Str(attribute="name")
roleCode = fields.Str(attribute="code")
enable = fields.Str()
remark = fields.Str()
details = fields.Str()
sort = fields.Integer()
create_at = fields.DateTime()
update_at = fields.DateTime()
class PowerSchema(ma.Schema): # 序列化类
powerId = fields.Str(attribute="id")
powerName = fields.Str(attribute="name")
powerType = fields.Str(attribute="type")
powerUrl = fields.Str(attribute="url")
openType = fields.Str(attribute="pen_type")
parentId = fields.Str(attribute="parent_id")
icon = fields.Str()
sort = fields.Integer()
create_time = fields.DateTime()
update_time = fields.DateTime()
enable = fields.Integer()
# 获取角色对象
def get_role_data(page, limit, filters):
role = Role.query.filter(and_(*[getattr(Role, k).like(v) for k, v in filters.items()])).paginate(page=page,
per_page=limit,
error_out=False)
count = Role.query.count()
return role, count
# 获取角色dict
def get_role_data_dict(page, limit, filters):
role, count = get_role_data(page, limit, filters)
role_schema = RoleSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
output = role_schema.dump(role.items) # 生成可序列化对象
return output, count
# 增加角色
def add_role(req):
details = req.get("details")
enable = req.get("enable")
roleCode = req.get("roleCode")
roleName = req.get("roleName")
sort = req.get("sort")
role = Role(
details=details,
enable=enable,
code=roleCode,
name=roleName,
sort=sort
)
db.session.add(role)
db.session.commit()
# 通过id获取角色
def get_role_by_id(id):
r = Role.query.filter_by(id=id).first()
return r
# 更新角色
def update_role(req_json):
id = req_json.get("roleId")
data = {
"code": req_json.get("roleCode"),
"name": req_json.get("roleName"),
"sort": req_json.get("sort"),
"enable": req_json.get("enable"),
"details": req_json.get("details")
}
print(data)
role = Role.query.filter_by(id=id).update(data)
db.session.commit()
return role
# 获取角色的权限
def get_role_power(id):
role = Role.query.filter_by(id=id).first()
check_powers = role.power
check_powers_list = []
for cp in check_powers:
check_powers_list.append(cp.id)
powers = Power.query.all()
power_schema = PowerSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
output = power_schema.dump(powers) # 生成可序列化对象
for i in output:
if int(i.get("powerId")) in check_powers_list:
i["checkArr"] = "1"
else:
i["checkArr"] = "0"
return output
# 更新角色权限
def update_role_power(id, power_list):
role = Role.query.filter_by(id=id).first()
power_id_list = []
for p in role.power:
power_id_list.append(p.id)
print(p.id)
print(power_id_list)
powers = Power.query.filter(Power.id.in_(power_id_list)).all()
for p in powers:
role.power.remove(p)
powers = Power.query.filter(Power.id.in_(power_list)).all()
for p in powers:
role.power.append(p)
db.session.commit()
# 启动角色
def enable_status(id):
enable = 1
role = Role.query.filter_by(id=id).update({"enable": enable})
if role:
db.session.commit()
return True
return False
# 停用角色
def disable_status(id):
enable = 0
role = Role.query.filter_by(id=id).update({"enable": enable})
if role:
db.session.commit()
return True
return False
# 删除角色
def remove_role(id):
role = Role.query.filter_by(id=id).first()
# 删除该角色的权限
power_id_list = []
for p in role.power:
power_id_list.append(p.id)
powers = Power.query.filter(Power.id.in_(power_id_list)).all()
for p in powers:
role.power.remove(p)
user_id_list = []
for u in role.user:
user_id_list.append(u.id)
users = User.query.filter(User.id.in_(user_id_list)).all()
for u in users:
role.user.remove(u)
r = Role.query.filter_by(id=id).delete()
db.session.commit()
return r
# 批量删除
def batch_remove(ids):
# role = Role.query.filter(Role.id.in_(ids)).delete(synchronize_session=False)
# db.session.commit()
for id in ids:
remove_role(id)
-142
View File
@@ -1,142 +0,0 @@
from flask import jsonify
from flask_login import current_user
from flask_marshmallow import Marshmallow
from marshmallow import fields
from sqlalchemy import and_
from sqlalchemy.sql import exists
from applications.models import db
from applications.models.admin import User, Role
from sqlalchemy.orm.dynamic import AppenderQuery
ma = Marshmallow()
# 用户models的序列化类
class UserSchema(ma.Schema):
id = fields.Integer()
username = fields.Str()
realname = fields.Str()
enable = fields.Integer()
create_at = fields.DateTime()
update_at = fields.DateTime()
'''
获取用户的sqlalchemy对象
分页器
'''
def get_user_data(page, limit, filters):
user = User.query.filter(and_(*[getattr(User, k).like(v) for k, v in filters.items()])).paginate(page=page,
per_page=limit,
error_out=False)
count = User.query.count()
return user, count
'''
获取用户的dict数据
分页器
'''
def get_user_data_dict(page, limit, filters):
user, count = get_user_data(page, limit, filters)
user_schema = UserSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
output = user_schema.dump(user.items) # 生成可序列化对象
return output, count
'''
通过名称获取用户
'''
def get_user_by_name(username):
return User.query.filter_by(username=username).first()
# 判断用户是否存在
def is_user_exists(username):
res = User.query.filter_by(username=username).count()
return bool(res)
# 增加用户
def add_user(username, realName, password):
user = User(username=username, realname=realName)
user.set_password(password)
db.session.add(user)
db.session.commit()
return user.id
# 增加用户角色
def add_user_role(id, roles_list):
user = User.query.filter_by(id=id).first()
roles = Role.query.filter(Role.id.in_(roles_list)).all()
for r in roles:
user.role.append(r)
db.session.commit()
# 更新用户信息
def update_user(id, username, realname):
user = User.query.filter_by(id=id).update({'username': username, 'realname': realname})
db.session.commit()
return user
def delete_by_id(id):
user = User.query.filter_by(id=id).first()
roles_id = []
for role in user.role:
roles_id.append(role.id)
roles = Role.query.filter(Role.id.in_(roles_id)).all()
for r in roles:
user.role.remove(r)
res = User.query.filter_by(id=id).delete()
db.session.commit()
return res
# 启动用户
def enable_status(id):
enable = 1
user = User.query.filter_by(id=id).update({"enable": enable})
if user:
db.session.commit()
return True
return False
# 停用用户
def disable_status(id):
enable = 0
user = User.query.filter_by(id=id).update({"enable": enable})
if user:
db.session.commit()
return True
return False
# 批量删除
def batch_remove(ids):
for id in ids:
delete_by_id(id)
def update_user_role(id, roles_list):
user = User.query.filter_by(id=id).first()
roles_id = []
for role in user.role:
roles_id.append(role.id)
roles = Role.query.filter(Role.id.in_(roles_id)).all()
for r in roles:
user.role.remove(r)
roles = Role.query.filter(Role.id.in_(roles_list)).all()
for r in roles:
user.role.append(r)
db.session.commit()
-40
View File
@@ -1,40 +0,0 @@
from functools import wraps
from flask import abort, request, jsonify, session
from flask_login import login_required
from applications.service.admin_log import admin_log
def authorize(power: str):
def decorator(func):
@login_required
@wraps(func)
def wrapper(*args, **kwargs):
if not power in session.get('permissions'):
if request.method == 'GET':
abort(403)
else:
return jsonify(success=False, msg="权限不足!")
return func(*args, **kwargs)
return wrapper
return decorator
def authorize_and_log(power: str):
def decorator(func):
@login_required
@wraps(func)
def wrapper(*args, **kwargs):
if not power in session['permissions']:
admin_log(request=request, is_access=False)
if request.method == 'GET':
abort(403)
else:
return jsonify(success=False, msg="权限不足!")
admin_log(request=request, is_access=True)
return func(*args, **kwargs)
return wrapper
return decorator
-4
View File
@@ -1,4 +0,0 @@
from flask_uploads import UploadSet, IMAGES
photos = UploadSet('photos', IMAGES)
+13
View File
@@ -0,0 +1,13 @@
from applications.view.admin import register_admin_views
from applications.view.index import register_index_views
from applications.view.passport import register_passport_views
from applications.view.rights import register_rights_view
from applications.view.department import register_dept_views
def init_view(app):
register_admin_views(app)
register_index_views(app)
register_rights_view(app)
register_passport_views(app)
register_dept_views(app)
+23
View File
@@ -0,0 +1,23 @@
from flask import Flask
from applications.view.admin.admin_log import admin_log
from applications.view.admin.dict import admin_dict
from applications.view.admin.index import admin_bp
from applications.view.admin.file import admin_file
from applications.view.admin.power import admin_power
from applications.view.admin.role import admin_role
from applications.view.admin.user import admin_user
from applications.view.admin.monitor import admin_monitor_bp
from applications.view.admin.task import admin_task
def register_admin_views(app: Flask):
app.register_blueprint(admin_bp)
app.register_blueprint(admin_user)
app.register_blueprint(admin_file)
app.register_blueprint(admin_monitor_bp)
app.register_blueprint(admin_log)
app.register_blueprint(admin_power)
app.register_blueprint(admin_role)
app.register_blueprint(admin_dict)
app.register_blueprint(admin_task)
+40
View File
@@ -0,0 +1,40 @@
from flask import Blueprint, request, render_template
from sqlalchemy import desc
from applications.common.utils.http import table_api
from applications.common.utils.rights import authorize
from applications.models import AdminLog
from applications.schemas import LogOutSchema
from applications.common.curd import model_to_dicts
admin_log = Blueprint('adminLog', __name__, url_prefix='/admin/log')
# 日志管理
@admin_log.get('/')
@authorize("admin:log:main")
def index():
return render_template('admin/admin_log/main.html')
# 登录日志
@admin_log.get('/loginLog')
@authorize("admin:log:main")
def login_log():
# orm查询
# 使用分页获取data需要.items
log = AdminLog.query.filter_by(url='/passport/login').order_by(desc(AdminLog.create_time)).layui_paginate()
count = log.total
return table_api(data= model_to_dicts(schema=LogOutSchema, data=log.items), count=count)
# 操作日志
@admin_log.get('/operateLog')
@authorize("admin:log:main")
def operate_log():
# orm查询
# 使用分页获取data需要.items
log = AdminLog.query.filter(
AdminLog.url != '/passport/login').order_by(
desc(AdminLog.create_time)).layui_paginate()
count = log.total
return table_api(data=model_to_dicts(schema=LogOutSchema, data=log.items), count=count)
+221
View File
@@ -0,0 +1,221 @@
from flask import Blueprint, render_template, request, jsonify
from applications.common import curd
from applications.common.helper import ModelFilter
from applications.common.utils.http import table_api, success_api, fail_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import DictType, DictData
from applications.schemas import DictTypeOutSchema, DictDataOutSchema
admin_dict = Blueprint('adminDict', __name__, url_prefix='/admin/dict')
# 数据字典
@admin_dict.get('/')
@authorize("admin:dict:main", log=True)
def main():
return render_template('admin/dict/main.html')
@admin_dict.get('/dictType/data')
@authorize("admin:dict:main", log=True)
def dict_type_data():
# 获取请求参数
type_name = xss_escape(request.args.get('typeName', type=str))
# 查询参数构造
mf = ModelFilter()
if type_name:
mf.vague(field_name="type_name", value=type_name)
# orm查询
# 使用分页获取data需要.items
dict_all = DictType.query.filter(mf.get_filter(DictType)).layui_paginate()
count = dict_all.total
data = curd.model_to_dicts(schema=DictTypeOutSchema, data=dict_all.items)
return table_api(data=data, count=count)
@admin_dict.get('/dictType/add')
@authorize("admin:dict:add", log=True)
def dict_type_add():
return render_template('admin/dict/add.html')
@admin_dict.post('/dictType/save')
@authorize("admin:dict:add", log=True)
def dict_type_save():
req_json = request.json
description = xss_escape(req_json.get("description"))
enable = xss_escape(req_json.get("enable"))
type_code = xss_escape(req_json.get("typeCode"))
type_name = xss_escape(req_json.get("typeName"))
d = DictType(type_name=type_name, type_code=type_code, enable=enable, description=description)
db.session.add(d)
db.session.commit()
if d.id is None:
return fail_api(msg="增加失败")
return success_api(msg="增加成功")
# 编辑字典类型
@admin_dict.get('/dictType/edit')
@authorize("admin:dict:edit", log=True)
def dict_type_edit():
_id = request.args.get('dictTypeId', type=int)
dict_type = DictType.query.filter_by(id=_id).first()
return render_template('admin/dict/edit.html', dict_type=dict_type)
# 编辑字典类型
@admin_dict.put('/dictType/update')
@authorize("admin:dict:edit", log=True)
def dict_type_update():
req_json = request.json
id = xss_escape(req_json.get("id"))
description = xss_escape(req_json.get("description"))
enable = xss_escape(req_json.get("enable"))
type_code = xss_escape(req_json.get("typeCode"))
type_name = xss_escape(req_json.get("typeName"))
DictType.query.filter_by(id=id).update({
"description": description,
"enable": enable,
"type_code": type_code,
"type_name": type_name
})
db.session.commit()
return success_api(msg="更新成功")
# 启用字典
@admin_dict.put('/dictType/enable')
@authorize("admin:dict:edit", log=True)
def dict_type_enable():
_id = request.json.get('id')
if id:
res = curd.enable_status(DictType,_id)
if not res:
return fail_api(msg="出错啦")
return success_api("启动成功")
return fail_api(msg="数据错误")
# 禁用字典
@admin_dict.put('/dictType/disable')
@authorize("admin:dict:edit", log=True)
def dict_type_dis_enable():
_id = request.json.get('id')
if id:
res = curd.disable_status(DictType,_id)
if not res:
return fail_api(msg="出错啦")
return success_api("禁用成功")
return fail_api(msg="数据错误")
# 删除字典类型
@admin_dict.delete('/dictType/remove/<int:_id>')
@authorize("admin:dict:remove", log=True)
def dict_type_delete(_id):
res = curd.delete_one_by_id(DictType,_id)
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
@admin_dict.get('/dictData/data')
@authorize("admin:dict:main", log=True)
def dict_code_data():
type_code = xss_escape(request.args.get('typeCode', type=str))
dict_data = DictData.query.filter_by(type_code=type_code).layui_paginate()
count = dict_data.total
data = curd.model_to_dicts(schema=DictDataOutSchema, data=dict_data.items)
return table_api(data=data, count=count)
# 增加字典数据
@admin_dict.get('/dictData/add')
@authorize("admin:dict:add", log=True)
def dict_data_add():
type_code = request.args.get('typeCode', type=str)
return render_template('admin/dict/data/add.html', type_code=type_code)
# 增加字典数据
@admin_dict.post('/dictData/save')
@authorize("admin:dict:add", log=True)
def dict_data_save():
req_json = request.json
data_label = xss_escape(req_json.get("dataLabel"))
data_value = xss_escape(req_json.get("dataValue"))
enable = xss_escape(req_json.get("enable"))
remark = xss_escape(req_json.get("remark"))
type_code = xss_escape(req_json.get("typeCode"))
d = DictData(data_label=data_label, data_value=data_value, enable=enable, remark=remark, type_code=type_code)
db.session.add(d)
db.session.commit()
if not d.id:
return jsonify(success=False, msg="增加失败")
return jsonify(success=True, msg="增加成功")
# 编辑字典数据
@admin_dict.get('/dictData/edit')
@authorize("admin:dict:edit", log=True)
def dict_data_edit():
_id = request.args.get('dataId', type=str)
dict_data = curd.get_one_by_id(DictData, _id)
return render_template('admin/dict/data/edit.html', dict_data=dict_data)
# 编辑字典数据
@admin_dict.put('/dictData/update')
@authorize("admin:dict:edit", log=True)
def dict_data_update():
req_json = request.json
id = req_json.get("dataId")
DictData.query.filter_by(id=id).update({
"data_label": xss_escape(req_json.get("dataLabel")),
"data_value": xss_escape(req_json.get("dataValue")),
"enable": xss_escape(req_json.get("enable")),
"remark": xss_escape(req_json.get("remark")),
"type_code": xss_escape(req_json.get("typeCode"))
})
db.session.commit()
return success_api(msg="更新成功")
# 启用字典数据
@admin_dict.put('/dictData/enable')
@authorize("admin:dict:edit", log=True)
def dict_data_enable():
_id = request.json.get('dataId')
if _id:
res = curd.enable_status(model=DictData, id=_id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="启动成功")
return fail_api(msg="数据错误")
# 禁用字典数据
@admin_dict.put('/dictData/disable')
@authorize("admin:dict:edit", log=True)
def dict_data_disenable():
_id = request.json.get('dataId')
if _id:
res = curd.disable_status(model=DictData, id=_id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="禁用成功")
return fail_api(msg="数据错误")
# 删除字典类型
@admin_dict.delete('dictData/remove/<int:id>')
@authorize("admin:dict:remove", log=True)
def dict_data_delete(id):
res = curd.delete_one_by_id(model=DictData, id=id)
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
@@ -1,74 +1,75 @@
import os import os
from flask import Blueprint, request, render_template, jsonify, current_app from flask import Blueprint, request, render_template, jsonify, current_app
from flask_login import login_required
from applications.models import db from applications.common.utils.http import fail_api, success_api, table_api
from applications.models.admin import Photo from applications.common.utils.rights import authorize
from applications.service.admin.file import get_photo, upload_one, delete_photo_by_id from applications.extensions import db
from applications.service.route_auth import authorize_and_log from applications.models import Photo
from applications.common.utils import upload as upload_curd
admin_file = Blueprint('adminFile', __name__, url_prefix='/admin/file') admin_file = Blueprint('adminFile', __name__, url_prefix='/admin/file')
# 图片管理 # 图片管理
@admin_file.route('/') @admin_file.get('/')
@authorize_and_log("admin:file:main") @authorize("admin:file:main", log=True)
def index(): def index():
return render_template('admin/photo/photo.html') return render_template('admin/photo/photo.html')
# 图片数据 # 图片数据
@admin_file.route('/table') @admin_file.get('/table')
@authorize_and_log("admin:file:main") @authorize("admin:file:main", log=True)
def table(): def table():
page = request.args.get('page', type=int) page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int) limit = request.args.get('limit', type=int)
data, count = get_photo(page=page, limit=limit) data, count = upload_curd.get_photo(page=page, limit=limit)
res = { return table_api(data=data, count=count)
'msg': "",
'code': 0,
'data': data,
'count': count,
'limit': "10"
}
return jsonify(res) # 上传
@admin_file.get('/upload')
@authorize("admin:file:add", log=True)
def upload():
return render_template('admin/photo/photo_add.html')
# 上传接口 # 上传接口
@admin_file.route('/upload', methods=['GET', 'POST']) @admin_file.post('/upload')
@authorize_and_log("admin:file:add") @authorize("admin:file:add", log=True)
def upload(): def upload_api():
if request.method == 'POST' and 'file' in request.files: if 'file' in request.files:
photo = request.files['file'] photo = request.files['file']
mime = request.files['file'].content_type mime = request.files['file'].content_type
file_url = upload_one(photo=photo, mime=mime)
file_url = upload_curd.upload_one(photo=photo, mime=mime)
res = { res = {
"msg": "上传成功", "msg": "上传成功",
"code": 0, "code": 0,
"success": True,
"data": "data":
{"src": file_url} {"src": file_url}
} }
return jsonify(res) return jsonify(res)
return fail_api()
return render_template('admin/photo/photo_add.html')
# 图片删除 # 图片删除
@admin_file.route('/delete', methods=['GET', 'POST']) @admin_file.route('/delete', methods=['GET', 'POST'])
@authorize_and_log("admin:file:delete") @authorize("admin:file:delete", log=True)
def delete(): def delete():
id = request.form.get('id') _id = request.form.get('id')
res = delete_photo_by_id(id) res = upload_curd.delete_photo_by_id(_id)
if res: if res:
return jsonify(msg="删除成功", code=200) return success_api(msg="删除成功")
else: else:
return jsonify(msg="删除失败", code=999) return fail_api(msg="删除失败")
# 图片批量删除 # 图片批量删除
@admin_file.route('/batchRemove', methods=['GET', 'POST']) @admin_file.route('/batchRemove', methods=['GET', 'POST'])
@authorize_and_log("admin:file:delete") @authorize("admin:file:delete", log=True)
def batchRemove(): def batch_remove():
ids = request.form.getlist('ids[]') ids = request.form.getlist('ids[]')
photo_name = Photo.query.filter(Photo.id.in_(ids)).all() photo_name = Photo.query.filter(Photo.id.in_(ids)).all()
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST") upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
@@ -77,7 +78,6 @@ def batchRemove():
photo = Photo.query.filter(Photo.id.in_(ids)).delete(synchronize_session=False) photo = Photo.query.filter(Photo.id.in_(ids)).delete(synchronize_session=False)
db.session.commit() db.session.commit()
if photo: if photo:
return jsonify(msg="删除成功", code=200) return success_api(msg="删除成功")
else: else:
res = {"msg": "删除失败", "code": 999} return fail_api(msg="删除失败")
return res
+19
View File
@@ -0,0 +1,19 @@
from flask import Blueprint, render_template
from flask_login import login_required, current_user
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
# 首页
@admin_bp.get('/')
@login_required
def index():
user = current_user
return render_template('admin/index.html', user=user)
# 控制台页面
@admin_bp.get('/welcome')
@login_required
def welcome():
return render_template('admin/console/console.html')
@@ -2,20 +2,17 @@ import os
import platform import platform
import re import re
from datetime import datetime from datetime import datetime
import time
import psutil import psutil
from flask import Blueprint, render_template from flask import Blueprint, render_template, jsonify
from flask_marshmallow import Marshmallow from applications.common.utils.rights import authorize
from applications.service.route_auth import authorize_and_log admin_monitor_bp = Blueprint('adminMonitor', __name__, url_prefix='/admin/monitor')
ma = Marshmallow()
admin_Monitor = Blueprint('adminMonitor', __name__, url_prefix='/admin/monitor')
# 系统监控 # 系统监控
@admin_Monitor.route('/') @admin_monitor_bp.get('/')
@authorize_and_log("admin:monitor:main") @authorize("admin:monitor:main", log=True)
def main(): def main():
# 主机名称 # 主机名称
hostname = platform.node() hostname = platform.node()
@@ -58,6 +55,8 @@ def main():
up_time_list = re.split(r':', str(up_time)) up_time_list = re.split(r':', str(up_time))
up_time_format = " {} 小时{} 分钟{}".format(up_time_list[0], up_time_list[1], up_time_list[2]) up_time_format = " {} 小时{} 分钟{}".format(up_time_list[0], up_time_list[1], up_time_list[2])
# 当前时间
time_now = time.strftime('%H:%M:%S ', time.localtime(time.time()))
return render_template('admin/monitor.html', return render_template('admin/monitor.html',
hostname=hostname, hostname=hostname,
system_version=system_version, system_version=system_version,
@@ -70,5 +69,20 @@ def main():
memory_free=memory_free, memory_free=memory_free,
boot_time=boot_time, boot_time=boot_time,
up_time_format=up_time_format, up_time_format=up_time_format,
disk_partitions_list=disk_partitions_list disk_partitions_list=disk_partitions_list,
time_now=time_now
) )
# 图表 api
@admin_monitor_bp.get('/polling')
@authorize("admin:monitor:main")
def ajax_polling():
# 获取cup使用率
cpus_percent = psutil.cpu_percent(interval=0.1)
# 获取内存使用率
memory_information = psutil.virtual_memory()
memory_usage = memory_information.percent
time_now = time.strftime('%H:%M:%S ', time.localtime(time.time()))
return jsonify(cups_percent=cpus_percent, memory_used=memory_usage, time_now=time_now)
+167
View File
@@ -0,0 +1,167 @@
from flask import Blueprint, render_template, request, jsonify
from applications.common import curd
from applications.common.utils.http import success_api, fail_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import Power
from applications.schemas import PowerOutSchema2
admin_power = Blueprint('adminPower', __name__, url_prefix='/admin/power')
@admin_power.get('/')
@authorize("admin:power:main", log=True)
def index():
return render_template('admin/power/main.html')
@admin_power.post('/data')
@authorize("admin:power:main", log=True)
def data():
power = Power.query.all()
res = {
"data": curd.model_to_dicts(schema=PowerOutSchema2, data=power)
}
return jsonify(res)
@admin_power.get('/add')
@authorize("admin:power:add", log=True)
def add():
return render_template('admin/power/add.html')
@admin_power.get('/selectParent')
@authorize("admin:power:main", log=True)
def select_parent():
power = Power.query.all()
res = curd.model_to_dicts(schema=PowerOutSchema2, data=power)
res.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
res = {
"status": {"code": 200, "message": "默认"},
"data": res
}
return jsonify(res)
# 增加
@admin_power.post('/save')
@authorize("admin:power:add", log=True)
def save():
req = request.json
icon = xss_escape(req.get("icon"))
openType = xss_escape(req.get("openType"))
parentId = xss_escape(req.get("parentId"))
powerCode = xss_escape(req.get("powerCode"))
powerName = xss_escape(req.get("powerName"))
powerType = xss_escape(req.get("powerType"))
powerUrl = xss_escape(req.get("powerUrl"))
sort = xss_escape(req.get("sort"))
power = Power(
icon=icon,
open_type=openType,
parent_id=parentId,
code=powerCode,
name=powerName,
type=powerType,
url=powerUrl,
sort=sort,
enable=1
)
r = db.session.add(power)
db.session.commit()
return success_api(msg="成功")
# 权限编辑
@admin_power.get('/edit/<int:_id>')
@authorize("admin:power:edit", log=True)
def edit(_id):
power = curd.get_one_by_id(Power, _id)
icon = str(power.icon).split()
if len(icon) == 2:
icon = icon[1]
else:
icon = None
return render_template('admin/power/edit.html', power=power, icon=icon)
# 权限更新
@admin_power.put('/update')
@authorize("admin:power:edit", log=True)
def update():
req_json = request.json
id = request.json.get("powerId")
data = {
"icon": xss_escape(req_json.get("icon")),
"open_type": xss_escape(req_json.get("openType")),
"parent_id": xss_escape(req_json.get("parentId")),
"code": xss_escape(req_json.get("powerCode")),
"name": xss_escape(req_json.get("powerName")),
"type": xss_escape(req_json.get("powerType")),
"url": xss_escape(req_json.get("powerUrl")),
"sort": xss_escape(req_json.get("sort"))
}
res = Power.query.filter_by(id=id).update(data)
db.session.commit()
if not res:
return fail_api(msg="更新权限失败")
return success_api(msg="更新权限成功")
# 启用权限
@admin_power.put('/enable')
@authorize("admin:power:edit", log=True)
def enable():
_id = request.json.get('powerId')
if id:
res = curd.enable_status(Power, _id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="启用成功")
return fail_api(msg="数据错误")
# 禁用权限
@admin_power.put('/disable')
@authorize("admin:power:edit", log=True)
def dis_enable():
_id = request.json.get('powerId')
if id:
res = curd.disable_status(Power, _id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="禁用成功")
return fail_api(msg="数据错误")
# 权限删除
@admin_power.delete('/remove/<int:id>')
@authorize("admin:power:remove", log=True)
def remove(id):
power = Power.query.filter_by(id=id).first()
power.role = []
r = Power.query.filter_by(id=id).delete()
db.session.commit()
if r:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
# 批量删除
@admin_power.delete('/batchRemove')
@authorize("admin:power:remove", log=True)
def batch_remove():
ids = request.form.getlist('ids[]')
for id in ids:
power = Power.query.filter_by(id=id).first()
power.role = []
r = Power.query.filter_by(id=id).delete()
db.session.commit()
return success_api(msg="批量删除成功")
+204
View File
@@ -0,0 +1,204 @@
from flask import Blueprint, render_template, request, jsonify
from flask_login import login_required
from applications.common.curd import model_to_dicts, enable_status, disable_status, get_one_by_id
from applications.common.helper import ModelFilter
from applications.common.utils.http import table_api, success_api, fail_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import Role, Power, User
from applications.schemas import RoleOutSchema, PowerOutSchema2
admin_role = Blueprint('adminRole', __name__, url_prefix='/admin/role')
# 用户管理
@admin_role.get('/')
@authorize("admin:role:main", log=True)
def main():
return render_template('admin/role/main.html')
# 表格数据
@admin_role.get('/data')
@authorize("admin:role:main", log=True)
def table():
# 获取请求参数
role_name = xss_escape(request.args.get('roleName', type=str))
role_code = xss_escape(request.args.get('roleCode', type=str))
# 查询参数构造
mf = ModelFilter()
if role_name:
mf.vague(field_name="name", value=role_name)
if role_code:
mf.vague(field_name="code", value=role_code)
# orm查询
# 使用分页获取data需要.items
role = Role.query.filter(mf.get_filter(Role)).layui_paginate()
count = role.total
# 返回api
return table_api(data=model_to_dicts(schema=RoleOutSchema, data=role.items), count=count)
# 角色增加
@admin_role.get('/add')
@authorize("admin:role:add", log=True)
def add():
return render_template('admin/role/add.html')
# 角色增加
@admin_role.post('/save')
@authorize("admin:role:add", log=True)
def save():
req = request.json
details = xss_escape(req.get("details"))
enable = xss_escape(req.get("enable"))
roleCode = xss_escape(req.get("roleCode"))
roleName = xss_escape(req.get("roleName"))
sort = xss_escape(req.get("sort"))
role = Role(
details=details,
enable=enable,
code=roleCode,
name=roleName,
sort=sort
)
db.session.add(role)
db.session.commit()
return success_api(msg="成功")
# 角色授权
@admin_role.get('/power/<int:_id>')
@authorize("admin:role:power", log=True)
def power(_id):
return render_template('admin/role/power.html', id=_id)
# 获取角色权限
@admin_role.get('/getRolePower/<int:id>')
@authorize("admin:role:main", log=True)
def get_role_power(id):
role = Role.query.filter_by(id=id).first()
check_powers = role.power
check_powers_list = []
for cp in check_powers:
check_powers_list.append(cp.id)
powers = Power.query.all()
power_schema = PowerOutSchema2(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
output = power_schema.dump(powers) # 生成可序列化对象
for i in output:
if int(i.get("powerId")) in check_powers_list:
i["checkArr"] = "1"
else:
i["checkArr"] = "0"
res = {
"data": output,
"status": {"code": 200, "message": "默认"}
}
return jsonify(res)
# 保存角色权限
@admin_role.put('/saveRolePower')
@authorize("admin:role:edit", log=True)
def save_role_power():
req_form = request.form
power_ids = req_form.get("powerIds")
power_list = power_ids.split(',')
role_id = req_form.get("roleId")
role = Role.query.filter_by(id=role_id).first()
powers = Power.query.filter(Power.id.in_(power_list)).all()
role.power = powers
db.session.commit()
return success_api(msg="授权成功")
# 角色编辑
@admin_role.get('/edit/<int:id>')
@authorize("admin:role:edit", log=True)
def edit(id):
r = get_one_by_id(model=Role, id=id)
return render_template('admin/role/edit.html', role=r)
# 更新角色
@admin_role.put('/update')
@authorize("admin:role:edit", log=True)
def update():
req_json = request.json
id = req_json.get("roleId")
data = {
"code": xss_escape(req_json.get("roleCode")),
"name": xss_escape(req_json.get("roleName")),
"sort": xss_escape(req_json.get("sort")),
"enable": xss_escape(req_json.get("enable")),
"details": xss_escape(req_json.get("details"))
}
role = Role.query.filter_by(id=id).update(data)
db.session.commit()
if not role:
return fail_api(msg="更新角色失败")
return success_api(msg="更新角色成功")
# 启用用户
@admin_role.put('/enable')
@authorize("admin:role:edit", log=True)
def enable():
id = request.json.get('roleId')
if id:
res = enable_status(Role, id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="启动成功")
return fail_api(msg="数据错误")
# 禁用用户
@admin_role.put('/disable')
@authorize("admin:role:edit", log=True)
def dis_enable():
_id = request.json.get('roleId')
if _id:
res = disable_status(Role, _id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="禁用成功")
return fail_api(msg="数据错误")
# 角色删除
@admin_role.delete('/remove/<int:id>')
@authorize("admin:role:remove", log=True)
def remove(id):
role = Role.query.filter_by(id=id).first()
# 删除该角色的权限和用户
role.power = []
role.user = []
r = Role.query.filter_by(id=id).delete()
db.session.commit()
if not r:
return fail_api(msg="角色删除失败")
return success_api(msg="角色删除成功")
# 批量删除
@admin_role.delete('/batchRemove')
@authorize("admin:role:remove", log=True)
@login_required
def batch_remove():
ids = request.form.getlist('ids[]')
for id in ids:
role = Role.query.filter_by(id=id).first()
# 删除该角色的权限和用户
role.power = []
role.user = []
r = Role.query.filter_by(id=id).delete()
db.session.commit()
return success_api(msg="批量删除成功")
+112
View File
@@ -0,0 +1,112 @@
from flask import Blueprint, request, render_template
from flask_apscheduler.utils import job_to_dict
from applications.common.tasks import tasks
from applications.common.tasks.tasks import task_list
from applications.common.utils.http import table_api, fail_api, success_api
from applications.extensions.init_apscheduler import scheduler
admin_task = Blueprint('adminTask', __name__, url_prefix='/admin/task')
@admin_task.route('/add_job', methods=['GET'])
def add_task():
scheduler.add_job(func=tasks.get(), id='4', args=(1, 1), trigger='interval', seconds=3,
replace_existing=True)
return '6'
@admin_task.get('/')
def main():
return render_template('admin/task/main.html')
@admin_task.route('/data', methods=['GET'])
def get_task(): # 获取
jobs = scheduler.get_jobs()
jobs_list = []
for job in jobs:
jobs_list.append(job_to_dict(job))
return table_api(data=jobs_list, count=len(jobs_list))
# 增加
@admin_task.get('/add')
def add():
return render_template('admin/task/add.html', task_list=task_list)
@admin_task.post('/save')
def save():
_id = request.json.get("id")
name = request.json.get("id")
type = request.json.get("type")
functions = request.json.get("functions")
datetime = request.json.get("datetime")
time = request.json.get("time")
if not hasattr(tasks, functions):
return fail_api()
if type == 'date':
scheduler.add_job(
func=getattr(tasks, functions),
id=_id,
name=name,
args=(1, 1),
trigger=type,
run_date=datetime,
replace_existing=True)
elif type == 'interval':
scheduler.add_job(
func=getattr(tasks, functions),
id=_id,
name=name,
args=(1, 1),
trigger=type,
replace_existing=True)
elif type == 'cron':
scheduler.add_job(
func=getattr(tasks, functions),
id=_id,
name=name,
args=(1, 1),
trigger=type,
replace_existing=True)
return success_api()
# 恢复
@admin_task.put('/enable')
def enable():
_id = request.json.get('id')
# print(id)
if _id:
scheduler.resume_job(str(_id))
return success_api(msg="启动成功")
return fail_api(msg="数据错误")
# 暂停
@admin_task.put('/disable')
def dis_enable():
_id = request.json.get('id')
if _id:
scheduler.pause_job(str(_id))
return success_api(msg="暂停成功")
return fail_api(msg="数据错误")
@admin_task.delete('/remove/<int:_id>')
def remove_job(_id): # 移除
scheduler.remove_job(str(_id))
return success_api(msg="删除成功")
# scheduler.add_job(func=task1, id='2', args=(1, 1), trigger='cron', day_of_week='0-6', hour=18, minute=24,
# second=10, replace_existing=True)
# scheduler.add_job(func=task4, id='4', args=(2, 2), trigger='interval', seconds=3,
# replace_existing=True, misfire_grace_time=3)
# # trigger='interval' 表示是一个循环任务,每隔多久执行一次
# scheduler.add_job(func=task2, id='3', args=(2, 2), trigger='interval', seconds=3,
# replace_existing=True)
+235
View File
@@ -0,0 +1,235 @@
from flask import Blueprint, render_template, request
from flask_login import login_required, current_user
from sqlalchemy import desc
from applications.common import curd
from applications.common.curd import model_to_dicts, enable_status, disable_status
from applications.common.helper import ModelFilter
from applications.common.utils.http import table_api, fail_api, success_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import Role
from applications.models import User, AdminLog
from applications.schemas import UserOutSchema
admin_user = Blueprint('adminUser', __name__, url_prefix='/admin/user')
# 用户管理
@admin_user.get('/')
@authorize("admin:user:main", log=True)
def main():
return render_template('admin/user/main.html')
# 用户分页查询
@admin_user.get('/data')
@authorize("admin:user:main", log=True)
def data():
# 获取请求参数
real_name = xss_escape(request.args.get('realName', type=str))
username = xss_escape(request.args.get('username', type=str))
dept_id = request.args.get('deptId', type=int)
# 查询参数构造
mf = ModelFilter()
if real_name:
mf.contains(field_name="realname", value=real_name)
if username:
mf.contains(field_name="username", value=username)
if dept_id:
mf.exact(field_name="dept_id", value=dept_id)
# orm查询
# 使用分页获取data需要.items
user = User.query.filter(mf.get_filter(model=User)).layui_paginate()
count = user.total
# 返回api
return table_api(data=model_to_dicts(schema=UserOutSchema, data=user.items), count=count)
# 用户增加
@admin_user.get('/add')
@authorize("admin:user:add", log=True)
def add():
roles = Role.query.all()
return render_template('admin/user/add.html', roles=roles)
@admin_user.post('/save')
@authorize("admin:user:add", log=True)
def save():
req_json = request.json
a = req_json.get("roleIds")
username = xss_escape(req_json.get('username'))
real_name = xss_escape(req_json.get('realName'))
password = xss_escape(req_json.get('password'))
role_ids = a.split(',')
if not username or not real_name or not password:
return fail_api(msg="账号姓名密码不得为空")
if bool(User.query.filter_by(username=username).count()):
return fail_api(msg="用户已经存在")
user = User(username=username, realname=real_name)
user.set_password(password)
db.session.add(user)
roles = Role.query.filter(Role.id.in_(role_ids)).all()
for r in roles:
user.role.append(r)
db.session.commit()
return success_api(msg="增加成功")
# 删除用户
@admin_user.delete('/remove/<int:id>')
@authorize("admin:user:remove", log=True)
def delete(id):
user = User.query.filter_by(id=id).first()
user.role = []
res = User.query.filter_by(id=id).delete()
db.session.commit()
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
# 编辑用户
@admin_user.get('/edit/<int:id>')
@authorize("admin:user:edit", log=True)
def edit(id):
user = curd.get_one_by_id(User,id)
roles = Role.query.all()
checked_roles = []
for r in user.role:
checked_roles.append(r.id)
return render_template('admin/user/edit.html', user=user, roles=roles, checked_roles=checked_roles)
# 编辑用户
@admin_user.put('/update')
@authorize("admin:user:edit", log=True)
def update():
req_json = request.json
a = xss_escape(req_json.get("roleIds"))
id = xss_escape(req_json.get("userId"))
username = xss_escape(req_json.get('username'))
real_name = xss_escape(req_json.get('realName'))
dept_id = xss_escape(req_json.get('deptId'))
role_ids = a.split(',')
User.query.filter_by(id=id).update({'username': username, 'realname': real_name, 'dept_id': dept_id})
u = User.query.filter_by(id=id).first()
roles = Role.query.filter(Role.id.in_(role_ids)).all()
u.role = roles
db.session.commit()
return success_api(msg="更新成功")
# 个人中心
@admin_user.get('/center')
@login_required
def center():
user_info = current_user
user_logs = AdminLog.query.filter_by(url='/passport/login').filter_by(uid=current_user.id).order_by(
desc(AdminLog.create_time)).limit(10)
return render_template('admin/user/center.html', user_info=user_info, user_logs=user_logs)
# 修改头像
@admin_user.get('/profile')
@login_required
def profile():
return render_template('admin/user/profile.html')
# 修改头像
@admin_user.put('/updateAvatar')
@login_required
def update_avatar():
url = request.json.get("avatar").get("src")
r = User.query.filter_by(id=current_user.id).update({"avatar": url})
db.session.commit()
if not r:
return fail_api(msg="出错啦")
return success_api(msg="修改成功")
# 修改当前用户信息
@admin_user.put('/updateInfo')
@login_required
def update_info():
req_json = request.json
r = User.query.filter_by(id=current_user.id).update(
{"realname": req_json.get("realName"), "remark": req_json.get("details")})
db.session.commit()
if not r:
return fail_api(msg="出错啦")
return success_api(msg="更新成功")
# 修改当前用户密码
@admin_user.get('/editPassword')
@login_required
def edit_password():
return render_template('admin/user/edit_password.html')
# 修改当前用户密码
@admin_user.put('/editPassword')
@login_required
def edit_password_put():
res_json = request.json
if res_json.get("newPassword") == '':
return fail_api("新密码不得为空")
if res_json.get("newPassword") != res_json.get("confirmPassword"):
return fail_api("俩次密码不一样")
user = current_user
is_right = user.validate_password(res_json.get("oldPassword"))
if not is_right:
return fail_api("旧密码错误")
user.set_password(res_json.get("newPassword"))
db.session.add(user)
db.session.commit()
return success_api("更改成功")
# 启用用户
@admin_user.put('/enable')
@authorize("admin:user:edit", log=True)
def enable():
_id = request.json.get('userId')
if _id:
res = enable_status(model=User, id=_id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="启动成功")
return fail_api(msg="数据错误")
# 禁用用户
@admin_user.put('/disable')
@authorize("admin:user:edit", log=True)
def dis_enable():
_id = request.json.get('userId')
if _id:
res = disable_status(model=User,id=_id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="禁用成功")
return fail_api(msg="数据错误")
# 批量删除
@admin_user.delete('/batchRemove')
@authorize("admin:user:remove", log=True)
def batch_remove():
ids = request.form.getlist('ids[]')
for id in ids:
user = User.query.filter_by(id=id).first()
user.role = []
res = User.query.filter_by(id=id).delete()
db.session.commit()
return success_api(msg="批量删除成功")
+144
View File
@@ -0,0 +1,144 @@
from flask import Blueprint, render_template, request, jsonify
from applications.common import curd
from applications.common.utils import validate
from applications.common.utils.http import success_api, fail_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import Dept, User
from applications.schemas import DeptOutSchema
dept_bp = Blueprint('dept', __name__, url_prefix='/dept')
def register_dept_views(app):
app.register_blueprint(dept_bp)
@dept_bp.get('/')
@authorize("admin:dept:main", log=True)
def main():
return render_template('admin/dept/main.html')
@dept_bp.post('/data')
@authorize("admin:dept:main", log=True)
def data():
dept = Dept.query.order_by(Dept.sort).all()
power_data = curd.model_to_dicts(schema=DeptOutSchema, data=dept)
res = {
"data": power_data
}
return jsonify(res)
@dept_bp.get('/add')
@authorize("admin:dept:add", log=True)
def add():
return render_template('admin/dept/add.html')
@dept_bp.get('/tree')
@authorize("admin:dept:main", log=True)
def tree():
dept = Dept.query.order_by(Dept.sort).all()
power_data = curd.model_to_dicts(schema=DeptOutSchema, data=dept)
res = {
"status": {"code": 200, "message": "默认"},
"data": power_data
}
return jsonify(res)
@dept_bp.post('/save')
@authorize("admin:dept:add", log=True)
def save():
req_json = request.json
dept = Dept(
parent_id=req_json.get('parentId'),
dept_name=xss_escape(req_json.get('deptName')),
sort=xss_escape(req_json.get('sort')),
leader=xss_escape(req_json.get('leader')),
phone=xss_escape(req_json.get('phone')),
email=xss_escape(req_json.get('email')),
status=xss_escape(req_json.get('status')),
address=xss_escape(req_json.get('address'))
)
r = db.session.add(dept)
db.session.commit()
return success_api(msg="成功")
@dept_bp.get('/edit')
@authorize("admin:dept:edit", log=True)
def edit():
_id = request.args.get("deptId")
dept = curd.get_one_by_id(model=Dept, id=_id)
return render_template('admin/dept/edit.html', dept=dept)
# 启用
@dept_bp.put('/enable')
@authorize("admin:dept:edit", log=True)
def enable():
id = request.json.get('deptId')
if id:
enable = 1
d = Dept.query.filter_by(id=id).update({"status": enable})
if d:
db.session.commit()
return success_api(msg="启用成功")
return fail_api(msg="出错啦")
return fail_api(msg="数据错误")
# 禁用
@dept_bp.put('/disable')
@authorize("admin:dept:edit", log=True)
def dis_enable():
id = request.json.get('deptId')
if id:
enable = 0
d = Dept.query.filter_by(id=id).update({"status": enable})
if d:
db.session.commit()
return success_api(msg="禁用成功")
return fail_api(msg="出错啦")
return fail_api(msg="数据错误")
@dept_bp.put('/update')
@authorize("admin:dept:edit", log=True)
def update():
json = request.json
id = json.get("deptId"),
data = {
"dept_name": validate.xss_escape(json.get("deptName")),
"sort": validate.xss_escape(json.get("sort")),
"leader": validate.xss_escape(json.get("leader")),
"phone": validate.xss_escape(json.get("phone")),
"email": validate.xss_escape(json.get("email")),
"status": validate.xss_escape(json.get("status")),
"address": validate.xss_escape(json.get("address"))
}
d = Dept.query.filter_by(id=id).update(data)
if not d:
return fail_api(msg="更新失败")
db.session.commit()
return success_api(msg="更新成功")
@dept_bp.delete('/remove/<int:_id>')
@authorize("admin:dept:remove", log=True)
def remove(_id):
d = Dept.query.filter_by(id=_id).delete()
if not d:
return fail_api(msg="删除失败")
res = User.query.filter_by(dept_id=_id).update({"dept_id": None})
db.session.commit()
if res:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
+12
View File
@@ -0,0 +1,12 @@
from applications.view.index.index import index_bp
from . import index
def register_index_views(app):
"""
初始化蓝图
"""
app.register_blueprint(index_bp)
+8
View File
@@ -0,0 +1,8 @@
from flask import render_template, Blueprint
index_bp = Blueprint('Index', __name__, url_prefix='/')
@index_bp.route('/')
def index():
return render_template('index/index.html')
+76
View File
@@ -0,0 +1,76 @@
from flask import Blueprint, session, redirect, url_for, render_template, request
from flask_login import current_user, login_user, login_required, logout_user
from applications.common import admin as index_curd
from applications.common.admin_log import login_log
from applications.common.utils.http import fail_api, success_api
from applications.models import User
passport_bp = Blueprint('passport', __name__, url_prefix='/passport')
def register_passport_views(app):
app.register_blueprint(passport_bp)
# 获取验证码
@passport_bp.get('/getCaptcha')
def get_captcha():
resp, code = index_curd.get_captcha()
session["code"] = code
return resp
# 登录
@passport_bp.get('/login')
def login():
if current_user.is_authenticated:
return redirect(url_for('admin.index'))
return render_template('admin/login.html')
# 登录
@passport_bp.post('/login')
def login_post():
req = request.form
username = req.get('username')
password = req.get('password')
code = req.get('captcha').__str__().lower()
if not username or not password or not code:
return fail_api(msg="用户名或密码没有输入")
s_code = session.get("code", None)
session["code"] = None
if not all([code, s_code]):
return fail_api(msg="参数错误")
if code != s_code:
return fail_api(msg="验证码错误")
user = User.query.filter_by(username=username).first()
if not user:
return fail_api(msg="不存在的用户")
if user.enable == 0:
return fail_api(msg="用户被暂停使用")
if username == user.username and user.validate_password(password):
# 登录
login_user(user)
# 记录登录日志
login_log(request, uid=user.id, is_access=True)
# 存入权限
index_curd.add_auth_session()
return success_api(msg="登录成功")
login_log(request, uid=user.id, is_access=False)
return fail_api(msg="用户名或密码错误")
# 退出登录
@passport_bp.post('/logout')
@login_required
def logout():
logout_user()
session.pop('permissions')
return success_api(msg="注销成功")
+9
View File
@@ -0,0 +1,9 @@
from flask import Blueprint, Flask
rights_bp = Blueprint('rights', __name__, url_prefix='/rights')
from . import routes
def register_rights_view(app: Flask):
app.register_blueprint(rights_bp)
+20
View File
@@ -0,0 +1,20 @@
# 渲染配置
from flask import jsonify
from flask_login import login_required
from . import rights_bp
from ...common import admin
@rights_bp.get('/configs')
@login_required
def configs():
return admin.get_render_config()
# 菜单
@rights_bp.get('/menu')
@login_required
def menu():
menu_tree = admin.make_menu_tree()
return jsonify(menu_tree)
-7
View File
@@ -1,7 +0,0 @@
from applications.views.admin import init_adminViews
from applications.views.index import init_indexViews
def init_view(app):
init_adminViews(app)
init_indexViews(app)
-22
View File
@@ -1,22 +0,0 @@
from applications.views.admin.index import admin_index
from applications.views.admin.user import admin_user
from applications.views.admin.file import admin_file
from applications.views.admin.monitor import admin_Monitor
from applications.views.admin.admin_log import admin_log
from applications.views.admin.power import admin_power
from applications.views.admin.role import admin_role
"""
初始化蓝图
"""
def init_adminViews(app):
app.register_blueprint(admin_index)
app.register_blueprint(admin_user)
app.register_blueprint(admin_file)
app.register_blueprint(admin_Monitor)
app.register_blueprint(admin_log)
app.register_blueprint(admin_power)
app.register_blueprint(admin_role)
-88
View File
@@ -1,88 +0,0 @@
from flask import Blueprint, request, render_template, jsonify
from flask_login import login_required
from flask_marshmallow import Marshmallow
from marshmallow import fields
from sqlalchemy import desc
from applications.models.admin import AdminLog
ma = Marshmallow()
admin_log = Blueprint('adminLog', __name__, url_prefix='/admin/log')
# ----------------------------------------------------------
# ------------------------- 日志管理 --------------------------
# ----------------------------------------------------------
@admin_log.route('/')
@login_required
def index():
return render_template('admin/admin_log/main.html')
class LogSchema(ma.Schema): # 序列化类
id = fields.Integer()
method = fields.Str()
uid = fields.Str()
url = fields.Str()
desc = fields.Str()
ip = fields.Str()
user_agent = fields.Str()
success = fields.Bool()
create_time = fields.DateTime()
# ==========================================================
# 登录日志
# ==========================================================
@admin_log.route('/loginLog')
@login_required
def loginLog():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
log = AdminLog.query.filter_by(url='/admin/login').order_by(desc(AdminLog.create_time)).paginate(page=page,
per_page=limit,
error_out=False)
count = AdminLog.query.filter_by(url='/admin/login').count()
role_schema = LogSchema(many=True)
output = role_schema.dump(log.items)
res = {
'msg': "",
'code': 0,
'data': output,
'count': count,
'limit': "10"
}
return jsonify(res)
# ==========================================================
# 操作日志
# ==========================================================
@admin_log.route('/operateLog')
@login_required
def operateLog():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
log = AdminLog.query.filter(AdminLog.url != '/admin/login').order_by(desc(AdminLog.create_time)).paginate(page=page,
per_page=limit,
error_out=False)
count = AdminLog.query.filter(AdminLog.url != '/admin/login').count()
role_schema = LogSchema(many=True)
output = role_schema.dump(log.items)
res = {
'msg': "",
'code': 0,
'data': output,
'count': count,
'limit': "10"
}
return jsonify(res)
@@ -1,11 +0,0 @@
from flask import session
def init_template_global(app):
@app.template_global()
def authorize(power):
# print(power)
# print(session.get('permissions'))
return bool(power in session.get('permissions'))
-16
View File
@@ -1,16 +0,0 @@
from flask import render_template
def init_error(app):
@app.errorhandler(403)
def page_not_found(e):
return render_template('errors/403.html'), 403
@app.errorhandler(404)
def page_not_found(e):
return render_template('errors/404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('errors/500.html'), 500
-89
View File
@@ -1,89 +0,0 @@
from flask import Blueprint, render_template, jsonify, request, session, redirect, url_for
from flask_login import login_user, login_required, logout_user, current_user
from applications.service.admin.index import add_auth_session, make_menu_tree, get_captcha
from applications.service.admin_log import login_log
from applications.models.admin import User
admin_index = Blueprint('adminIndex', __name__, url_prefix='/admin')
# 首页
@admin_index.route('/')
@login_required
def index():
realname = current_user.realname
return render_template('admin/index.html', realname=realname)
# 获取验证码
@admin_index.route('/getCaptcha', methods=["GET"])
def getCaptcha():
resp,code=get_captcha()
session["code"] = code
return resp
# 登录
@admin_index.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
req = request.form
username = req.get('username')
password = req.get('password')
code = req.get('captcha')
if not username or not password or not code:
return jsonify(msg="用户名或密码没有输入", code=0,success=False)
s_code = session.get("code", None)
if not all([code, s_code]):
return jsonify(msg="参数错误", code=0,success=False)
if code != s_code:
return jsonify(msg="验证码错误", code=0,success=False)
user = User.query.filter_by(username=username).first()
if user is None:
return jsonify(msg="不存在的用户", code=0,success=False)
if user.enable is 0:
return jsonify(msg="用户被暂停使用", code=0,success=False)
if username == user.username and user.validate_password(password):
# 登录
login_user(user)
# 记录登录日志
login_log(request, uid=user.id, is_access=True)
# 存入权限
add_auth_session()
return jsonify(msg="登录成功", code=1,success=True)
login_log(request,uid=user.id, is_access=False)
return jsonify(msg="用户名或密码错误", code=0,success=False)
print(current_user.is_authenticated)
if current_user.is_authenticated:
return redirect(url_for('adminIndex.index'))
return render_template('admin/login.html')
# 退出登录
@admin_index.route('/logout', methods=['GET', 'POST'])
@login_required
def logout():
logout_user()
session.pop('permissions')
return jsonify(msg="注销成功", success=True)
# 菜单
@admin_index.route('/menu')
@login_required
def menu():
menu_tree = make_menu_tree()
return jsonify(menu_tree)
# 控制台页面
@admin_index.route('/welcome')
@login_required
def welcome():
return render_template('admin/console/console.html')
-121
View File
@@ -1,121 +0,0 @@
from flask import Blueprint, render_template, request, jsonify
from flask_login import login_required
from applications.service.admin.power import get_power_dict, select_parent, save_power, remove_power, get_power_by_id, \
update_power, enable_status, disable_status, batch_remove
from applications.service.route_auth import authorize_and_log
admin_power = Blueprint('adminPower', __name__, url_prefix='/admin/power')
@admin_power.route('/')
@authorize_and_log("admin:power:main")
def index():
return render_template('admin/power/main.html')
@admin_power.route('/data')
@authorize_and_log("admin:power:main")
def data():
power_data = get_power_dict()
res = {
"data": power_data
}
return jsonify(res)
@admin_power.route('/add')
@authorize_and_log("admin:power:add")
def add():
return render_template('admin/power/add.html')
@admin_power.route('/selectParent')
@authorize_and_log("admin:power:main")
def selectParent():
power_data = select_parent()
res = {
"status": {"code": 200, "message": "默认"},
"data": power_data
}
return jsonify(res)
# 增加
@admin_power.route('/save', methods=['POST'])
@authorize_and_log("admin:power:add")
def save():
req = request.json
save_power(req)
return jsonify(msg="成功", success=True)
# 权限编辑
@admin_power.route('/edit/<int:id>', methods=['GET', 'POST'])
@authorize_and_log("admin:power:edit")
def edit(id):
power = get_power_by_id(id)
icon = str(power.icon).split()
if len(icon) == 2:
icon = icon[1]
else:
icon = None
return render_template('admin/power/edit.html', power=power, icon=icon)
# 权限更新
@admin_power.route('/update', methods=['PUT'])
@authorize_and_log("admin:power:edit")
def update():
res = update_power(request.json)
if not res:
return jsonify(success=False, msg="更新权限失败")
return jsonify(success=True, msg="更新权限成功")
# 启用用户
@admin_power.route('/enable', methods=['PUT'])
@authorize_and_log("admin:power:edit")
def enable():
id = request.json.get('powerId')
print(id)
if id:
res = enable_status(id)
if not res:
return jsonify(msg="出错啦", success=False)
return jsonify(msg="启动成功", success=True)
return jsonify(msg="数据错误", success=False)
# 禁用用户
@admin_power.route('/disable', methods=['PUT'])
@authorize_and_log("admin:power:edit")
def disenable():
id = request.json.get('powerId')
print(id)
if id:
res = disable_status(id)
if not res:
return jsonify(msg="出错啦", success=False)
return jsonify(msg="禁用成功", success=True)
return jsonify(msg="数据错误", success=False)
# 权限删除
@admin_power.route('/remove/<int:id>', methods=['DELETE'])
@authorize_and_log("admin:power:remove")
def remove(id):
r = remove_power(id)
if r:
return jsonify(success=True, msg="删除成功")
else:
return jsonify(success=False, msg="删除失败")
# 批量删除
@admin_power.route('/batchRemove', methods=['DELETE'])
@authorize_and_log("admin:power:remove")
def batchRemove():
ids = request.form.getlist('ids[]')
batch_remove(ids)
return jsonify(success=True, msg="批量删除成功")
-153
View File
@@ -1,153 +0,0 @@
from flask import Blueprint, render_template, request, jsonify
from flask_login import login_required
from applications.service.admin.role import get_role_data_dict, add_role, get_role_power, update_role_power, \
get_role_by_id, update_role, remove_role, batch_remove, enable_status, disable_status
from applications.service.route_auth import authorize_and_log
admin_role = Blueprint('adminRole', __name__, url_prefix='/admin/role')
# 用户管理
@admin_role.route('/')
@authorize_and_log("admin:role:main")
def main():
return render_template('admin/role/main.html')
# 表格数据
@admin_role.route('/data')
@authorize_and_log("admin:role:main")
def table():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
roleName = request.args.get('roleName', type=str)
roleCode = request.args.get('roleCode', type=str)
filters = {}
if roleName:
filters["name"] = ('%' + roleName + '%')
if roleCode:
filters["code"] = ('%' + roleCode + '%')
data, count = get_role_data_dict(page=page, limit=limit, filters=filters)
res = {
'msg': "",
'code': 0,
'data': data,
'count': count,
'limit': "10"
}
return jsonify(res)
# 角色增加
@admin_role.route('/add')
@authorize_and_log("admin:role:add")
@login_required
def add():
return render_template('admin/role/add.html')
# 角色增加
@admin_role.route('/save', methods=['POST'])
@authorize_and_log("admin:role:add")
def save():
req = request.json
add_role(req=req)
return jsonify(msg="成功", success=True)
# 角色授权
@admin_role.route('/power/<int:id>')
@authorize_and_log("admin:role:power")
def power(id):
return render_template('admin/role/power.html', id=id)
# 获取角色权限
@admin_role.route('/getRolePower/<int:id>')
@authorize_and_log("admin:role:main")
def getRolePower(id):
powers = get_role_power(id)
res = {
"data": powers,
"status": {"code": 200, "message": "默认"}
}
return jsonify(res)
# 保存角色权限
@admin_role.route('/saveRolePower', methods=['PUT'])
@authorize_and_log("admin:role:edit")
def saveRolePower():
req_form = request.form
powerIds = req_form.get("powerIds")
power_list = powerIds.split(',')
roleId = req_form.get("roleId")
update_role_power(id=roleId, power_list=power_list)
return jsonify(success=True, msg="授权成功")
# 角色编辑
@admin_role.route('/edit/<int:id>', methods=['GET', 'POST'])
@authorize_and_log("admin:role:edit")
def edit(id):
role = get_role_by_id(id)
return render_template('admin/role/edit.html', role=role)
# 更新角色
@admin_role.route('/update', methods=['PUT'])
@authorize_and_log("admin:role:edit")
def update():
res = update_role(request.json)
if not res:
return jsonify(success=False, msg="更新角色失败")
return jsonify(success=True, msg="更新角色成功")
# 启用用户
@admin_role.route('/enable', methods=['PUT'])
@authorize_and_log("admin:role:edit")
def enable():
id = request.json.get('roleId')
print(id)
if id:
res = enable_status(id)
if not res:
return jsonify(msg="出错啦", success=False)
return jsonify(msg="启动成功", success=True)
return jsonify(msg="数据错误", success=False)
# 禁用用户
@admin_role.route('/disable', methods=['PUT'])
@authorize_and_log("admin:role:edit")
def disenable():
id = request.json.get('roleId')
print(id)
if id:
res = disable_status(id)
if not res:
return jsonify(msg="出错啦", success=False)
return jsonify(msg="禁用成功", success=True)
return jsonify(msg="数据错误", success=False)
# 角色删除
@admin_role.route('/remove/<int:id>', methods=['DELETE'])
@authorize_and_log("admin:role:remove")
def remove(id):
res = remove_role(id)
if not res:
return jsonify(success=False, msg="角色删除失败")
return jsonify(success=True, msg="角色删除成功")
# 批量删除
@admin_role.route('/batchRemove', methods=['DELETE'])
@authorize_and_log("admin:role:remove")
@login_required
def batchRemove():
ids = request.form.getlist('ids[]')
batch_remove(ids)
return jsonify(success=True,msg="批量删除成功")
-144
View File
@@ -1,144 +0,0 @@
from flask import Blueprint, render_template, request, jsonify
from flask_login import login_required
from applications.models.admin import User, Role
from applications.service.admin.user import get_user_data_dict, add_user, delete_by_id, \
batch_remove, update_user, update_user_role, is_user_exists, add_user_role, enable_status, disable_status
from applications.service.route_auth import authorize_and_log
admin_user = Blueprint('adminUser', __name__, url_prefix='/admin/user')
# 用户管理
@admin_user.route('/')
@authorize_and_log("admin:user:main")
def main():
return render_template('admin/user/main.html')
# 用户分页查询
@admin_user.route('/data')
@authorize_and_log("admin:user:main")
def data():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
realName = request.args.get('realName', type=str)
username = request.args.get('username', type=str)
filters = {}
if realName:
filters["realname"] = ('%' + realName + '%')
if username:
filters["username"] = ('%' + username + '%')
data, count = get_user_data_dict(page=page, limit=limit, filters=filters)
res = {
'msg': "",
'code': 0,
'data': data,
'count': count,
'limit': "10"
}
return jsonify(res)
# 用户增加
@admin_user.route('/add')
@authorize_and_log("admin:user:add")
def add():
roles = Role.query.all()
return render_template('admin/user/add.html', roles=roles)
@admin_user.route('/save', methods=['POST'])
@authorize_and_log("admin:user:add")
def save():
req_json = request.json
a = req_json.get("roleIds")
username = req_json.get('username')
realName = req_json.get('realName')
password = req_json.get('password')
role_ids = a.split(',')
if not username or not realName or not password:
return jsonify(success=False, msg="账号姓名密码不得为空")
if is_user_exists(username):
return jsonify(success=False, msg="用户已经存在")
id = add_user(username, realName, password)
add_user_role(id, role_ids)
return jsonify(success=True, msg="增加成功")
# 删除用户
@admin_user.route('/remove/<int:id>', methods=['DELETE'])
@authorize_and_log("admin:user:remove")
def delete(id):
res = delete_by_id(id)
if not res:
return jsonify(msg="删除失败", success=False)
return jsonify(msg="删除成功", success=True)
# 编辑用户
@admin_user.route('/edit/<int:id>', methods=['GET', 'POST'])
@authorize_and_log("admin:user:edit")
def edit(id):
user = User.query.filter_by(id=id).first()
roles = Role.query.all()
checked_roles = []
for r in user.role:
checked_roles.append(r.id)
return render_template('admin/user/edit.html', user=user, roles=roles, checked_roles=checked_roles)
# 编辑用户
@admin_user.route('/update', methods=['PUT'])
@authorize_and_log("admin:user:edit")
def update():
req_json = request.json
a = req_json.get("roleIds")
id = req_json.get("userId")
username = req_json.get('username')
realName = req_json.get('realName')
role_ids = a.split(',')
update_user(id, username, realName)
update_user_role(id, role_ids)
return jsonify(success=True, msg="更新成功")
# 启用用户
@admin_user.route('/enable', methods=['PUT'])
@authorize_and_log("admin:user:edit")
def enable():
id = request.json.get('userId')
print(id)
if id:
res = enable_status(id)
if not res:
return jsonify(msg="出错啦", success=False)
return jsonify(msg="启动成功", success=True)
return jsonify(msg="数据错误", success=False)
# 禁用用户
@admin_user.route('/disable', methods=['PUT'])
@authorize_and_log("admin:user:edit")
def disenable():
id = request.json.get('userId')
print(id)
if id:
res = disable_status(id)
if not res:
return jsonify(msg="出错啦", success=False)
return jsonify(msg="禁用成功", success=True)
return jsonify(msg="数据错误", success=False)
# 批量删除
@admin_user.route('/batchRemove', methods=['DELETE'])
@authorize_and_log("admin:user:remove")
def batchRemove():
ids = request.form.getlist('ids[]')
batch_remove(ids)
return jsonify(success=True, msg="批量删除成功")
-10
View File
@@ -1,10 +0,0 @@
from applications.views.index.index import index_index
def init_indexViews(app):
"""
初始化蓝图
"""
app.register_blueprint(index_index)
-11
View File
@@ -1,11 +0,0 @@
from flask import Blueprint,render_template
index_index = Blueprint('Index', __name__, url_prefix='/')
from flask_marshmallow import Marshmallow
ma = Marshmallow()
@index_index.route('/')
def index():
return render_template('index/index.html')
-1144
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
version: '3'
services:
flask:
build: .
ports:
- "8000:8000"
restart: always
links:
- mysql:dbserver
mysql:
image: "mysql:5.7"
environment:
- MYSQL_ROOT_PASSWORD=root
restart: always
View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Some files were not shown because too many files have changed in this diff Show More