diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3e90094574b35fa7cd337afaec812c0ce05a4340..0000000000000000000000000000000000000000 --- a/.gitignore +++ /dev/null @@ -1,127 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# idea -.idea/ - -# 迁移文件 -migrations/ - -# sqlite -*.db - -# 文件上传 -static/upload/ \ No newline at end of file diff --git a/applications/__init__.py b/applications/__init__.py index af5bb8e42654ca58101c80d7654ca961369e95c3..e2ef8f36b31f9cbf33d5e2fc7173e9355603b0fc 100644 --- a/applications/__init__.py +++ b/applications/__init__.py @@ -20,4 +20,6 @@ def create_app(): # 注册命令 init_script(app) + + return app diff --git a/applications/common/script/admin.py b/applications/common/script/admin.py index 35c9cffae456fa61b3f663bab30814a384fe7409..3bfe2a82c875dab1e6cc1dad43e072bf9c2347c6 100644 --- a/applications/common/script/admin.py +++ b/applications/common/script/admin.py @@ -1,9 +1,10 @@ -import datetime +import datetime,click from flask.cli import AppGroup from applications.extensions import db -from applications.models import User, Role, Dept, Power +from applications.models import User, Role, RoleGroup, Dept, Power + admin_cli = AppGroup("admin") @@ -51,6 +52,7 @@ roledata = [ enable=1, details='管理员', sort=1, + group_id="1", create_time=now_time, ), Role( @@ -60,8 +62,16 @@ roledata = [ enable=1, details='只有查看,没有增删改权限', sort=2, + group_id="1", create_time=now_time, + ), + RoleGroup( + id=1, + name="默认分组", + sort=1, + create_time =now_time, ) + ] deptdata = [ Dept( @@ -556,6 +566,14 @@ def add_role_power(): db.session.commit() + +@admin_cli.command("drop") +def init_db(): + click.confirm('此操作或删除所有数据表,确认继续?', abort=True) + db.drop_all() + click.echo('数据表删除完毕') + + @admin_cli.command("init") def init_db(): db.session.add_all(userdata) diff --git a/applications/common/utils/http.py b/applications/common/utils/http.py index 680df5fc336e3a1895816ef2d7f24a79a9cca291..b2e740a5ecba7c7b42e1405393b2bc855b923029 100644 --- a/applications/common/utils/http.py +++ b/applications/common/utils/http.py @@ -22,3 +22,12 @@ def table_api(msg: str = "", count=0, data=None, limit=10): } return jsonify(res) + +def data_api(msg: str = "成功", data=None, success=True ): + """ 动态数据渲染响应 """ + res = { + 'msg': msg, + 'data': data, + "success": success + } + return jsonify(res) diff --git a/applications/config.py b/applications/config.py deleted file mode 100644 index ed79ce14a028a78d00e2a4ef98a9561c41eebd8c..0000000000000000000000000000000000000000 --- a/applications/config.py +++ /dev/null @@ -1,80 +0,0 @@ -import logging -# from urllib.parse import quote_plus as urlquote - - -class BaseConfig: - SUPERADMIN = 'admin' - - 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'] - UPLOADS_AUTOSERVE = True - - # JSON配置 - JSON_AS_ASCII = False - - SECRET_KEY = "pear-system-flask" - - # mysql 配置 - # MYSQL_USERNAME = "root" - # MYSQL_PASSWORD = "123456" - # MYSQL_HOST = "127.0.0.1" - # MYSQL_PORT = 3306 - # MYSQL_DATABASE = "PearAdminFlask" - - # 数据库的配置信息 - SQLALCHEMY_DATABASE_URI = 'sqlite:///../pear.db' - # SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USERNAME}:{urlquote(MYSQL_PASSWORD)}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}?charset=utf8mb4" - - # 默认日志等级 - LOG_LEVEL = logging.WARN - # flask-mail配置 - MAIL_SERVER = 'smtp.qq.com' - MAIL_USE_TLS = False - MAIL_USE_SSL = True - MAIL_PORT = 465 - MAIL_USERNAME = '123@qq.com' - MAIL_PASSWORD = 'XXXXX' # 生成的授权码 - MAIL_DEFAULT_SENDER = MAIL_USERNAME - - # 插件配置,填写插件的文件名名称,默认不启用插件。 - PLUGIN_ENABLE_FOLDERS = [] - - # 配置多个数据库连接的连接串写法示例 - # HOSTNAME: 指数据库的IP地址、USERNAME:指数据库登录的用户名、PASSWORD:指数据库登录密码、PORT:指数据库开放的端口、DATABASE:指需要连接的数据库名称 - # MSSQL: f"mssql+pymssql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=cp936" - # MySQL: f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4" - # Oracle: f"oracle+cx_oracle://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}" - # SQLite "sqlite:/// database.db" - # Postgres f"postgresql+psycopg2://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}" - # Oracle的第二种连接方式 - # dsnStr = cx_Oracle.makedsn({HOSTNAME}, 1521, service_name='orcl') - # connect_str = "oracle://%s:%s@%s" % ('{USERNAME}', ' {PASSWORD}', dsnStr) - - # 在SQLALCHEMY_BINDS 中设置:'{数据库连接别名}': '{连接串}' - # 最后在models的数据模型class中,在__tablename__前设置 __bind_key__ = '{数据库连接别名}' 即可,表示该数据模型不使用默认的数据库连接,改用“SQLALCHEMY_BINDS”中设置的其他数据库连接 - # SQLALCHEMY_BINDS = { - # 'testMySQL': 'mysql+pymysql://test:123456@192.168.1.1:3306/test?charset=utf8', - # 'testMsSQL': 'mssql+pymssql://test:123456@192.168.1.1:1433/test?charset=cp936', - # 'testOracle': 'oracle+cx_oracle://test:123456@192.168.1.1:1521/test', - # 'testSQLite': 'sqlite:///database.db - # } diff --git a/applications/extensions/__init__.py b/applications/extensions/__init__.py index 7a1a3788039c74513bafa1c1e5b306d62197c3d9..18418d280bc04fbe0124660510cc626c62694f46 100644 --- a/applications/extensions/__init__.py +++ b/applications/extensions/__init__.py @@ -7,7 +7,7 @@ from .init_error_views import init_error_views from .init_mail import init_mail, mail as flask_mail from .init_upload import init_upload from .init_migrate import init_migrate - +from .init_template_func import init_template_func def init_plugs(app: Flask) -> None: init_login_manager(app) @@ -16,4 +16,5 @@ def init_plugs(app: Flask) -> None: init_error_views(app) init_mail(app) init_upload(app) - init_migrate(app) \ No newline at end of file + init_migrate(app) + init_template_func(app) \ No newline at end of file diff --git a/applications/extensions/init_template_func.py b/applications/extensions/init_template_func.py new file mode 100644 index 0000000000000000000000000000000000000000..2979d8bdbc90a251525accf08f97b8e4ff24ea30 --- /dev/null +++ b/applications/extensions/init_template_func.py @@ -0,0 +1,47 @@ +from datetime import datetime +import datetime as datetimeO +def init(app): + @app.template_global() + def getDatetimeSplit(d): + if d: + dt=datetime.strptime(d,"%Y-%m-%dT%H:%M:%S") + return [dt.year,dt.month,dt.day,dt.hour,dt.minute,dt.second] + else: + return None + @app.template_global() + def getDateSplit(d): + if d: + dt=datetime.strptime(d,"%Y-%m-%d") + return [dt.year,dt.month,dt.day] + else: + return None + @app.template_global() + def _len(d): + return len(d) + @app.template_global() + def _toString(d): + return str(d) + @app.template_global() + def _enumerate(d): + return enumerate(d) + @app.template_global() + def _strftime(d,formatStr='%Y-%m-%d'): + if "T" in d : + dt = datetime.strptime(d, "%Y-%m-%dT%H:%M:%S") + else: + dt=datetime.strptime(d,"%Y-%m-%d") + return dt.strftime(formatStr) + + @app.template_global() + def _timedelta(d,days=0,hours=0,minutes=0,seconds=0,formatStr='%Y-%m-%d'): + dt= datetime.strptime(d,formatStr) + dt=dt+ datetimeO.timedelta(days=days,hours=hours,minutes=minutes,seconds=seconds) + return datetime.strftime(dt,formatStr) + + @app.template_global() + def _round(n,d): + return round(n,d) + + +def init_template_func(app): + init(app) diff --git a/applications/models/__init__.py b/applications/models/__init__.py index d94b26539de13d4ef34542987f64fd1d72d236a3..fdd3713dd83de964015ff6f10778456cf91f71c9 100644 --- a/applications/models/__init__.py +++ b/applications/models/__init__.py @@ -3,7 +3,7 @@ from .admin_dict import DictType, DictData from .admin_log import AdminLog from .admin_photo import Photo from .admin_power import Power -from .admin_role import Role +from .admin_role import Role,RoleGroup from .admin_role_power import role_power from .admin_user import User from .admin_user_role import user_role diff --git a/applications/models/admin_role.py b/applications/models/admin_role.py index 718a5d2929a592229afcf34647129959f221b3f8..b880031ebf55b6dc0c3b616b2c83437e67471021 100644 --- a/applications/models/admin_role.py +++ b/applications/models/admin_role.py @@ -14,3 +14,15 @@ class Role(db.Model): 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')) + group_id = db.Column(db.Integer, db.ForeignKey('admin_role_group.id')) + group=db.relationship("RoleGroup", back_populates="roles") + +class RoleGroup(db.Model): + __tablename__ = 'admin_role_group' + id = db.Column(db.Integer, primary_key=True, comment='角色分组ID') + name = db.Column(db.String(255), comment='角色分组名称') + remark = 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='更新时间') + roles = db.relationship('Role', back_populates="group") \ No newline at end of file diff --git a/applications/schemas/__init__.py b/applications/schemas/__init__.py index 56aa9d0b0aeb1964054bdee9d789753eef865c54..704cd382d5062eb5cb382d9e53649653e9244831 100644 --- a/applications/schemas/__init__.py +++ b/applications/schemas/__init__.py @@ -1,5 +1,5 @@ -from .admin_role import RoleOutSchema +from .admin_role import RoleOutSchema,RoleGroupSchema from .admin_power import PowerOutSchema, PowerOutSchema2 from .admin_dict import DictDataOutSchema, DictTypeOutSchema from .admin_dept import DeptSchema diff --git a/applications/schemas/admin_role.py b/applications/schemas/admin_role.py index bf6c819e3d4a2810d9c16baf5fbef3ea88f6fb52..04eaf5c75136bf23d2184eec7c296df535e58815 100644 --- a/applications/schemas/admin_role.py +++ b/applications/schemas/admin_role.py @@ -1,12 +1,37 @@ from flask_marshmallow.sqla import SQLAlchemyAutoSchema - +from applications.extensions import ma +from applications.extensions.init_sqlalchemy import db +from marshmallow import fields from applications.models import Role -class RoleOutSchema(SQLAlchemyAutoSchema): - class Meta: - model = Role # table = models.Album.__table__ - # include_relationships = True # 输出模型对象时同时对外键,是否也一并进行处理 - include_fk = True # 序列化阶段是否也一并返回主键 - # fields= ["id","name"] # 启动的字段列表 - # exclude = ["id","name"] # 排除字段列表 +# class RoleOutSchema(SQLAlchemyAutoSchema): + # class Meta: + # model = Role # table = models.Album.__table__ + # include_relationships = True # 输出模型对象时同时对外键,是否也一并进行处理 + # include_fk = True # 序列化阶段是否也一并返回主键 + # sqla_session=db.session + # # fields= ["id","name"] # 启动的字段列表 + # # exclude = ["id","name"] # 排除字段列表 + +class RoleOutSchema(ma.Schema): + id = fields.Integer() + name = fields.Str() + code = fields.Str() + enable=fields.Integer() + remark = fields.Str() + details = fields.Str() + sort = fields.Integer() + create_time = fields.DateTime() + update_time = fields.DateTime() + group=fields.Nested(lambda:RoleGroupSchema()) + +# 用户models的序列化类 +class RoleGroupSchema(ma.Schema): + id = fields.Integer() + name = fields.Str() + remark = fields.Str() + sort = fields.Integer() + create_time = fields.DateTime() + update_time = fields.DateTime() + # roles = fields.Nested(lambda: RoleOutSchema()) diff --git a/applications/view/system/role.py b/applications/view/system/role.py index 54b9f813a301f0ace90d6b6d507ead5cd7e22519..d25a4fb25a4a022d78020711ff1d926a0d8e7361 100644 --- a/applications/view/system/role.py +++ b/applications/view/system/role.py @@ -5,9 +5,10 @@ from applications.common.curd import model_to_dicts, enable_status, disable_stat 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 str_escape +from sqlalchemy import func from applications.extensions import db -from applications.models import Role, Power, User -from applications.schemas import RoleOutSchema, PowerOutSchema2 +from applications.models import Role, RoleGroup, Power, User +from applications.schemas import RoleOutSchema, PowerOutSchema2,RoleGroupSchema bp = Blueprint('role', __name__, url_prefix='/role') @@ -37,7 +38,9 @@ def table(): @bp.get('/add') @authorize("system:role:add", log=True) def add(): - return render_template('system/role/add.html') + roleGroups=RoleGroup.query.order_by(RoleGroup.id.asc()).all() + selectDic=[{"name":roleGroup.name,"value":roleGroup.id} for roleGroup in roleGroups] + return render_template('system/role/add.html',selectDic=selectDic) # 角色增加 @@ -49,13 +52,15 @@ def save(): enable = str_escape(req.get("enable")) roleCode = str_escape(req.get("roleCode")) roleName = str_escape(req.get("roleName")) + groupId = str_escape(req.get("groupId")) sort = str_escape(req.get("sort")) role = Role( details=details, enable=enable, code=roleCode, name=roleName, - sort=sort + sort=sort, + group_id=groupId ) db.session.add(role) db.session.commit() @@ -114,8 +119,10 @@ def save_role_power(): @bp.get('/edit/') @authorize("system:role:edit", log=True) def edit(id): - r = get_one_by_id(model=Role, id=id) - return render_template('system/role/edit.html', role=r) + role = get_one_by_id(model=Role, id=id) + roleGroups=RoleGroup.query.order_by(RoleGroup.id.asc()).all() + selectDic=[{"name":roleGroup.name,"value":roleGroup.id} for roleGroup in roleGroups] + return render_template('system/role/edit.html', role=role,selectDic=selectDic) # 更新角色 @@ -178,3 +185,89 @@ def remove(id): if not r: return fail_api(msg="角色删除失败") return success_api(msg="角色删除成功") + + +# 角色分组管理 +@bp.get('/group') +@authorize("system:role:add", log=True) +def group(): + obj=RoleGroup.query.with_entities(func.max(RoleGroup.sort)).first() + if obj: + maxSort=obj[0]+1 + else: + maxSort=1 + return render_template('system/role/group.html',sort=maxSort) + +# 角色分组管理 +@bp.get('/group/data') +@authorize("system:role:add", log=True) +def groupData(): + print("jaja") + roleGroups = RoleGroup.query.order_by(RoleGroup.sort.asc()).layui_paginate() + return table_api(data=RoleGroupSchema(many=True).dump(roleGroups), count=roleGroups.total) + +# 角色分组编辑 +@bp.put('/group/update') +@authorize("system:role:edit", log=True) +def groupUpdate(): + req_json = request.get_json(force=True) + id = req_json.get("id") + + roleGroup=get_one_by_id(RoleGroup,id) + + if roleGroup.name=="默认分组": + return fail_api(msg="默认分组不允许修改") + data = { + "name": str_escape(req_json.get("name")), + "sort": str_escape(req_json.get("sort")), + "remark": str_escape(req_json.get("remark")) + } + + role = RoleGroup.query.filter_by(id=id).update(data) + db.session.commit() + if not role: + return fail_api(msg="更新角色分组失败") + return success_api(msg="更新角色分组成功") + + +# 角色分组增加 +@bp.post('/group/save') +@authorize("system:role:add", log=True) +def groupSave(): + req = request.get_json(force=True) + name = str_escape(req.get("name")) + sort = str_escape(req.get("sort")) + remark = str_escape(req.get("remark")) + + roleGroup = RoleGroup.query.filter_by(name="name").first() + if roleGroup: + return fail_api(msg="改分组名称已存在!") + + roleGroup = RoleGroup( + name=name, + sort=sort, + remark=remark, + ) + db.session.add(roleGroup) + db.session.commit() + return success_api(msg="成功") + +# 角色分组删除 +@bp.delete('/group/remove/') +@authorize("system:role:remove", log=True) +def groupRemove(id): + + roleGroup = RoleGroup.query.filter_by(id=id).first() + if roleGroup.name=="默认分组": + return fail_api(msg="默认分组不能删除!") + + roleGroupDefault = RoleGroup.query.filter_by(name="默认分组").first() + if len(roleGroup.roles)>0: + roleGroupDefault.roles=roleGroupDefault.roles+roleGroup.roles + r=RoleGroup.query.filter_by(id=id).delete() + db.session.commit() + if not r: + return fail_api(msg="角色分组删除失败") + return success_api(msg="角色分组删除成功") + + diff --git a/applications/view/system/user.py b/applications/view/system/user.py index e0f93458702e12f8e62ce987fc67f0560291acb0..41c0e2fff9a8d2c8e345155d65a635bb0ee56aa3 100644 --- a/applications/view/system/user.py +++ b/applications/view/system/user.py @@ -8,7 +8,7 @@ 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 str_escape from applications.extensions import db -from applications.models import Role, Dept +from applications.models import Role,RoleGroup,Dept from applications.models import User, AdminLog bp = Blueprint('user', __name__, url_prefix='/user') @@ -39,7 +39,7 @@ def data(): if dept_id: filters.append(User.dept_id == dept_id) - # print(*filters) + query = db.session.query( User, Dept @@ -111,13 +111,15 @@ def delete(id): @authorize("system:user:edit", log=True) def edit(id): user = curd.get_one_by_id(User, id) - roles = Role.query.all() + + roles = Role.query.order_by(Role.group_id.asc()).all() checked_roles = [] for r in user.role: checked_roles.append(r.id) return render_template('system/user/edit.html', user=user, roles=roles, checked_roles=checked_roles) + # 编辑用户 @bp.put('/update') @authorize("system:user:edit", log=True) diff --git a/templates/macros/input.html b/templates/macros/input.html new file mode 100644 index 0000000000000000000000000000000000000000..1f4ca19ba89470527da70251b11b51e9c0d4aad4 --- /dev/null +++ b/templates/macros/input.html @@ -0,0 +1,15 @@ + +{#下拉列表 模板#} +{% macro selectField(name,selectDic,defaultKey,isRequire=False) %} + +{% endmacro %} + diff --git a/templates/system/role/add.html b/templates/system/role/add.html index 86c76df61d47ef735d0d77ca804e05758d22c8a1..2b7667455487e812a0235f12ef787aeec193d91a 100644 --- a/templates/system/role/add.html +++ b/templates/system/role/add.html @@ -1,3 +1,4 @@ +{%from "macros/input.html" import selectField%} @@ -22,6 +23,12 @@ class="layui-input"> +
+ +
+ {{selectField("groupId",selectDic,"默认分组",True)}} +
+
@@ -42,6 +49,8 @@
+ + diff --git a/templates/system/role/edit.html b/templates/system/role/edit.html index c8402845a649f2ba0a1fa77057c1198169912d8e..df5a95f1bb101534fc673ae67c6834ffbab35996 100644 --- a/templates/system/role/edit.html +++ b/templates/system/role/edit.html @@ -1,3 +1,4 @@ +{%from "macros/input.html" import selectField%} @@ -29,6 +30,12 @@ autocomplete="off" placeholder="请输入标题" class="layui-input"> +
+ +
+ {{selectField("groupId",selectDic,role.group.name,True)}} +
+
diff --git a/templates/system/role/group.html b/templates/system/role/group.html new file mode 100644 index 0000000000000000000000000000000000000000..d484e33f9686e5ddf9613469d664df6ef0dfcb30 --- /dev/null +++ b/templates/system/role/group.html @@ -0,0 +1,160 @@ + + + + 角色分组管理 + {% include 'system/common/header.html' %} + + + + +
+
+
+
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+
+
+
+
+ + + + + +{% include 'system/common/footer.html' %} + + + \ No newline at end of file diff --git a/templates/system/role/main.html b/templates/system/role/main.html index 850cecdb9a0f77e12ae2e769d90f1bc8de2d253c..6237260afde3e69779874b3d3abfc8e8df228f70 100644 --- a/templates/system/role/main.html +++ b/templates/system/role/main.html @@ -41,6 +41,11 @@ 新增 + + {% endif %} @@ -66,6 +71,10 @@ + + {% include 'system/common/footer.html' %}