!8 v2新分支

Merge pull request !8 from zhengxinonly/mini
This commit is contained in:
mkg
2021-07-03 00:18:38 +00:00
committed by Gitee
108 changed files with 2176 additions and 4493 deletions
+17
View File
@@ -0,0 +1,17 @@
# MySql配置信息
MYSQL_HOST=127.0.0.1
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-flask2'
FLASK_ENV=development
FLASK_RUN_HOST=0.0.0.0
FLASK_APP='applications:create_app("development")'
+6
View File
@@ -116,3 +116,9 @@ dmypy.json
#ide #ide
.idea/ .idea/
# 数据库文件
*.db
#上传到本地的图片文件
*upload*
migrations/
+37 -49
View File
@@ -30,17 +30,23 @@
</div> </div>
#### 项目简介 #### 项目简介
>Pear Admin Flask 基于 Flask 的后台管理系统,拥抱应用广泛的python语言,通过使用本系统,即可快速构建你的功能业务 Pear Admin Flask 基于 Flask 生态的后台管理系统,该项目旨在为 python 开发者提供一个快速开发前后端半分离的后台管理系统的模板
>
>项目旨在为python开发者提供一个后台管理系统的模板,成为您构建信息管理系统,物联网后台....等等应用时灵活,简单的工具 在使用本项目之前最好掌握以下知识点
>
>各位Python爱好者多多指教 + html、css、JavaScript
+ jQuery
+ layui
+ flask
+ flask-login
+ flask-sqlalchemy
+ flask-restful
Pear Admin Flask 分为 Common / Simple 两个版本: Pear Admin Flask 分为 Common / Simple 两个版本:
[** Common 通用版本 **](https://gitee.com/pear-admin/pear-admin-flask/tree/master/) [Common 通用版本](https://gitee.com/pear-admin/pear-admin-flask/tree/master/)
[Simple 简洁版本](https://gitee.com/pear-admin/pear-admin-flask/tree/simple/)
[** Simple 简洁版本 **](https://gitee.com/pear-admin/pear-admin-flask/tree/simple/)
#### 内置功能 #### 内置功能
@@ -49,9 +55,7 @@ Pear Admin Flask 分为 Common / Simple 两个版本:
- [x] 角色管理:角色菜单权限分配。 - [x] 角色管理:角色菜单权限分配。
- [x] 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 - [x] 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
- [x] 登录日志:系统登录日志记录查询包含登录异常。 - [x] 登录日志:系统登录日志记录查询包含登录异常。
- [x] 服务监控:监视当前系统CPU、内存、磁盘、python版本,运行时长等相关信息。 - [x] 文件上传: 图片上传示例
- [x] 文件上传: 图片上传示例
- [ ] 代码生成: 构想中....
#### 项目结构 #### 项目结构
@@ -73,62 +77,46 @@ Pear Admin Flask
├─requirement # 依赖文件 ├─requirement # 依赖文件
├─test # 测试文件夹(占坑) ├─test # 测试文件夹(占坑)
└─.env # 项目的配置文件 └─.env # 项目的配置文件
``` ```
#### 项目启动
##### 1. 下载源码
#### 项目安装
```bash ```bash
# 下 载
git clone https://gitee.com/pear-admin/pear-admin-flask git clone https://gitee.com/pear-admin/pear-admin-flask
# 安 装 # 切换分支
pip install -r requirement\requirement-dev.txt git checkout mini
# 配 置
.env
``` ```
#### 修改配置 ##### 2. 安装依赖
```python
# 主 机
HOST = '127.0.0.1'
# 端 口
PORT = '3306'
# 数 据 库
DATABASE = 'PearAdminFlask'
# 账 户
USERNAME = 'root'
# 密 码
PASSWORD = 'root'
```
#### Venv 安装
```bash ```bash
# 创建虚拟环境
python -m venv venv python -m venv venv
# 然后使虚拟环境生效(windows,Linux自行解决)
venv\Scripts\activate
# 安装依赖
pip install -r requirement\requirement-dev.txt
``` ```
#### 运行项目 ##### 3. 数据迁移
默认的使用 `sqlite3` 作为开发环境的数据库进行演示,如果需要二次开发,建议改成 `mysql`
```bash ```bash
# 初 始 化 数 据 库 flask db init
python test\init_databases.py flask db migrate -m '数据初始化'
flask db upgrade
flask init-db
``` ```
执行 flask run 命令启动项目
##### 4. 其他事项
如果需要在开发环境使用 mysql 作为数据库,请查看 `applications/configs/config.py` 文件里面的相关配置文件
如果需要修改数据的配置信息,请在 `.flaskenv` 里面调整即可
#### 预览项目 #### 预览项目
-6
View File
@@ -1,6 +0,0 @@
from applications import create_app
app = create_app()
if __name__ == '__main__':
app.run()
+20 -1
View File
@@ -30,10 +30,29 @@ def create_app(config_name=None):
configure_uploads(app, photos) configure_uploads(app, photos)
logo() logo()
register_shell(app)
return app return app
def register_shell(app: Flask):
@app.cli.command()
def create():
"""测试关系"""
from .extensions import db
from .models import Power, User
from .models import PowerSchema
power_schema = PowerSchema(many=True) # 用已继承 ma.ModelSchema 类的自定制类生成序列化类
user = User.query.get(1)
power_list = []
for role in user.role:
if role.enable:
for power in role.power:
# 权限被启用
if power.enable and power:
power_list.append(power)
power_dict = sorted(power_list, key=lambda i: i['sort'])
def logo(): def logo():
print(''' print('''
_____ _ _ ______ _ _ _____ _ _ ______ _ _
-87
View File
@@ -1,87 +0,0 @@
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import Dept, DeptSchema
from applications.models import User
from applications.common.curd import model_to_dicts
def get_dept_dict():
dept = Dept.query.order_by(Dept.sort).all()
res = model_to_dicts(Schema=DeptSchema, model=dept)
return res
def save_dept(req):
address = xss_escape(req.get("address"))
deptName = xss_escape(req.get("deptName"))
email = xss_escape(req.get("email"))
leader = xss_escape(req.get("leader"))
parentId = xss_escape(req.get("parentId"))
phone = xss_escape(req.get("phone"))
sort = xss_escape(req.get("sort"))
status = xss_escape(req.get("status"))
dept = Dept(
parent_id=parentId,
dept_name=deptName,
sort=sort,
leader=leader,
phone=phone,
email=email,
status=status,
address=address
)
r = db.session.add(dept)
db.session.commit()
return r
def get_dept_by_id(id):
d = Dept.query.filter_by(id=id).first()
return d
# 启动权限
def enable_status(id):
enable = 1
d = Dept.query.filter_by(id=id).update({"status": enable})
if d:
db.session.commit()
return True
return False
# 停用权限
def disable_status(id):
enable = 0
d = Dept.query.filter_by(id=id).update({"status": enable})
if d:
db.session.commit()
return True
return False
def update_dept(json):
id = json.get("deptId"),
data = {
"dept_name": xss_escape(json.get("deptName")),
"sort": xss_escape(json.get("sort")),
"leader": xss_escape(json.get("leader")),
"phone": xss_escape(json.get("phone")),
"email": xss_escape(json.get("email")),
"status": xss_escape(json.get("status")),
"address": xss_escape(json.get("address"))
}
d = Dept.query.filter_by(id=id).update(data)
if not d:
return False
db.session.commit()
return True
def remove_dept(id):
d = Dept.query.filter_by(id=id).delete()
if not d:
return False
User.query.filter_by(dept_id=id).update({"dept_id": None})
db.session.commit()
return True
-145
View File
@@ -1,145 +0,0 @@
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import DictType, DictData, DictTypeSchema, DictDataSchema
from applications.common.curd import model_to_dicts
def get_dict(typecode: str):
dict_list = []
if DictType.query.filter_by(type_code=typecode, enable=1).first():
dicts = DictData.query.filter_by(type_code=typecode, enable=1).all()
for d in dicts:
dict_dict = {"key": d.data_label, "value": d.data_value}
dict_list.append(dict_dict)
else:
return None
return dict_list
def get_dict_type(page, limit, type_name):
dict_all = DictType.query
if type_name:
dict_all = dict_all.filter(DictType.type_name.like('%' + type_name + '%'))
dict_all = dict_all.paginate(page=page,
per_page=limit,
error_out=False)
count = DictType.query.count()
data = model_to_dicts(Schema=DictTypeSchema, model=dict_all.items)
return data, count
def get_dict_data(page, limit, type_code):
dict_all = DictData.query.filter_by(type_code=type_code).paginate(page=page,
per_page=limit,
error_out=False)
count = DictType.query.count()
dict_dict = model_to_dicts(Schema=DictDataSchema, model=dict_all.items)
return dict_dict, count
# 增加 dict type
def save_dict_type(req_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()
return d.id
# 编辑字典类型
def update_dict_type(req_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
def enable_dict_type_status(id):
enable = 1
res = DictType.query.filter_by(id=id).update({"enable": enable})
if res:
db.session.commit()
return True
return False
def disable_dict_type_status(id):
enable = 0
res = DictType.query.filter_by(id=id).update({"enable": enable})
if res:
db.session.commit()
return True
return False
# 删除字典类型
def delete_type_by_id(id):
type_code = DictType.query.filter_by(id=id).first().type_code
DictData.query.filter_by(type_code=type_code).delete()
res = DictType.query.filter_by(id=id).delete()
db.session.commit()
return res
# 增加dictdata
def save_dict_data(req_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()
return d.id
# 编辑字典数据
def update_dict_data(req_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
def enable_dict_data_status(id):
enable = 1
res = DictData.query.filter_by(id=id).update({"enable": enable})
if res:
db.session.commit()
return True
return False
def disable_dict_data_status(id):
enable = 0
res = DictData.query.filter_by(id=id).update({"enable": enable})
if res:
db.session.commit()
return True
return False
# 删除dictdata
def delete_data_by_id(id):
res = DictData.query.filter_by(id=id).delete()
db.session.commit()
return res
-112
View File
@@ -1,112 +0,0 @@
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models.admin_power import Power, PowerSchema2
from applications.models import Role
from applications.common.curd import model_to_dicts
def get_power_dict():
power = Power.query.all()
res = model_to_dicts(Schema=PowerSchema2, model=power)
return res
# 选择父节点
def select_parent():
power = Power.query.all()
res = model_to_dicts(Schema=PowerSchema2, model=power)
res.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
return res
# 增加权限
def save_power(req):
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 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": 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"))
}
# 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)
-149
View File
@@ -1,149 +0,0 @@
from sqlalchemy import and_
from applications.common.utils.validate import xss_escape
from applications.extensions import db
from applications.models import Role, RoleSchema
from applications.models.admin_power import Power, PowerSchema2
from applications.models import User
# 获取角色对象
from applications.common.curd import model_to_dicts
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)
data = model_to_dicts(Schema=RoleSchema, model=role.items)
return data, count
# 增加角色
def add_role(req):
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()
# 通过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": 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()
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 = PowerSchema2(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)
-159
View File
@@ -1,159 +0,0 @@
from flask import jsonify
from flask_login import current_user
from sqlalchemy import and_, desc
from applications.extensions import db
from applications.models import User, UserSchema
from applications.models import Role
from applications.models import AdminLog
# 获取用户的 sqlalchemy 对象分页器
from applications.common.curd import model_to_dicts
def get_user_data(page, limit, filters, deptId):
if deptId:
user = User.query.filter_by(dept_id=deptId).filter(
and_(*[getattr(User, k).like(v) for k, v in filters.items()])).paginate(page=page,
per_page=limit,
error_out=False)
else:
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, deptId):
user, count = get_user_data(page, limit, filters, deptId)
data = model_to_dicts(Schema=UserSchema, model=user.items)
return data, count
# 通过名称获取用户
def get_user_by_name(username):
return User.query.filter_by(username=username).first()
# 获取当前用户日志
def get_current_user_logs():
log = AdminLog.query.filter_by(url='/admin/login').filter_by(uid=current_user.id).order_by(
desc(AdminLog.create_time)).limit(10)
return log
# 判断用户是否存在
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_avatar(url):
r = User.query.filter_by(id=current_user.id).update({"avatar": url})
db.session.commit()
return r
# 更新用户信息
def update_user(id, username, realname, deptId):
user = User.query.filter_by(id=id).update({'username': username, 'realname': realname, 'dept_id': deptId})
db.session.commit()
return user
# 更新当前用户信息
def update_current_user_info(req_json):
r = User.query.filter_by(id=current_user.id).update(
{"realname": req_json.get("realName"), "remark": req_json.get("details")})
db.session.commit()
return r
# 修改当前用户密码
def edit_password(res_json):
if res_json.get("newPassword") == '':
return jsonify(success=False, msg="新密码不得为空")
if res_json.get("newPassword") != res_json.get("confirmPassword"):
return jsonify(success=False, msg="俩次密码不一样")
user = current_user
is_right = user.validate_password(res_json.get("oldPassword"))
if not is_right:
return jsonify(success=False, msg="旧密码错误")
user.set_password(res_json.get("newPassword"))
db.session.add(user)
db.session.commit()
return jsonify(success=True, msg="更改成功")
# 删除用户
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()
@@ -0,0 +1,76 @@
from flask_restful import fields
power_fields = {
'powerId': fields.String(attribute="id"),
'powerName': fields.String(attribute="name"),
'powerType': fields.String(attribute="type"),
'powerUrl': fields.String(attribute="url"),
'openType': fields.String(attribute="open_type"),
'parentId': fields.String(attribute="parent_id"),
'icon': fields.String,
'sort': fields.Integer,
'create_time': fields.DateTime,
'update_time': fields.DateTime,
'enable': fields.Integer,
}
power2_fields = {
'id': fields.Integer,
'title': fields.String(attribute="name"),
'type': fields.String,
'code': fields.String,
'href': fields.String(attribute="url"),
'openType': fields.String(attribute="open_type"),
'parent_id': fields.Integer,
'icon': fields.String,
'sort': fields.Integer,
'create_time': fields.DateTime,
'update_time': fields.DateTime,
'enable': fields.Integer,
}
role_fields = {
'id': fields.Integer,
'roleName': fields.String(attribute="name"),
'roleCode': fields.String(attribute="code"),
'enable': fields.String,
'remark': fields.String,
'details': fields.String,
'sort': fields.Integer,
'create_at': fields.DateTime,
'update_at': fields.DateTime,
}
log_fields = {
'id': fields.Integer,
'method': fields.String,
'uid': fields.String,
'url': fields.String,
'desc': fields.String,
'ip': fields.String,
'user_agent': fields.String,
'success': fields.Boolean,
'create_time': fields.DateTime,
}
dept_fields = {
'deptId': fields.Integer(attribute="id"),
'parentId': fields.Integer(attribute="parent_id"),
'deptName': fields.String(attribute="dept_name"),
'leader': fields.String,
'phone': fields.String,
'email': fields.String,
'address': fields.String,
'status': fields.Integer,
'sort': fields.Integer,
}
photo_fields = {
'id': fields.Integer,
'name': fields.String,
'href': fields.String,
'mime': fields.String,
'size': fields.String,
'ext': fields.String,
'create_time': fields.DateTime,
}
-14
View File
@@ -1,14 +0,0 @@
from captcha.image import ImageCaptcha
from PIL import Image
from random import choices
def gen_captcha(content='0123456789'):
""" 生成验证码 """
image = ImageCaptcha()
# 获取字符串
captcha_text = "".join(choices(content, k=4))
# 生成图像
captcha_image = Image.open(image.generate(captcha_text))
return captcha_text, captcha_image
+1 -1
View File
@@ -9,7 +9,7 @@ def authorize(power: str, log: bool = False):
@login_required @login_required
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if not power in session.get('permissions'): if power not in session.get('permissions'):
if log: if log:
admin_log(request=request, is_access=False) admin_log(request=request, is_access=False)
if request.method == 'GET': if request.method == 'GET':
-8
View File
@@ -2,14 +2,6 @@
from flask import abort, make_response, jsonify 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): def check_data(schema, data):
errors = schema.validate(data) errors = schema.validate(data)
for k, v in errors.items(): for k, v in errors.items():
+3
View File
@@ -31,9 +31,12 @@ class TestingConfig(BaseConfig):
class DevelopmentConfig(BaseConfig): class DevelopmentConfig(BaseConfig):
""" 开发配置 """ """ 开发配置 """
SQLALCHEMY_DATABASE_URI = r'sqlite:///../sql_pear_admin.db'
SQLALCHEMY_TRACK_MODIFICATIONS = True SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = False SQLALCHEMY_ECHO = False
UPLOADED_PHOTOS_DEST = os.path.join(os.path.dirname(os.path.abspath(__name__)), 'applications', 'static', 'upload')
class ProductionConfig(BaseConfig): class ProductionConfig(BaseConfig):
"""生成环境配置""" """生成环境配置"""
+3 -3
View File
@@ -1,15 +1,15 @@
from flask import Flask from flask import Flask
from .init_sqlalchemy import db, ma, init_databases from .init_databases import register_script
from .init_sqlalchemy import db, init_databases
from .init_login import init_login_manager 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_template_directives import init_template_directives
from .init_error_views import init_error_views from .init_error_views import init_error_views
def init_plugs(app: Flask) -> None: def init_plugs(app: Flask) -> None:
# init_debug_tool(app)
init_login_manager(app) init_login_manager(app)
init_databases(app) init_databases(app)
init_template_directives(app) init_template_directives(app)
init_error_views(app) init_error_views(app)
register_script(app)
@@ -0,0 +1,276 @@
from flask import Flask
import re
from datetime import datetime
date_str = re.compile('\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$')
def add_data(fields, data_list, obj):
from applications.extensions import db
for _data in data_list:
dept = obj()
for key, value in dict(zip(fields, _data)).items():
if isinstance(value, str) and date_str.match(value):
value = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
setattr(dept, key, value)
db.session.add(dept)
db.session.commit()
def create_dept_data():
"""创建化部门数据"""
from applications.models import Dept
_data_list = [
(1, 0, '总公司', 1, '就眠仪式', '12312345679', '123qq.com', 1, None, '这是总公司', None, '2021-06-01 17:23:20'),
(4, 1, '济南分公司', 2, '就眠仪式', '12312345678', '1234qq.com', 1, None, '这是济南', '2021-06-01 17:24:33',
'2021-06-01 17:25:19'),
(5, 1, '唐山分公司', 4, 'mkg', '12312345678', '123@qq.com', 1, None, '这是唐山', '2021-06-01 17:25:15',
'2021-06-01 17:25:20'),
(7, 4, '济南分公司开发部', 5, '就眠仪式', '12312345678', '123@qq.com', 1, None, '测试', '2021-06-01 17:27:39',
'2021-06-01 17:27:39'),
(8, 5, '唐山测试部', 6, 'mkg', '12312345678', '123@qq.com', 1, None, '测试部', '2021-06-01 17:28:27',
'2021-06-01 17:28:27'),
]
_fields = ['id', 'parent_id', 'dept_name', 'sort', 'leader', 'phone', 'email', 'status', 'remark', 'address',
'create_at', 'update_at']
_data_list = [item for item in _data_list]
add_data(_fields, _data_list, Dept)
def create_admin_photo():
from applications.models import Photo
_fields = [
'id',
'name',
'href',
'mime',
'size',
'create_time',
]
_data_list = [
(3, '6958819_pear-admin_1607443454_1.png',
'http://127.0.0.1:5000/_uploads/photos/6958819_pear-admin_1607443454_1.png', 'image/png', '2204',
'2021-03-19 18:53:02'),
(17, '1617291580000.jpg', 'http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', 'image/png', '94211',
'2021-04-01 23:39:41'),
]
add_data(_fields, _data_list, Photo)
def create_admin_power():
from applications.models import Power
_data_list = \
[
(1, '系统管理', '0', '', None, None, '0', 'layui-icon layui-icon-set-fill', 1, None, None, 1),
(3, '用户管理', '1', 'admin:user:main', '/users/', '_iframe', '1',
'layui-icon layui-icon layui-icon layui-icon layui-icon-rate', 1, None, None, 1),
(4, '权限管理', '1', 'admin:power:main', '/rights/', '_iframe', '1', None, 2, None, None, 1),
(9, '角色管理', '1', 'admin:role:main', '/admin/role', '_iframe', '1', 'layui-icon layui-icon-username', 2,
'2021-03-16 22:24:58', '2021-03-25 19:15:24', 1),
(13, '日志管理', '1', 'admin:log:main', '/logs', '_iframe', '1', 'layui-icon layui-icon-read', 4,
'2021-03-18 22:37:10', '2021-06-03 11:06:25', 1),
(17, '文件管理', '0', '', '', '', '0', 'layui-icon layui-icon-camera', 2, '2021-03-19 18:56:23',
'2021-03-25 19:15:08', 1),
(18, '图片上传', '1', 'admin:file:main', '/file', '_iframe', '17', 'layui-icon layui-icon-camera', 5,
'2021-03-19 18:57:19', '2021-03-25 19:15:13', 1),
(21, '权限增加', '2', 'admin:power:add', '', '', '4', 'layui-icon layui-icon-add-circle', 1,
'2021-03-22 19:43:52',
'2021-03-25 19:15:22', 1),
(22, '用户增加', '2', 'admin:user:add', '', '', '3', 'layui-icon layui-icon-add-circle', 1,
'2021-03-22 19:45:40',
'2021-03-25 19:15:17', 1),
(23, '用户编辑', '2', 'admin:user:edit', '', '', '3', 'layui-icon layui-icon-rate', 2, '2021-03-22 19:46:15',
'2021-03-25 19:15:18', 1),
(24, '用户删除', '2', 'admin:user:remove', '', '', '3', 'layui-icon None', 3, '2021-03-22 19:46:51',
'2021-03-25 19:15:18', 1),
(25, '权限编辑', '2', 'admin:power:edit', '', '', '4', 'layui-icon layui-icon-edit', 2, '2021-03-22 19:47:36',
'2021-03-25 19:15:22', 1),
(26, '用户删除', '2', 'admin:power:remove', '', '', '4', 'layui-icon layui-icon-delete', 3,
'2021-03-22 19:48:17',
'2021-03-25 19:15:23', 1),
(27, '用户增加', '2', 'admin:role:add', '', '', '9', 'layui-icon layui-icon-add-circle', 1,
'2021-03-22 19:49:09',
'2021-03-25 19:15:24', 1),
(28, '角色编辑', '2', 'admin:role:edit', '', '', '9', 'layui-icon layui-icon-edit', 2, '2021-03-22 19:49:41',
'2021-03-25 19:15:25', 1),
(
29, '角色删除', '2', 'admin:role:remove', '', '', '9', 'layui-icon layui-icon-delete', 3,
'2021-03-22 19:50:15',
'2021-03-25 19:15:26', 1),
(30, '角色授权', '2', 'admin:role:power', '', '', '9', 'layui-icon layui-icon-component', 4,
'2021-03-22 19:50:54',
'2021-03-25 19:15:26', 1),
(31, '图片增加', '2', 'admin:file:add', '', '', '18', 'layui-icon layui-icon-add-circle', 1,
'2021-03-22 19:58:05',
'2021-03-25 19:15:28', 1),
(32, '图片删除', '2', 'admin:file:delete', '', '', '18', 'layui-icon layui-icon-delete', 2,
'2021-03-22 19:58:45',
'2021-03-25 19:15:29', 1),
(48, '部门管理', '1', 'admin:dept:main', '/dept', '_iframe', '1', 'layui-icon layui-icon-group', 3,
'2021-06-01 16:22:11', '2021-06-01 16:22:11', 1),
(49, '部门增加', '2', 'admin:dept:add', '', '', '48', 'layui-icon None', 1, '2021-06-01 17:35:52',
'2021-06-01 17:36:15', 1),
(50, '部门编辑', '2', 'admin:dept:edit', '', '', '48', 'layui-icon ', 2, '2021-06-01 17:36:41',
'2021-06-01 17:36:41', 1),
(51, '部门删除', '2', 'admin:dept:remove', '', '', '48', 'layui-icon None', 3, '2021-06-01 17:37:15',
'2021-06-01 17:37:26', 1),
]
_fields = [
'id',
'name',
'type',
'code',
'url',
'open_type',
'parent_id',
'icon',
'sort',
'create_time',
'update_time',
'enable',
]
add_data(_fields, _data_list, Power)
def create_admin_role():
from applications.models import Role
_fields = [
'id',
'name',
'code',
'remark',
'details',
'sort',
'create_time',
'update_time',
'enable',
]
_data_list = [
(1, '管理员', 'admin', None, '管理员', 1, None, None, 1),
(2, '普通用户', 'common', None, '只有查看,没有增删改权限', 2, '2021-03-22 20:02:38', '2021-04-01 22:29:56', 1),
]
add_data(_fields, _data_list, Role)
def create_admin_role_power():
from applications.extensions import db
_data_list = [
(237, 1, 1),
(238, 3, 1),
(239, 4, 1),
(240, 9, 1),
(241, 12, 1),
(242, 13, 1),
(243, 17, 1),
(244, 18, 1),
(245, 21, 1),
(246, 22, 1),
(247, 23, 1),
(248, 24, 1),
(249, 25, 1),
(250, 26, 1),
(251, 27, 1),
(252, 28, 1),
(253, 29, 1),
(254, 30, 1),
(255, 31, 1),
(256, 32, 1),
(257, 44, 1),
(258, 45, 1),
(259, 46, 1),
(260, 47, 1),
(261, 48, 1),
(262, 49, 1),
(263, 50, 1),
(264, 51, 1),
(265, 1, 2),
(266, 3, 2),
(267, 4, 2),
(268, 9, 2),
(269, 12, 2),
(270, 13, 2),
(271, 17, 2),
(272, 18, 2),
(273, 44, 2),
(274, 48, 2),
]
# add_data(_fields, _data_list, UserRole)
for data in _data_list:
db.session.execute('insert into rt_role_power VALUES (%s, %s, %s);' % data)
db.session.commit()
def create_admin_user():
from applications.models import User
_fields = [
'id',
'username',
'password_hash',
'create_at',
'update_at',
'enable',
'realname',
'remark',
'avatar',
'dept_id',
]
_data_list = [
(1, 'admin', 'pbkdf2:sha256:150000$raM7mDSr$58fe069c3eac01531fc8af85e6fc200655dd2588090530084d182e6ec9d52c85',
None, '2021-06-01 17:28:55', 1, '超级管理', '要是不能把握时机,就要终身蹭蹬,一事无成!',
'http://127.0.0.1:5000/_uploads/photos/1617291580000.jpg', 1),
(7, 'test', 'pbkdf2:sha256:150000$cRS8bYNh$adb57e64d929863cf159f924f74d0634f1fecc46dba749f1bfaca03da6d2e3ac',
'2021-03-22 20:03:42', '2021-06-01 17:29:47', 1, '超级管理', '要是不能把握时机,就要终身蹭蹬,一事无成',
'/static/admin/admin/images/avatar.jpg', 1),
(8, 'wind', 'pbkdf2:sha256:150000$skME1obT$6a2c20cd29f89d7d2f21d9e373a7e3445f70ebce3ef1c3a555e42a7d17170b37',
'2021-06-01 17:30:39', '2021-06-01 17:30:52', 1, '', None, '/static/admin/admin/images/avatar.jpg', 7),
]
add_data(_fields, _data_list, User)
def create_admin_user_role() -> object:
from applications.extensions import db
_data_list = [
(21, 1, 1),
(22, 7, 2),
(24, 8, 2),
]
# add_data(_fields, _data_list, UserRole)
for data in _data_list:
db.session.execute('insert into rt_user_role VALUES (%s, %s, %s);' % data)
db.session.commit()
def register_script(app: Flask):
@app.cli.command()
def init_db():
"""数据库初始化"""
create_dept_data()
create_admin_photo()
create_admin_power()
create_admin_role()
create_admin_role_power()
create_admin_user()
create_admin_user_role()
@app.cli.command()
def turn():
"""清空数据库"""
from applications.extensions import db
db.drop_all()
db.create_all()
@@ -1,7 +0,0 @@
from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension
def init_debug_tool(app: Flask):
toolbar = DebugToolbarExtension()
toolbar.init_app(app)
+3 -46
View File
@@ -1,54 +1,11 @@
from flask import Flask from flask import Flask
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow from flask_migrate import Migrate
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": "不是合法布尔值"
}
db = SQLAlchemy() db = SQLAlchemy()
ma = Marshmallow() migrate = Migrate()
def init_databases(app: Flask): def init_databases(app: Flask):
db.init_app(app) db.init_app(app)
ma.init_app(app) migrate.init_app(app, db)
+7 -9
View File
@@ -1,9 +1,7 @@
from .admin_dept import Dept, DeptSchema from applications.models.company import Dept, User
from .admin_dict import DictType, DictData, DictTypeSchema, DictDataSchema from applications.models.log import AdminLog
from .admin_log import AdminLog, LogSchema from applications.models.file.photo import Photo
from .admin_photo import Photo, PhotoSchema from applications.models.rights.power import Power
from .admin_power import Power, PowerSchema, PowerSchema2 from applications.models.rights.role import Role
from .admin_role import Role, RoleSchema from applications.models.rights.role_power import role_power
from .admin_role_power import role_power from applications.models.rights.user_role import user_role
from .admin_user import User, UserSchema
from .admin_user_role import user_role
-47
View File
@@ -1,47 +0,0 @@
import datetime
from applications.extensions import db, ma
from marshmallow import fields
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='更新时间')
class DictTypeSchema(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 DictDataSchema(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()
-28
View File
@@ -1,28 +0,0 @@
import datetime
from applications.extensions import db, ma
from marshmallow import fields
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 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()
-49
View File
@@ -1,49 +0,0 @@
import datetime
from applications.extensions import db, ma
from marshmallow import fields
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='是否开启')
# 权限models序列化类
class PowerSchema(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 PowerSchema2(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()
-9
View File
@@ -1,9 +0,0 @@
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='角色编号'), # 属性 外键
)
-9
View File
@@ -1,9 +0,0 @@
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='角色编号'), # 属性 外键
)
+2
View File
@@ -0,0 +1,2 @@
from .dept import Dept
from .users import User
@@ -1,10 +1,9 @@
import datetime import datetime
from applications.extensions import db, ma from applications.extensions import db
from marshmallow import fields, validate
class Dept(db.Model): class Dept(db.Model):
__tablename__ = 'admin_dept' __tablename__ = 'cp_dept'
id = db.Column(db.Integer, primary_key=True, comment="部门ID") id = db.Column(db.Integer, primary_key=True, comment="部门ID")
parent_id = db.Column(db.Integer, comment="父级编号") parent_id = db.Column(db.Integer, comment="父级编号")
dept_name = db.Column(db.String(50), comment="部门名称") dept_name = db.Column(db.String(50), comment="部门名称")
@@ -17,15 +16,3 @@ class Dept(db.Model):
address = db.Column(db.String(255), comment="详细地址") address = db.Column(db.String(255), comment="详细地址")
create_at = db.Column(db.DateTime, default=datetime.datetime.now, 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='创建时间') update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
class DeptSchema(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()
@@ -1,16 +1,15 @@
import datetime import datetime
from flask_login import UserMixin from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from applications.extensions import db, ma from applications.extensions import db
from marshmallow import fields
from applications.models import Dept
class User(db.Model, UserMixin): class User(db.Model, UserMixin):
__tablename__ = 'admin_user' __tablename__ = 'cp_user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='用户ID') id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='用户ID')
username = db.Column(db.String(20), comment='用户名') username = db.Column(db.String(20), comment='用户名')
realname = db.Column(db.String(20), comment='真实名字') realname = db.Column(db.String(20), comment='真实名字')
mobile = db.Column(db.String(11), comment='电话号码')
avatar = db.Column(db.String(255), comment='头像', default="/static/admin/admin/images/avatar.jpg") avatar = db.Column(db.String(255), comment='头像', default="/static/admin/admin/images/avatar.jpg")
remark = db.Column(db.String(255), comment='备注') remark = db.Column(db.String(255), comment='备注')
password_hash = db.Column(db.String(128), comment='哈希密码') password_hash = db.Column(db.String(128), comment='哈希密码')
@@ -18,7 +17,8 @@ class User(db.Model, UserMixin):
dept_id = db.Column(db.Integer, comment='部门id') dept_id = db.Column(db.Integer, comment='部门id')
create_at = db.Column(db.DateTime, default=datetime.datetime.now, 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='创建时间') 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')
role = db.relationship('Role', secondary="rt_user_role", backref=db.backref('user'), lazy='dynamic')
def set_password(self, password): def set_password(self, password):
self.password_hash = generate_password_hash(password) self.password_hash = generate_password_hash(password)
@@ -26,19 +26,3 @@ class User(db.Model, UserMixin):
def validate_password(self, password): def validate_password(self, password):
return check_password_hash(self.password_hash, password) return check_password_hash(self.password_hash, password)
# 用户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()
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
@@ -1,11 +1,10 @@
import datetime import datetime
from applications.extensions import db, ma from applications.extensions import db
from marshmallow import fields
class Photo(db.Model): class Photo(db.Model):
__tablename__ = 'admin_photo' __tablename__ = 'file_photo'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False) name = db.Column(db.String(255), nullable=False)
href = db.Column(db.String(255)) href = db.Column(db.String(255))
@@ -13,12 +12,3 @@ class Photo(db.Model):
size = db.Column(db.CHAR(30), nullable=False) size = db.Column(db.CHAR(30), nullable=False)
create_time = db.Column(db.DateTime, default=datetime.datetime.now) create_time = db.Column(db.DateTime, default=datetime.datetime.now)
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()
+15
View File
@@ -0,0 +1,15 @@
from datetime 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.now)
+20
View File
@@ -0,0 +1,20 @@
import datetime
from applications.extensions import db
class Power(db.Model):
__tablename__ = 'rt_power'
id = db.Column(db.Integer, primary_key=True, comment='权限编号')
name = db.Column(db.String(255), comment='权限名称')
type = db.Column(db.SMALLINT, 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, db.ForeignKey("rt_power.id"), 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='是否开启')
parent = db.relationship("Power", remote_side=[id]) # 自关联
@@ -1,10 +1,9 @@
import datetime import datetime
from applications.extensions import db, ma from applications.extensions import db
from marshmallow import fields
class Role(db.Model): class Role(db.Model):
__tablename__ = 'admin_role' __tablename__ = 'rt_role'
id = db.Column(db.Integer, primary_key=True, comment='角色ID') id = db.Column(db.Integer, primary_key=True, comment='角色ID')
name = db.Column(db.String(255), comment='角色名称') name = db.Column(db.String(255), comment='角色名称')
code = db.Column(db.String(255), comment='角色标识') code = db.Column(db.String(255), comment='角色标识')
@@ -14,16 +13,4 @@ class Role(db.Model):
sort = db.Column(db.Integer, comment='排序') sort = db.Column(db.Integer, comment='排序')
create_time = db.Column(db.DateTime, default=datetime.datetime.now, 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='更新时间') 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')) power = db.relationship('Power', secondary="rt_role_power", backref=db.backref('role'))
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()
+9
View File
@@ -0,0 +1,9 @@
from applications.extensions import db
# 创建中间表
role_power = db.Table(
"rt_role_power", # 中间表名称
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
db.Column("power_id", db.Integer, db.ForeignKey("rt_power.id"), comment='用户编号'), # 属性 外键
db.Column("role_id", db.Integer, db.ForeignKey("rt_role.id"), comment='角色编号'), # 属性 外键
)
+9
View File
@@ -0,0 +1,9 @@
from applications.extensions import db
# 创建中间表
user_role = db.Table(
"rt_user_role", # 中间表名称
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
db.Column("user_id", db.Integer, db.ForeignKey("cp_user.id"), comment='用户编号'), # 属性 外键
db.Column("role_id", db.Integer, db.ForeignKey("rt_role.id"), comment='角色编号'), # 属性 外键
)
@@ -12,14 +12,14 @@
<div class="layui-tab layui-tab-card"> <div class="layui-tab layui-tab-card">
<ul class="layui-tab-title"> <ul class="layui-tab-title">
<li class="layui-this">登录日志</li> <li class="layui-this">登录日志</li>
<li>操作日志</li> <li>访问日志</li>
</ul> </ul>
<div class="layui-tab-content"> <div class="layui-tab-content">
<div class="layui-tab-item layui-show"> <div class="layui-tab-item layui-show">
<table style="margin-top: 10px;" id="log-login-table" lay-filter="log-login-table"></table> <table style="margin-top: 10px;" id="log-login-table" lay-filter="log-login-table"></table>
</div> </div>
<div class="layui-tab-item"> <div class="layui-tab-item">
<table style="margin-top: 10px;" id="log-operate-table" lay-filter="log-operate-table"></table> <table style="margin-top: 10px;" id="log-access-table" lay-filter="log-access-table"></table>
</div> </div>
</div> </div>
</div> </div>
@@ -44,7 +44,7 @@
let table = layui.table let table = layui.table
let form = layui.form let form = layui.form
let MODULE_PATH = '/admin/log/' let MODULE_PATH = '/logs/'
let cols = [ let cols = [
[ [
@@ -61,8 +61,8 @@
] ]
table.render({ table.render({
elem: '#log-operate-table', elem: '#log-access-table',
url: MODULE_PATH + 'operateLog', url: MODULE_PATH + 'access_log',
page: true, page: true,
cols: cols, cols: cols,
skin: 'line', skin: 'line',
@@ -71,7 +71,7 @@
table.render({ table.render({
elem: '#log-login-table', elem: '#log-login-table',
url: MODULE_PATH + 'loginLog', url: MODULE_PATH + 'login_log',
page: true, page: true,
cols: cols, cols: cols,
skin: 'line', skin: 'line',
@@ -12,6 +12,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">上级部门</label> <label class="layui-form-label">上级部门</label>
<div class="layui-input-block"> <div class="layui-input-block">
{#渲染树形选择器#}
<ul id="selectParent" name="parentId" class="dtree" data-id="0"></ul> <ul id="selectParent" name="parentId" class="dtree" data-id="0"></ul>
</div> </div>
</div> </div>
@@ -86,6 +87,7 @@
let $ = layui.jquery let $ = layui.jquery
let dtree = layui.dtree let dtree = layui.dtree
/*渲染下拉选择组件*/
dtree.renderSelect({ dtree.renderSelect({
elem: '#selectParent', elem: '#selectParent',
url: '/dept/tree', url: '/dept/tree',
@@ -97,9 +99,10 @@
selectInitVal: '1' selectInitVal: '1'
}) })
/*表单组件,提交按钮之后触发的事件*/
form.on('submit(dept-save)', function (data) { form.on('submit(dept-save)', function (data) {
$.ajax({ $.ajax({
url: '/dept/save', url: '/dept/add',
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@@ -97,9 +97,10 @@
let form = layui.form let form = layui.form
let $ = layui.jquery let $ = layui.jquery
// 当点击更新之后,获取数据
form.on('submit(dept-update)', function (data) { form.on('submit(dept-update)', function (data) {
$.ajax({ $.ajax({
url: '/dept/update', url: '/dept/edit' + data.field.deptId,
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@@ -107,7 +108,7 @@
success: function (result) { success: function (result) {
if (result.success) { if (result.success) {
layer.msg(result.msg, { icon: 1, time: 1000 }, function () { layer.msg(result.msg, { icon: 1, time: 1000 }, function () {
parent.layer.close(parent.layer.getFrameIndex(window.name))//关闭当前页 parent.layer.close(parent.layer.getFrameIndex(window.name)) //关闭当前页
parent.render() parent.render()
}) })
} else { } else {
@@ -4,19 +4,24 @@
<title>部门新增</title> <title>部门新增</title>
{% include 'admin/common/header.html' %} {% include 'admin/common/header.html' %}
</head> </head>
{#pear 后台边框#}
<body class="pear-container"> <body class="pear-container">
{#搜索框布局#}
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-body"> <div class="layui-card-body">
<form class="layui-form" action=""> <form class="layui-form" action="">
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">部门名称</label> <label class="layui-form-label">部门名称</label>
<div class="layui-input-inline"> <div class="layui-input-inline">
{#部门信息搜索字段#}
<input type="text" name="deptName" placeholder="" class="layui-input"> <input type="text" name="deptName" placeholder="" class="layui-input">
</div> </div>
{#查询按钮--需要触发layui事件#}
<button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="dept-query"> <button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="dept-query">
<i class="layui-icon layui-icon-search"></i> <i class="layui-icon layui-icon-search"></i>
查询 查询
</button> </button>
{#重置按钮#}
<button type="reset" class="pear-btn pear-btn-md"> <button type="reset" class="pear-btn pear-btn-md">
<i class="layui-icon layui-icon-refresh"></i> <i class="layui-icon layui-icon-refresh"></i>
重置 重置
@@ -28,10 +33,12 @@
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-body"> <div class="layui-card-body">
{#表格字段渲染#}
<table id="dept-table" lay-filter="dept-table"></table> <table id="dept-table" lay-filter="dept-table"></table>
</div> </div>
</div> </div>
{#表格工具栏,需要在表格中绑定,且需要自定义事件#}
<script type="text/html" id="dept-toolbar"> <script type="text/html" id="dept-toolbar">
{% if authorize("admin:dept:edit") %} {% if authorize("admin:dept:edit") %}
<button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add"> <button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add">
@@ -47,19 +54,17 @@
{% endif %} {% endif %}
</script> </script>
{#操作字段,需要在表格中使用#}
<script type="text/html" id="dept-bar"> <script type="text/html" id="dept-bar">
{% if authorize("admin:dept:edit") %} <button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit">
<button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit"><i <i class="layui-icon layui-icon-edit"></i>
class="layui-icon layui-icon-edit"></i> </button>
</button> <button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove">
{% endif %} <i class="layui-icon layui-icon-delete"></i>
{% if authorize("admin:dept:remove") %} </button>
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i
class="layui-icon layui-icon-delete"></i>
</button>
{% endif %}
</script> </script>
{#表格内的切换按钮#}
<script type="text/html" id="dept-status"> <script type="text/html" id="dept-status">
<input type="checkbox" name="status" value="{{ "{{ d.deptId }}" }}" lay-skin="switch" lay-text="启用|禁用" <input type="checkbox" name="status" value="{{ "{{ d.deptId }}" }}" lay-skin="switch" lay-text="启用|禁用"
lay-filter="dept-enable" lay-filter="dept-enable"
@@ -78,20 +83,23 @@
let treetable = layui.treetable let treetable = layui.treetable
let popup = layui.popup let popup = layui.popup
{#定义模块访问路径#}
let MODULE_PATH = '/dept/' let MODULE_PATH = '/dept/'
{#定义表格渲染函数#}
window.render = function () { window.render = function () {
/*树形表格渲染*/
treetable.render({ treetable.render({
treeColIndex: 1, treeColIndex: 1,
treeSpid: 0, treeSpid: 0,
treeIdName: 'deptId', treeIdName: 'deptId', /*表格的顶级字段*/
treePidName: 'parentId', treePidName: 'parentId', /*父级id选项*/
skin: 'line', skin: 'line',
method: 'post', method: 'post', /**/
treeDefaultClose: false, treeDefaultClose: false,
toolbar: '#dept-toolbar', toolbar: '#dept-toolbar', /*表格的工具栏*/
elem: '#dept-table', elem: '#dept-table', /*表格挂载的对象*/
url: '/dept/data', url: '/dept/data', /*数据来源接口*/
page: false, page: false,
cols: [ cols: [
[ [
@@ -105,19 +113,22 @@
{ field: 'sort', title: '排序', width: 100 }, { field: 'sort', title: '排序', width: 100 },
{ title: '操作', templet: '#dept-bar', width: 120, align: 'center' } { title: '操作', templet: '#dept-bar', width: 120, align: 'center' }
] ]
] ]/*表格渲染的数据*/
}) })
} }
/*查询表单按钮被点击的事件*/
form.on('submit(dept-query)', function (data) { form.on('submit(dept-query)', function (data) {
var keyword = data.field.deptName let keyword = $.trim(data.field.deptName)
var $tds = $('#dept-table').next('.treeTable').find('.layui-table-body tbody tr td') /*获取所有的公司节点*/
let $tds = $('#dept-table').next('.treeTable').find('.layui-table-body tbody tr td')
if (!keyword) { if (!keyword) {
$tds.css('background-color', 'transparent') $tds.css('background-color', 'transparent')
layer.msg('请输入关键字', { icon: 5 }) layer.msg('请输入关键字', { icon: 5 })
return return false
} }
var searchCount = 0 /*本地查询并且标注高亮*/
let searchCount = 0
$tds.each(function () { $tds.each(function () {
$(this).css('background-color', 'transparent') $(this).css('background-color', 'transparent')
if ($(this).text().indexOf(keyword) >= 0) { if ($(this).text().indexOf(keyword) >= 0) {
@@ -139,36 +150,44 @@
render() render()
/*表格的操作栏*/
table.on('tool(dept-table)', function (obj) { table.on('tool(dept-table)', function (obj) {
if (obj.event === 'remove') { if (obj.event === 'remove') {
/*移出事件*/
window.remove(obj) window.remove(obj)
} else if (obj.event === 'edit') { } else if (obj.event === 'edit') {
/*编辑事件*/
window.edit(obj) window.edit(obj)
} }
}) })
/*表格的工具栏*/
table.on('toolbar(dept-table)', function (obj) { table.on('toolbar(dept-table)', function (obj) {
console.log(obj)
if (obj.event === 'add') { if (obj.event === 'add') {
window.add() window.add()
} else if (obj.event === 'refresh') { } else if (obj.event === 'refresh') {
window.refresh() window.refresh()
} else if (obj.event === 'batchRemove') { } else if (obj.event === 'batchRemove') {
{#window.batchRemove(obj)#} {#window.batchRemove(obj)#}
console.log(table.checkStatus(obj.config.id).data) layer.msg('禁止批量删除')
return false
{#console.log(table.checkStatus(obj.config.id).data)#}
} }
}) })
/*切换选择状态*/
form.on('switch(dept-enable)', function (obj) { form.on('switch(dept-enable)', function (obj) {
let operate let operate
if (obj.elem.checked) { if (obj.elem.checked) {
operate = 'enable' operate = 1
} else { } else {
operate = 'disable' operate = 0
} }
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: '/dept/' + operate, url: '/dept/enable',
data: JSON.stringify({ deptId: this.value }), data: JSON.stringify({ deptId: this.value, operate: operate }),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
type: 'put', type: 'put',
@@ -188,8 +207,8 @@
type: 2, type: 2,
title: '新增', title: '新增',
shade: 0.1, shade: 0.1,
area: ['450px', '500px'], area: ['450px', '500px'], /*新增窗口的大小*/
content: MODULE_PATH + 'add' content: MODULE_PATH + 'add' /*新增窗口从那里来*/
}) })
} }
@@ -199,7 +218,7 @@
title: '修改', title: '修改',
shade: 0.1, shade: 0.1,
area: ['450px', '500px'], area: ['450px', '500px'],
content: MODULE_PATH + 'edit?deptId=' + obj.data['deptId'] content: MODULE_PATH + 'edit/' + obj.data['deptId'] /*编辑修改窗口从那里来*/
}) })
} }
@@ -208,7 +227,7 @@
layer.close(index) layer.close(index)
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: MODULE_PATH + 'remove/' + obj.data['deptId'], url: MODULE_PATH + 'edit/' + obj.data['deptId'],
dataType: 'json', dataType: 'json',
type: 'delete', type: 'delete',
success: function (result) { success: function (result) {
@@ -1,87 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>字典增加</title>
{% include 'admin/common/header.html' %}
</head>
<body>
<form class="layui-form" action="">
<div class="mainBox">
<div class="main-container">
<div class="main-container">
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input type="text" name="typeName" lay-verify="title" autocomplete="off" placeholder="请输入名称"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">标识</label>
<div class="layui-input-block">
<input type="text" name="typeCode" lay-verify="title" autocomplete="off" placeholder="请输入标识"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" name="enable" value="1" title="开启">
<input type="radio" name="enable" value="0" title="关闭" checked>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea placeholder="请输入描述" name="description" class="layui-textarea"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="button-container">
<button type="submit" class="pear-btn pear-btn-primary pear-btn-sm" lay-submit=""
lay-filter="dict-type-save">
<i class="layui-icon layui-icon-ok"></i>
提交
</button>
<button type="reset" class="pear-btn pear-btn-sm">
<i class="layui-icon layui-icon-refresh"></i>
重置
</button>
</div>
</div>
</form>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['form', 'jquery'], function () {
let form = layui.form
let $ = layui.jquery
form.on('submit(dict-type-save)', function (data) {
$.ajax({
url: '/admin/dict/dictType/save',
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'post',
success: function (result) {
if (result.success) {
layer.msg(result.msg, { icon: 1, time: 1000 }, function () {
parent.layer.close(parent.layer.getFrameIndex(window.name))//关闭当前页
parent.layui.table.reload('dict-type-table')
})
} else {
layer.msg(result.msg, { icon: 2, time: 1000 })
}
}
})
return false
})
})
</script>
<script>
</script>
</body>
</html>
@@ -1,95 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>字典增加</title>
{% include 'admin/common/header.html' %}
</head>
<body>
<form class="layui-form" action="">
<div class="mainBox">
<div class="main-container">
<div class="main-container">
<div class="layui-form-item">
<label class="layui-form-label">标签</label>
<div class="layui-input-block">
<input type="text" name="dataLabel" lay-verify="title" autocomplete="off" placeholder="请输入标签"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="text" name="dataValue" lay-verify="title" autocomplete="off" placeholder="请输入值"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">标识</label>
<div class="layui-input-block">
<input type="text" value="{{ type_code }}" readonly="readonly" name="typeCode"
lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" name="enable" value="1" title="开启">
<input type="radio" name="enable" value="0" title="关闭" checked>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea placeholder="请输入描述" name="remark" class="layui-textarea"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="button-container">
<button type="submit" class="pear-btn pear-btn-primary pear-btn-sm" lay-submit=""
lay-filter="dict-data-save">
<i class="layui-icon layui-icon-ok"></i>
提交
</button>
<button type="reset" class="pear-btn pear-btn-sm">
<i class="layui-icon layui-icon-refresh"></i>
重置
</button>
</div>
</div>
</form>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['form', 'jquery'], function () {
let form = layui.form
let $ = layui.jquery
form.on('submit(dict-data-save)', function (data) {
$.ajax({
url: '/admin/dict/dictData/save',
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'post',
success: function (result) {
if (result.success) {
layer.msg(result.msg, { icon: 1, time: 1000 }, function () {
parent.layer.close(parent.layer.getFrameIndex(window.name))//关闭当前页
parent.layui.table.reload('dict-data-table')
})
} else {
layer.msg(result.msg, { icon: 2, time: 1000 })
}
}
})
return false
})
})
</script>
<script>
</script>
</body>
</html>
@@ -1,103 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>字典增加</title>
{% include 'admin/common/header.html' %}
</head>
<body>
<form class="layui-form" action="">
<div class="mainBox">
<div class="main-container">
<div class="main-container">
<div class="layui-form-item layui-hide">
<label class="layui-form-label">编号</label>
<div class="layui-input-block">
<input type="text" value="{{ dict_data.id }}" name="dataId" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">标签</label>
<div class="layui-input-block">
<input type="text" value="{{ dict_data.data_label }}" name="dataLabel" lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="text" value="{{ dict_data.data_value }}" name="dataValue" lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">标识</label>
<div class="layui-input-block">
<input type="text" value="{{ dict_data.type_code }}" readonly="readonly" name="typeCode"
lay-verify="title" autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" {% if dict_data.enable == 1 %} } checked{% endif %} name="enable" value="1"
title="开启">
<input type="radio" {% if dict_data.enable == 0 %} } checked{% endif %} name="enable" value="0"
title="关闭">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea placeholder="请输入描述" name="remark"
class="layui-textarea">{{ dict_data.remark }}</textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="button-container">
<button type="submit" class="pear-btn pear-btn-primary pear-btn-sm" lay-submit=""
lay-filter="dict-data-save">
<i class="layui-icon layui-icon-ok"></i>
提交
</button>
<button type="reset" class="pear-btn pear-btn-sm">
<i class="layui-icon layui-icon-refresh"></i>
重置
</button>
</div>
</div>
</form>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['form', 'jquery'], function () {
let form = layui.form
let $ = layui.jquery
form.on('submit(dict-data-save)', function (data) {
$.ajax({
url: '/admin/dict/dictData/update',
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'put',
success: function (result) {
if (result.success) {
layer.msg(result.msg, { icon: 1, time: 1000 }, function () {
parent.layer.close(parent.layer.getFrameIndex(window.name))//关闭当前页
parent.layui.table.reload('dict-data-table')
})
} else {
layer.msg(result.msg, { icon: 2, time: 1000 })
}
}
})
return false
})
})
</script>
<script>
</script>
</body>
</html>
@@ -1,97 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>字典增加</title>
{% include 'admin/common/header.html' %}
</head>
<body>
<form class="layui-form" action="">
<div class="mainBox">
<div class="main-container">
<div class="main-container">
<div class="layui-form-item layui-hide">
<label class="layui-form-label">编号</label>
<div class="layui-input-block">
<input type="text" value="{{ dict_type.id }}" name="id" lay-verify="title" autocomplete="off"
placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input type="text" value="{{ dict_type.type_name }}" name="typeName" lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">标识</label>
<div class="layui-input-block">
<input type="text" value="{{ dict_type.type_code }}" readonly name="typeCode"
lay-verify="title" autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="radio" {% if dict_type.enable == 1 %} } checked{% endif %} name="enable" value="1"
title="开启">
<input type="radio" {% if dict_type.enable == 0 %} } checked{% endif %} name="enable" value="0"
title="关闭">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea placeholder="请输入描述" name="description"
class="layui-textarea">{{ dict_type.description }}</textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="button-container">
<button type="submit" class="pear-btn pear-btn-primary pear-btn-sm" lay-submit=""
lay-filter="dict-type-update">
<i class="layui-icon layui-icon-ok"></i>
提交
</button>
<button type="reset" class="pear-btn pear-btn-sm">
<i class="layui-icon layui-icon-refresh"></i>
重置
</button>
</div>
</div>
</form>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['form', 'jquery'], function () {
let form = layui.form
let $ = layui.jquery
form.on('submit(dict-type-update)', function (data) {
$.ajax({
url: '/admin/dict/dictType/update',
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'put',
success: function (result) {
if (result.success) {
layer.msg(result.msg, { icon: 1, time: 1000 }, function () {
parent.layer.close(parent.layer.getFrameIndex(window.name))//关闭当前页
parent.layui.table.reload('dict-type-table')
})
} else {
layer.msg(result.msg, { icon: 2, time: 1000 })
}
}
})
return false
})
})
</script>
<script>
</script>
</body>
</html>
-374
View File
@@ -1,374 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>字典管理</title>
{% include 'admin/common/header.html' %}
</head>
<body class="pear-container">
<div class="layui-row layui-col-space10">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-body">
<form class="layui-form" action="">
<div class="layui-form-item">
<label class="layui-form-label">字典名称</label>
<div class="layui-input-inline">
<input type="text" name="typeName" placeholder="" class="layui-input">
</div>
<button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="dict-type-query">
<i class="layui-icon layui-icon-search"></i>
查询
</button>
<button type="reset" class="pear-btn pear-btn-md">
<i class="layui-icon layui-icon-refresh"></i>
重置
</button>
</div>
</form>
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-body">
<table id="dict-type-table" lay-filter="dict-type-table"></table>
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-body">
<svg class="empty" style="margin-top: 50px;margin-left: 220px;margin-bottom: 80px;" width="184"
height="152" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<g transform="translate(24 31.67)">
<ellipse fillOpacity=".8" fill="#F5F5F7" cx="67.797" cy="106.89" rx="67.797"
ry="12.668"></ellipse>
<path d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
fill="#AEB8C2"></path>
<path d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
fill="url(#linearGradient-1)" transform="translate(13.56)"></path>
<path d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
fill="#F5F5F7"></path>
<path d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
fill="#DCE0E6"></path>
</g>
<path d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
fill="#DCE0E6"></path>
<g transform="translate(149.65 15.383)" fill="#FFF">
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"></ellipse>
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"></path>
</g>
</g>
</svg>
<table id="dict-data-table" lay-filter="dict-data-table"></table>
</div>
</div>
</div>
</div>
</body>
<script type="text/html" id="dict-type-toolbar">
<button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add">
<i class="layui-icon layui-icon-add-1"></i>
新增
</button>
<button class="pear-btn pear-btn-md" lay-event="batchRemove">
<i class="layui-icon layui-icon-delete"></i>
删除
</button>
</script>
<script type="text/html" id="dict-type-bar">
<button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit">
<i class="layui-icon layui-icon-edit"></i>
</button>
<button class="pear-btn pear-btn-warming pear-btn-sm" lay-event="details">
<i class="layui-icon layui-icon-transfer"></i>
</button>
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove">
<i class="layui-icon layui-icon-delete"></i>
</button>
</script>
<script type="text/html" id="dict-type-enable">
<input type="checkbox" value="{{ "{{ d.id }}" }}" lay-skin="switch" lay-text="启用|禁用" lay-filter="dict-type-enable"
{{ "{{# if(d.enable==1){ }} checked {{# } }}" }}>
</script>
<script type="text/html" id="dict-data-toolbar">
<button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add">
<i class="layui-icon layui-icon-add-1"></i>
新增
</button>
<button class="pear-btn pear-btn-md" lay-event="batchRemove">
<i class="layui-icon layui-icon-delete"></i>
删除
</button>
</script>
<script type="text/html" id="dict-data-bar">
<button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>
</button>
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i class="layui-icon layui-icon-delete"></i>
</button>
</script>
<script type="text/html" id="dict-data-enable">
<input type="checkbox" value="{{ "{{ d.dataId }}" }}" lay-skin="switch" lay-text="启用|禁用"
lay-filter="dict-data-enable"
{{ "{{# if(d.enable==1){ }} checked {{# } }}" }}>
</script>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['table', 'form', 'jquery', 'popup'], function () {
let table = layui.table
let form = layui.form
let $ = layui.jquery
let popup = layui.popup
let typeCode
let cols = [
[
{ type: 'checkbox' },
{ title: '字典名称', field: 'typeName', align: 'center', width: 120 },
{ title: '描述', field: 'description', align: 'center' },
{ title: '字典状态', field: 'enable', align: 'center', templet: '#dict-type-enable' },
{ title: '操作', toolbar: '#dict-type-bar', align: 'center', width: 180 }
]
]
let dataCols = [
[
{ type: 'checkbox' },
{ title: '标签', field: 'dataLabel', align: 'center', width: 120 },
{ title: '对应值', field: 'dataValue', align: 'center' },
{ title: '状态', field: 'enable', align: 'center', templet: '#dict-data-enable' },
{ title: '操作', toolbar: '#dict-data-bar', align: 'center', width: 180 }
]
]
table.render({
elem: '#dict-type-table',
url: '/admin/dict/dictType/data',
page: true,
cols: cols,
skin: 'line',
height: 'full-148',
toolbar: '#dict-type-toolbar',
defaultToolbar: [{
layEvent: 'refresh',
icon: 'layui-icon-refresh',
}, 'filter', 'print', 'exports']
})
window.renderData = function (code) {
typeCode = code
$('.empty').hide()
table.render({
elem: '#dict-data-table',
url: '/admin/dict/dictData/data?typeCode=' + typeCode,
page: true,
height: 'full-148',
cols: dataCols,
skin: 'line',
toolbar: '#dict-data-toolbar'
})
}
table.on('tool(dict-type-table)', function (obj) {
if (obj.event === 'remove') {
window.removeType(obj)
} else if (obj.event === 'edit') {
window.editType(obj)
} else if (obj.event === 'details') {
window.renderData(obj.data['typeCode'])
}
})
table.on('toolbar(dict-type-table)', function (obj) {
if (obj.event === 'add') {
window.addType()
} else if (obj.event === 'refresh') {
window.refreshType()
}
})
form.on('submit(dict-type-query)', function (data) {
table.reload('dict-type-table', { where: data.field })
return false
})
form.on('switch(dict-type-enable)', function (obj) {
let operate
if (obj.elem.checked) {
operate = 'enable'
} else {
operate = 'disable'
}
let loading = layer.load()
$.ajax({
url: '/admin/dict/dictType/' + operate,
data: JSON.stringify({ id: this.value }),
dataType: 'json',
contentType: 'application/json',
type: 'put',
success: function (result) {
layer.close(loading)
if (result.success) {
popup.success(result.msg)
} else {
popup.failure(result.msg)
}
}
})
})
window.addType = function () {
layer.open({
type: 2,
title: '新增',
shade: 0.1,
area: ['500px', '400px'],
content: '/admin/dict/dictType/add'
})
}
window.editType = function (obj) {
layer.open({
type: 2,
title: '修改',
shade: 0.1,
area: ['500px', '400px'],
content: '/admin/dict/dictType/edit?dictTypeId=' + obj.data['id']
})
}
window.removeType = function (obj) {
layer.confirm('确定要删除该字典分类', { icon: 3, title: '提示' }, function (index) {
layer.close(index)
let loading = layer.load()
$.ajax({
url: '/admin/dict/dictType/remove/' + obj.data['id'],
dataType: 'json',
type: 'delete',
success: function (result) {
layer.close(loading)
if (result.success) {
popup.success(result.msg, function () {
if (typeCode === obj.data['typeCode']) {
$('[lay-id=\'dict-data-table\']').remove()
$('.empty').show()
}
obj.del()
})
} else {
popup.failure(result.msg)
}
}
})
})
}
window.refreshType = function () {
table.reload('dict-type-table')
}
window.addData = function () {
layer.open({
type: 2,
title: '新增',
shade: 0.1,
area: ['500px', '450px'],
content: '/admin/dict/dictData/add?typeCode=' + typeCode
})
}
window.editData = function (obj) {
layer.open({
type: 2,
title: '修改',
shade: 0.1,
area: ['500px', '450px'],
content: '/admin/dict/dictData/edit?dataId=' + obj.data['dataId']
})
}
window.removeData = function (obj) {
layer.confirm('确定要删除该字典值', { icon: 3, title: '提示' }, function (index) {
layer.close(index)
let loading = layer.load()
$.ajax({
url: '/admin/dict/dictData/remove/' + obj.data['dataId'],
dataType: 'json',
type: 'delete',
success: function (result) {
layer.close(loading)
if (result.success) {
popup.success(result.msg, function () {
obj.del()
})
} else {
popup.failure(result.msg)
}
}
})
})
}
table.on('tool(dict-data-table)', function (obj) {
if (obj.event === 'remove') {
window.removeData(obj)
} else if (obj.event === 'edit') {
window.editData(obj)
} else if (obj.event === 'details') {
window.details(obj)
}
})
table.on('toolbar(dict-data-table)', function (obj) {
if (obj.event === 'add') {
window.addData()
} else if (obj.event === 'refresh') {
window.refreshData()
}
})
form.on('submit(dict-data-query)', function (data) {
data.field.typeCode = typeCode
table.reload('dict-data-table', { where: data.field })
return false
})
form.on('switch(dict-data-enable)', function (obj) {
let operate
if (obj.elem.checked) {
operate = 'enable'
} else {
operate = 'disable'
}
let loading = layer.load()
$.ajax({
url: '/admin/dict/dictData/' + operate,
data: JSON.stringify({ dataId: this.value }),
dataType: 'json',
contentType: 'application/json',
type: 'put',
success: function (result) {
layer.close(loading)
if (result.success) {
popup.success(result.msg)
} else {
popup.failure(result.msg)
}
}
})
})
window.refreshData = function () {
table.reload('dict-data-table')
}
})
</script>
</html>
@@ -162,10 +162,10 @@
layer.close(index) layer.close(index)
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: 'delete', url: 'upload',
data: { id: obj.data['id'] }, data: { id: obj.data['id'] },
dataType: 'json', dataType: 'json',
type: 'POST', type: 'delete',
success: function (res) { success: function (res) {
layer.close(loading) layer.close(loading)
if (res.success) { if (res.success) {
@@ -210,7 +210,7 @@
layer.close(index) layer.close(index)
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: "{{ url_for('adminFile.batch_remove') }}", url: "{{ url_for('file.batch_remove') }}",
data: { ids: ids }, data: { ids: ids },
dataType: 'json', dataType: 'json',
type: 'POST', type: 'POST',
@@ -43,7 +43,7 @@
//选完文件后不自动上传 //选完文件后不自动上传
upload.render({ upload.render({
elem: '#logo-img' elem: '#logo-img'
, url: "{{ url_for('adminFile.upload') }}" , url: "{{ url_for('file.upload') }}"
, auto: false , auto: false
, exts: 'jpg|png|gif|bmp|jpeg' , exts: 'jpg|png|gif|bmp|jpeg'
, size: 1000 , size: 1000
+11 -5
View File
@@ -17,9 +17,12 @@
<div class="layui-header"> <div class="layui-header">
<!-- 顶 部 左 侧 功 能 --> <!-- 顶 部 左 侧 功 能 -->
<ul class="layui-nav layui-layout-left"> <ul class="layui-nav layui-layout-left">
<li class="collaspe layui-nav-item"><a href="#" class="layui-icon layui-icon-shrink-right"></a></li> <li class="collaspe layui-nav-item">
<li class="refresh layui-nav-item"><a href="#" class="layui-icon layui-icon-refresh-1" title="刷新页面" <a href="#" class="layui-icon layui-icon-shrink-right"></a>
loading=600></a></li> </li>
<li class="refresh layui-nav-item">
<a href="#" class="layui-icon layui-icon-refresh-1" title="刷新页面" loading=600></a>
</li>
</ul> </ul>
<!-- 顶 部 右 侧 菜 单 --> <!-- 顶 部 右 侧 菜 单 -->
<div id="control" class="layui-layout-control"></div> <div id="control" class="layui-layout-control"></div>
@@ -39,7 +42,7 @@
</a> </a>
<!-- 功 能 菜 单 --> <!-- 功 能 菜 单 -->
<dl class="layui-nav-child"> <dl class="layui-nav-child">
<dd><a user-menu-url="/admin/user/center" user-menu-id="5555" user-menu-title="基本资料">基本资料</a></dd> <dd><a user-menu-url="/users/center" user-menu-id="5555" user-menu-title="基本资料">基本资料</a></dd>
<dd><a href="javascript:void(0);" class="logout">注销登录</a></dd> <dd><a href="javascript:void(0);" class="logout">注销登录</a></dd>
</dl> </dl>
</li> </li>
@@ -58,6 +61,7 @@
<div id="sideMenu"></div> <div id="sideMenu"></div>
</div> </div>
</div> </div>
<!-- 内容主题 -->
<div class="layui-body"> <div class="layui-body">
<div id="content"></div> <div id="content"></div>
</div> </div>
@@ -65,7 +69,9 @@
<!-- 遮 盖 层 --> <!-- 遮 盖 层 -->
<div class="pear-cover"></div> <div class="pear-cover"></div>
<!-- 移 动 端 便 捷 操 作 --> <!-- 移 动 端 便 捷 操 作 -->
<div class="pear-collasped-pe collaspe"><a href="#" class="layui-icon layui-icon-shrink-right"></a></div> <div class="pear-collasped-pe collaspe">
<a href="#" class="layui-icon layui-icon-shrink-right"></a>
</div>
<!-- 加 载 动 画--> <!-- 加 载 动 画-->
<div class="loader-main"> <div class="loader-main">
<div class="loader"></div> <div class="loader"></div>
+46 -41
View File
@@ -39,51 +39,56 @@
</form> </form>
{% include 'admin/common/footer.html' %} {% include 'admin/common/footer.html' %}
<script> <script>
layui.use(['form', 'jquery', 'layer', 'button', 'popup'], function () { layui.use(['form', 'jquery', 'layer', 'button', 'popup'], function () {
let form = layui.form; let form = layui.form
let $ = layui.jquery; let $ = layui.jquery
let layer = layui.layer; let layer = layui.layer
let button = layui.button; let button = layui.button
let popup = layui.popup; let popup = layui.popup
let captchaPath = "{{ url_for('passport.get_captcha') }}"; let captchaPath = "{{ url_for('passport.get_captcha') }}"
form.on('submit(login)', function (data) { form.on('submit(login)', function (data) {
let loader = layer.load(); // data 是表单的数据
let btn = button.load({elem: '.login'}); let loader = layer.load() // 加载按钮
$.ajax({ let btn = button.load({ elem: '.login' })
data: data.field, $.ajax({
type: "post", data: data.field,
dataType: 'json', type: 'post',
success: function (result) { dataType: 'json',
layer.close(loader); // 请求成功之后返回的数据
btn.stop(function () { success: function (result) {
if (result.success) { layer.close(loader) // 关闭加载按钮
popup.success(result.msg, function () { btn.stop(function () {
location.href = "{{ url_for('admin.index') }}"; if (result.success) {
}) popup.success(result.msg, function () {
} else { location.href = "{{ url_for('admin.index') }}"
popup.failure(result.msg, function () { })
document.getElementById("captchaImage").src = captchaPath + "?" + Math.random(); } else {
}); popup.failure(result.msg, function () {
} document.getElementById('captchaImage').src = captchaPath + '?' + Math.random()
}) })
} }
}); })
return false; }
}); })
return false
$("#captchaImage").click(function () {
document.getElementById("captchaImage").src = captchaPath + "?" + Math.random();
});
setInterval(function () {
document.getElementById("captchaImage").src = captchaPath + "?" + Math.random();
}, 30 * 1000);
}) })
// 点击按钮更新验证码
$('#captchaImage').click(function () {
document.getElementById('captchaImage').src = captchaPath + '?' + Math.random()
})
// 定时器,定时更新验证码
setInterval(function () {
document.getElementById('captchaImage').src = captchaPath + '?' + Math.random()
}, 30 * 1000)
})
</script> </script>
<script> <script>
if (window !== top) { if (window !== top) {
top.location.href = location.href; top.location.href = location.href
} }
</script> </script>
</body> </body>
</html> </html>
-408
View File
@@ -1,408 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>首页</title>
{% include 'admin/common/header.html' %}
<link rel="stylesheet" href="{{ url_for('static', filename='/admin/admin/css/other/console2.css') }}"/>
</head>
<body class="pear-container">
<div class="layui-row layui-col-space10">
<div class="layui-col-md8">
<div class="layui-row layui-col-space10">
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-header">
主机信息
</div>
<div class="layui-card-body">
<div class="layui-row layui-col-space10">
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">核心数</div>
<div class="count pear-text">{{ cpu_count }}</div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">空闲率</div>
<div class="count pear-text"></div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">等待率</div>
<div class="count pear-text"></div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">使用率</div>
<div class="count pear-text">{{ cpus_percent }}%</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-header">
内存监控
</div>
<div class="layui-card-body">
<div class="layui-row layui-col-space10">
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">空闲内存</div>
<div class="count pear-text">{{ memory_free }}M</div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">最大内存</div>
<div class="count pear-text">{{ memory_total }}M</div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">已用内存</div>
<div class="count pear-text">{{ memory_used }}M</div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6 layui-col-xs6">
<div class="pear-card2">
<div class="title">内存使用</div>
<div class="count pear-text">{{ memory_usage }}%</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">主机监控</div>
<div class="layui-card-body">
<div class="layui-tab custom-tab layui-tab-brief" lay-filter="docDemoTabBrief">
<div id="echarts-records" style="background-color:#ffffff;min-height:400px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-md4">
<div class="layui-card">
<div class="layui-card-header">磁盘信息</div>
<div class="layui-card-body">
<ul class="pear-card-status">
{% for i in disk_partitions_list %}
<li>
<p>{{ i.device }}</p>
<p>{{ i.fstype }}</p>
磁盘大小&nbsp;<span>{{ i.total }}M</span>&nbsp;&nbsp;
空闲大小&nbsp;<span>{{ i.free }}M</span>&nbsp;&nbsp;
<br/>
<br/>
已经使用&nbsp;<span>{{ i.used }}M</span>&nbsp;&nbsp;
使用概率&nbsp;<span>{{ i.percent }}%</span>
<br/>
<a href="javascript:;" data-id="1" class="pear-btn pear-btn-xs pear-btn-primary pear-reply">详情</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="layui-card">
<div class="layui-card-header">主机信息</div>
<div class="layui-card-body">
<table class="layui-table" lay-skin="line">
<thead>
<tr>
<th>属性</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>名称</td>
<td>{{ hostname }}</td>
</tr>
<tr>
<td>系统</td>
<td>{{ system_version }}</td>
</tr>
<tr>
<td>开机时间</td>
<td>{{ boot_time }}</td>
</tr>
<tr>
<td>运行时长</td>
<td>{{ up_time_format }}</td>
</tr>
<tr>
<td>python版本</td>
<td>{{ python_version }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['layer', 'echarts', 'popup'], function () {
var $ = layui.jquery,
echarts = layui.echarts;
let popup = layui.popup;
var echartsRecords = echarts.init(document.getElementById('echarts-records'), 'walden');
$("body").on("click", "[data-url]", function () {
parent.layui.tab.addTabOnlyByElem("content", {
id: $(this).attr("data-id"),
title: $(this).attr("data-title"),
url: $(this).attr("data-url"),
close: true
})
})
let bgColor = "#fff";
let color = [
"#0090FF",
"#36CE9E",
"#FFC005",
"#FF515A",
"#8B5CFF",
"#00CA69"
];
let echartData = [
{
name: "{{time_now}}",
cpu_percent: {{ cpus_percent }},
memory_percent: {{ memory_usage }}
},
];
var xAxisData = echartData.map(v => v.name);
// ["1", "2", "3", "4", "5", "6", "7", "8"]
var yAxisData1 = echartData.map(v => v.cpu_percent);
// [100, 138, 350, 173, 180, 150, 180, 230]
var yAxisData2 = echartData.map(v => v.memory_percent);
// [233, 233, 200, 180, 199, 233, 210, 180]
const hexToRgba = (hex, opacity) => {
let rgbaColor = "";
let reg = /^#[\da-f]{6}$/i;
if (reg.test(hex)) {
rgbaColor =
`rgba(${parseInt("0x" + hex.slice(1, 3))},${parseInt(
"0x" + hex.slice(3, 5)
)},${parseInt("0x" + hex.slice(5, 7))},${opacity})`;
}
return rgbaColor;
}
option = {
backgroundColor: bgColor,
color: color,
legend: {
right: 10,
top: 10
},
tooltip: {
trigger: "axis",
formatter: function (params) {
let html = '';
params.forEach(v => {
html +=
`<div style="color: #666;font-size: 14px;line-height: 24px">
<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${color[v.componentIndex]};"></span>
${v.seriesName}.${v.name}
<span style="color:${color[v.componentIndex]};font-weight:700;font-size: 18px">${v.value}</span>
%`;
})
return html
},
extraCssText: 'background: #fff; border-radius: 0;box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);color: #333;',
axisPointer: {
type: 'shadow',
shadowStyle: {
color: '#ffffff',
shadowColor: 'rgba(225,225,225,1)',
shadowBlur: 5
}
}
},
grid: {
top: 100,
containLabel: true
},
xAxis: [{
type: "category",
boundaryGap: false,
axisLabel: {
formatter: '{value}',
textStyle: {
color: "#333"
}
},
axisLine: {
lineStyle: {
color: "#D9D9D9"
}
},
data: xAxisData
}],
yAxis: [{
type: "value",
name: '单位:百分比',
axisLabel: {
textStyle: {
color: "#666"
}
},
nameTextStyle: {
color: "#666",
fontSize: 12,
lineHeight: 40
},
splitLine: {
lineStyle: {
type: "dashed",
color: "#E9E9E9"
}
},
axisLine: {
show: false
},
axisTick: {
show: false
}
}],
series: [{
name: "CPU",
type: "line",
smooth: true,
symbolSize: 8,
zlevel: 3,
lineStyle: {
normal: {
color: color[0],
shadowBlur: 3,
shadowColor: hexToRgba(color[0], 0.5),
shadowOffsetY: 8
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[{
offset: 0,
color: hexToRgba(color[0], 0.3)
},
{
offset: 1,
color: hexToRgba(color[0], 0.1)
}
],
false
),
shadowColor: hexToRgba(color[0], 0.1),
shadowBlur: 10
}
},
data: yAxisData1
}, {
name: '内存',
type: "line",
smooth: true,
symbolSize: 8,
zlevel: 3,
lineStyle: {
normal: {
color: color[1],
shadowBlur: 3,
shadowColor: hexToRgba(color[1], 0.5),
shadowOffsetY: 8
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[{
offset: 0,
color: hexToRgba(color[1], 0.3)
},
{
offset: 1,
color: hexToRgba(color[1], 0.1)
}
],
false
),
shadowColor: hexToRgba(color[1], 0.1),
shadowBlur: 10
}
},
data: yAxisData2
}]
};
echartsRecords.setOption(option);
window.onresize = function () {
echartsRecords.resize();
};
setInterval(ajaxPolling, 1000 * 10);
function ajaxPolling() {
$.ajax({
url: "/admin/monitor/polling",
success: function (data) {
echartData.push({
name: data.time_now,
cpu_percent: data.cups_percent,
memory_percent: data.memory_used
});
if (echartData.length > 8) {
echartData.shift();
}
xAxisData = echartData.map(v => v.name);
yAxisData1 = echartData.map(v => v.cpu_percent);
yAxisData2 = echartData.map(v => v.memory_percent);
option.xAxis[0].data = xAxisData;
option.series[0].data = yAxisData1;
option.series[1].data = yAxisData2;
echartsRecords.setOption(option);
},
error: function (xhr, type, errorThrown) {
popup.failure("api错误");
}
})
}
});
</script>
</body>
</html>
@@ -93,7 +93,7 @@
dtree.renderSelect({ dtree.renderSelect({
elem: '#selectParent', elem: '#selectParent',
url: '/admin/power/selectParent', url: '{{ url_for('rights.select_parent') }}',
method: 'get', method: 'get',
selectInputName: { nodeId: 'parentId', context: 'powerName' }, selectInputName: { nodeId: 'parentId', context: 'powerName' },
skin: 'layui', skin: 'layui',
@@ -126,7 +126,7 @@
form.on('submit(power-save)', function (data) { form.on('submit(power-save)', function (data) {
data.field.icon = 'layui-icon ' + data.field.icon data.field.icon = 'layui-icon ' + data.field.icon
$.ajax({ $.ajax({
url: '/admin/power/save', url: '/rights/power',
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@@ -105,7 +105,7 @@
dtree.renderSelect({ dtree.renderSelect({
elem: '#selectParent', elem: '#selectParent',
url: '/admin/power/selectParent', url: '{{ url_for('rights.select_parent')}}',
method: 'get', method: 'get',
selectInputName: { nodeId: 'parentId', context: 'powerName' }, selectInputName: { nodeId: 'parentId', context: 'powerName' },
skin: 'layui', skin: 'layui',
@@ -137,8 +137,9 @@
form.on('submit(power-save)', function (data) { form.on('submit(power-save)', function (data) {
data.field.icon = 'layui-icon ' + data.field.icon data.field.icon = 'layui-icon ' + data.field.icon
console.log(data.field)
$.ajax({ $.ajax({
url: '/admin/power/update', url: '/rights/power/' + data.field.powerId ,
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@@ -191,7 +192,7 @@
} }
} }
window.init("{{ power.type}}") window.init("{{ power.type }}")
}) })
</script> </script>
<script> <script>
@@ -92,7 +92,7 @@
let treetable = layui.treetable let treetable = layui.treetable
let popup = layui.popup let popup = layui.popup
let MODULE_PATH = '/admin/power/' let MODULE_PATH = '/rights/'
window.render = function () { window.render = function () {
treetable.render({ treetable.render({
@@ -105,7 +105,7 @@
treeDefaultClose: true, treeDefaultClose: true,
toolbar: '#power-toolbar', toolbar: '#power-toolbar',
elem: '#power-table', elem: '#power-table',
url: '/admin/power/data', url: '{{ url_for('rights.data') }}',
page: false, page: false,
cols: [ cols: [
[ [
@@ -174,14 +174,14 @@
form.on('switch(power-enable)', function (obj) { form.on('switch(power-enable)', function (obj) {
let operate let operate
if (obj.elem.checked) { if (obj.elem.checked) {
operate = 'enable' operate = 1 /*启用*/
} else { } else {
operate = 'disable' operate = 0 /*禁用*/
} }
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: '/admin/power/' + operate, url: MODULE_PATH + 'enable',
data: JSON.stringify({ powerId: this.value }), data: JSON.stringify({ powerId: this.value, operate: operate }),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
type: 'put', type: 'put',
@@ -202,7 +202,7 @@
title: '新增', title: '新增',
shade: 0.1, shade: 0.1,
area: ['450px', '500px'], area: ['450px', '500px'],
content: MODULE_PATH + 'add' content: MODULE_PATH + 'power'
}) })
} }
@@ -212,7 +212,7 @@
title: '修改', title: '修改',
shade: 0.1, shade: 0.1,
area: ['450px', '500px'], area: ['450px', '500px'],
content: MODULE_PATH + 'edit/' + obj.data['powerId'] content: MODULE_PATH + 'power/' + obj.data['powerId']
}) })
} }
window.remove = function (obj) { window.remove = function (obj) {
@@ -220,7 +220,7 @@
layer.close(index) layer.close(index)
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: MODULE_PATH + 'remove/' + obj.data['powerId'], url: MODULE_PATH + 'power/' + obj.data['powerId'],
dataType: 'json', dataType: 'json',
type: 'delete', type: 'delete',
success: function (result) { success: function (result) {
+1 -1
View File
@@ -66,7 +66,7 @@
form.on('submit(role-save)', function (data) { form.on('submit(role-save)', function (data) {
$.ajax({ $.ajax({
url: '/admin/role/save', url: '/admin/role/add',
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
+4 -3
View File
@@ -32,9 +32,9 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">状态</label> <label class="layui-form-label">状态</label>
<div class="layui-input-block"> <div class="layui-input-block">
<input type="radio" name="enable" {% if role.enable == 0 %}checked {% endif %}value="0" <input type="radio" name="enable" {% if role.enable == 1 %}checked {% endif %}value="0"
title="开启"> title="开启">
<input type="radio" name="enable" {% if role.enable == 1 %}checked {% endif %} value="1" <input type="radio" name="enable" {% if role.enable == 0 %}checked {% endif %} value="1"
title="关闭"> title="关闭">
</div> </div>
</div> </div>
@@ -78,7 +78,7 @@
form.on('submit(role-update)', function (data) { form.on('submit(role-update)', function (data) {
$.ajax({ $.ajax({
url: '/admin/role/update', url: '/admin/role/edit/' + data.field.roleId,
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@@ -96,6 +96,7 @@
}) })
return false return false
}) })
})
}) })
</script> </script>
<script> <script>
+6 -6
View File
@@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>用户管理</title> <title>角色管理</title>
{% include 'admin/common/header.html' %}</head> {% include 'admin/common/header.html' %}</head>
<body class="pear-container"> <body class="pear-container">
<div class="layui-card"> <div class="layui-card">
@@ -137,14 +137,14 @@
form.on('switch(role-enable)', function (obj) { form.on('switch(role-enable)', function (obj) {
let operate let operate
if (obj.elem.checked) { if (obj.elem.checked) {
operate = 'enable' operate = 1
} else { } else {
operate = 'disable' operate = 0
} }
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: '/admin/role/' + operate, url: '/admin/role/enable',
data: JSON.stringify({ roleId: this.value }), data: JSON.stringify({ roleId: this.value, operate: operate }),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
type: 'put', type: 'put',
@@ -194,7 +194,7 @@
layer.close(index) layer.close(index)
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: MODULE_PATH + 'remove/' + obj.data['id'], url: MODULE_PATH + 'role_power/' + obj.data['id'],
dataType: 'json', dataType: 'json',
type: 'delete', type: 'delete',
success: function (result) { success: function (result) {
+2 -2
View File
@@ -35,7 +35,7 @@
dtree.render({ dtree.render({
elem: '#role-power', elem: '#role-power',
method: 'get', method: 'get',
url: '/admin/role/getRolePower/' + {{id}}, url: '/admin/role/role_power/' + {{id}},
dataFormat: 'list', dataFormat: 'list',
checkbar: true, checkbar: true,
skin: 'layui', skin: 'layui',
@@ -56,7 +56,7 @@
data.field.powerIds = ids data.field.powerIds = ids
$.ajax({ $.ajax({
url: '/admin/role/saveRolePower', url: '/admin/role/role_power/' + data.field.roleId,
data: data.field, data: data.field,
dataType: 'json', dataType: 'json',
type: 'put', type: 'put',
+1 -1
View File
@@ -73,7 +73,7 @@
data.field.roleIds = roleIds data.field.roleIds = roleIds
$.ajax({ $.ajax({
url: '/admin/user/save', url: '/users/add',
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html>
<head>
<title>头像上传</title>
{% include 'admin/common/header.html' %}
</head>
<body class="pear-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-xs9">
<div style="height:325px;background-color: rgb(247, 247, 247);">
<img id="sourceImage" src="">
</div>
</div>
<div class="layui-col-xs3" style="padding-left:0px;">
<div id="previewImage"
style="width:210px;height:210px;border: 1px solid rgb(200, 200, 200);border-radius: 50%;overflow:hidden;">
</div>
</div>
</div>
<div class="layui-row">
<div class="layui-form-item">
<div class="layui-input-inline layui-btn-container" style="width: auto;vertical-align:top;">
<button class="pear-btn pear-btn-sm pear-btn-primary layui-icon layui-icon-left" cropper-event="rotate"
data-option="-15" title="左旋15°"></button>
<button class="pear-btn pear-btn-sm pear-btn-primary layui-icon layui-icon-right" cropper-event="rotate"
data-option="15" title="右旋15°"></button>
<button class="pear-btn pear-btn-sm pear-btn-danger layui-icon layui-icon-refresh" cropper-event="reset"
title="重置"></button>
<label for="uploadPicture" class="pear-btn pear-btn-sm pear-btn-primary layui-icon layui-icon-upload"
title="选择图片"></label>
<input class="layui-upload-file" id="uploadPicture" type="file" value="选择图片">
</div>
<div class="layui-form-mid layui-word-aux">建议:图片的尺寸宽高比为1:1,大小在5m以内。</div>
</div>
</div>
</body>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['jquery', 'layer', 'cropper', 'popup'], function () {
let $ = layui.jquery
let layer = layui.layer
let cropper = layui.cropper
let popup = layui.popup
let options = {
aspectRatio: 1 / 1, // 裁剪框比例
preview: '#previewImage', // 预览div
viewmode: 1
}
$('#sourceImage').attr('src', parent.layui.$('#avatar').attr('src'))
$('#sourceImage').cropper(options)
window.submitForm = function () {
$('#sourceImage').crossOrigin = 'anonymous'//解决跨域图片问题
$('#sourceImage').cropper('getCroppedCanvas', {
width: 280,
height: 140
}).toBlob(function (blob) {
let timeStamp = Date.parse(new Date())
let fileName = timeStamp + '.jpg'
let formData = new FormData()
formData.append('file', blob, fileName)
formData.append('fileName', fileName)
formData.append('fileToken', timeStamp)
$.ajax({
method: 'post',
url: '/admin/file/upload', //用于文件上传的服务器端请求地址
data: formData,
processData: false,
contentType: false,
success: function (result) {
if (result.success) {
// 修改 avatar 字段
$.ajax({
method: 'put',
url: '/users/avatar',
data: JSON.stringify({ avatar: result.data }),
dataType: 'json',
contentType: 'application/json',
success: function (res) {
if (res.success) {
// 关闭当前弹层
parent.layui.$('#avatar').attr('src', result.data.src)
top.layui.$('#avatar').attr('src', result.data.src)
parent.layer.close(parent.layer.getFrameIndex(window.name))
} else {
popup.failure('上传失败')
}
}
})
} else {
popup.failure('上传失败')
}
}
})
})
}
$('.pear-btn').on('click', function () {
let event = $(this).attr('cropper-event')
if (event === 'rotate') {
let option = $(this).attr('data-option')
$('#sourceImage').cropper('rotate', option)
} else if (event === 'reset') {
$('#sourceImage').cropper('reset')
}
$('#uploadPicture').change(function () {
let r = new FileReader()
let f = this.files[0]
let uploadFileSize = f.size / 1024
if (uploadFileSize > 5120) {
parent.layer.msg('上传文件不得超过5m', { icon: 5 })
return false
}
r.readAsDataURL(f)
r.onload = function (e) {
$('#sourceImage')
.cropper('destroy')
.attr('src', this.result)
.cropper(options)
}
})
})
})
</script>
@@ -1,227 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>个人中心</title>
{% include 'admin/common/header.html' %}
<style>
.layui-form-item {
margin-top: 17px !important;
margin-bottom: 17px !important;
}
</style>
</head>
<body class="pear-container">
<div class="layui-row layui-col-space10">
{# 左侧栏 #}
<div class="layui-col-md3">
{# 个人信息卡片 #}
<div class="layui-card">
<div class="layui-card-body" style="padding: 25px;">
<div class="text-center layui-text">
<div class="user-info-head" id="userInfoHead">
<img id="avatar" src="{{ user_info.avatar }}" width="115px" height="115px" alt="">
</div>
<h2 style="padding-top: 20px;font-size: 20px;">{{ user_info.realname }}</h2>
<p style="padding-top: 8px;margin-top: 10px;font-size: 13.5px;">China 中国</p>
</div>
</div>
<div style="height: 45px;border-top: 1px whitesmoke solid;text-align: center;line-height: 45px;font-size: 13.5px;">
<span>{{ user_info.remark }}</span>
</div>
</div>
{# 登录信息卡片 #}
<div class="layui-card">
<div class="layui-card-header">
登录记录
</div>
<div class="layui-card-body">
<ul class="list">
{% for log in user_logs %}
<li class="list-item">
<span class="title">{{ log.url }}</span>
<span class="footer">{{ log.create_time }}</span>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{# 右侧栏 #}
<div class="layui-col-md9">
{# 个人信息卡片 #}
<div class="layui-card">
<div class="layui-card-header">个人信息</div>
<div class="layui-card-body">
<div class="layui-tab layui-tab-brief">
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-block">
<input value="{{ user_info.username }}" type="text" readonly name="username"
lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">姓名</label>
<div class="layui-input-block">
<input value="{{ user_info.realname }}" type="text" name="realName"
lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea placeholder="请输入描述" name="details"
class="layui-textarea">{{ user_info.remark }}</textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="pear-btn pear-btn-primary pear-btn-sm" lay-submit
lay-filter="user-update">修改资料
</button>
<button class="pear-btn pear-btn-sm edit-password">更改密码</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{# 个人文章记录 #}
<div class="layui-card">
<div class="layui-card-header">
我的文章
</div>
<div class="layui-card-body">
<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'admin/common/footer.html' %}
<script>
layui.use(['element', 'jquery', 'layer', 'form'], function () {
let $ = layui.jquery
let layer = layui.layer
let form = layui.form
// 修改密码页面
$('.edit-password').click(function () {
layer.open({
type: 2,
title: '修改密码',
shade: 0.1,
area: ['550px', '280px'],
content: '/admin/user/editPassword'
})
return false
})
// 修改头像
$('#avatar').click(function () {
layer.open({
type: 2,
title: '更换图片',
shade: 0.1,
area: ['900px', '500px'],
content: '/admin/user/profile',
btn: ['确定', '取消'],
yes: function (index, layero) {
window['layui-layer-iframe' + index].submitForm()
}
})
})
// 表单提交更改个人信息数据
form.on('submit(user-update)', function (data) {
$.ajax({
url: '/admin/user/updateInfo',
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'put',
success: function (result) {
if (result.success) {
layer.msg('修改成功', { icon: 1, time: 1000 })
} else {
layer.msg('修改失败', { icon: 2, time: 1000 })
}
}
})
return false
})
})
</script>
</body>
</html>
@@ -57,7 +57,7 @@
// 修改密码提交 // 修改密码提交
form.on('submit(edit-password)', function (data) { form.on('submit(edit-password)', function (data) {
$.ajax({ $.ajax({
url: '/admin/user/editPassword', url: '/users/editPassword',
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
contentType: 'application/json', contentType: 'application/json',
dataType: 'json', dataType: 'json',
@@ -44,7 +44,7 @@
<label class="layui-form-label">角色</label> <label class="layui-form-label">角色</label>
<div class="layui-input-block"> <div class="layui-input-block">
{% for role in roles %} {% for role in roles %}
<input{% if role.id in checked_roles %} <input {% if role.id in checked_roles %}
checked checked
{% endif %} {% endif %}
value="{{ role.id }}" title="{{ role.name }}" type="checkbox" value="{{ role.id }}" title="{{ role.name }}" type="checkbox"
@@ -57,8 +57,7 @@
</div> </div>
<div class="bottom"> <div class="bottom">
<div class="button-container"> <div class="button-container">
<button type="submit" class="pear-btn pear-btn-primary pear-btn-sm" lay-submit="" <button type="submit" class="pear-btn pear-btn-primary pear-btn-sm" lay-submit="" lay-filter="user-update">
lay-filter="user-update">
<i class="layui-icon layui-icon-ok"></i> <i class="layui-icon layui-icon-ok"></i>
提交 提交
</button> </button>
@@ -94,7 +93,7 @@
roleIds = roleIds.substr(0, roleIds.length - 1) roleIds = roleIds.substr(0, roleIds.length - 1)
data.field.roleIds = roleIds data.field.roleIds = roleIds
$.ajax({ $.ajax({
url: '/admin/user/update', url: '/users/' + data.field.userId,
data: JSON.stringify(data.field), data: JSON.stringify(data.field),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
+7 -7
View File
@@ -111,7 +111,7 @@
let dtree = layui.dtree let dtree = layui.dtree
let popup = layui.popup let popup = layui.popup
let common = layui.common let common = layui.common
let MODULE_PATH = '/admin/user/' let MODULE_PATH = '/users/'
// 表格数据 // 表格数据
let cols = [ let cols = [
@@ -211,14 +211,14 @@
form.on('switch(user-enable)', function (obj) { form.on('switch(user-enable)', function (obj) {
let operate let operate
if (obj.elem.checked) { if (obj.elem.checked) {
operate = 'enable' operate = 1
} else { } else {
operate = 'disable' operate = 0
} }
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: '/admin/user/' + operate, url: MODULE_PATH + 'enable',
data: JSON.stringify({ userId: this.value }), data: JSON.stringify({ operate: operate, userId: this.value }),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
type: 'put', type: 'put',
@@ -249,7 +249,7 @@
title: '修改', title: '修改',
shade: 0.1, shade: 0.1,
area: ['550px', '500px'], area: ['550px', '500px'],
content: MODULE_PATH + 'edit/' + obj.data['id'] content: MODULE_PATH + obj.data['id']
}) })
} }
@@ -258,7 +258,7 @@
layer.close(index) layer.close(index)
let loading = layer.load() let loading = layer.load()
$.ajax({ $.ajax({
url: MODULE_PATH + 'remove/' + obj.data['id'], url: MODULE_PATH + obj.data['id'],
dataType: 'json', dataType: 'json',
type: 'delete', type: 'delete',
success: function (result) { success: function (result) {
+205 -104
View File
@@ -1,126 +1,227 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>头像上传</title> <title>个人中心</title>
{% include 'admin/common/header.html' %} {% include 'admin/common/header.html' %}
<style>
.layui-form-item {
margin-top: 17px !important;
margin-bottom: 17px !important;
}
</style>
</head> </head>
<body class="pear-container"> <body class="pear-container">
<div class="layui-row layui-col-space15"> <div class="layui-row layui-col-space10">
<div class="layui-col-xs9"> {# 左侧栏 #}
<div style="height:325px;background-color: rgb(247, 247, 247);"> <div class="layui-col-md3">
<img id="sourceImage" src=""> {# 个人信息卡片 #}
<div class="layui-card">
<div class="layui-card-body" style="padding: 25px;">
<div class="text-center layui-text">
<div class="user-info-head" id="userInfoHead">
<img id="avatar" src="{{ user_info.avatar }}" width="115px" height="115px" alt="">
</div>
<h2 style="padding-top: 20px;font-size: 20px;">{{ user_info.realname }}</h2>
<p style="padding-top: 8px;margin-top: 10px;font-size: 13.5px;">China 中国</p>
</div>
</div>
<div style="height: 45px;border-top: 1px whitesmoke solid;text-align: center;line-height: 45px;font-size: 13.5px;">
<span>{{ user_info.remark }}</span>
</div>
</div>
{# 登录信息卡片 #}
<div class="layui-card">
<div class="layui-card-header">
登录记录
</div>
<div class="layui-card-body">
<ul class="list">
{% for log in user_logs %}
<li class="list-item">
<span class="title">{{ log.url }}</span>
<span class="footer">{{ log.create_time }}</span>
</li>
{% endfor %}
</ul>
</div>
</div> </div>
</div> </div>
<div class="layui-col-xs3" style="padding-left:0px;"> {# 右侧栏 #}
<div id="previewImage" <div class="layui-col-md9">
style="width:210px;height:210px;border: 1px solid rgb(200, 200, 200);border-radius: 50%;overflow:hidden;"> {# 个人信息卡片 #}
<div class="layui-card">
<div class="layui-card-header">个人信息</div>
<div class="layui-card-body">
<div class="layui-tab layui-tab-brief">
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-block">
<input value="{{ user_info.username }}" type="text" readonly name="username"
lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">姓名</label>
<div class="layui-input-block">
<input value="{{ user_info.realname }}" type="text" name="realName"
lay-verify="title"
autocomplete="off" placeholder="请输入标题" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea placeholder="请输入描述" name="details"
class="layui-textarea">{{ user_info.remark }}</textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="pear-btn pear-btn-primary pear-btn-sm" lay-submit
lay-filter="user-update">修改资料
</button>
<button class="pear-btn pear-btn-sm edit-password">更改密码</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{# 个人文章记录 #}
<div class="layui-card">
<div class="layui-card-header">
我的文章
</div>
<div class="layui-card-body">
<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
<div class="layui-row layui-col-space10" style="margin: 15px;">
<div class="layui-col-md1">
<img src="{{ url_for('static', filename='admin/admin/images/act.jpg') }}"
style="width: 100%;height: 100%;border-radius: 5px;"/>
</div>
<div class="layui-col-md11" style="height: 80px;">
<div class="title">为什么程序员们愿意在GitHub上开源自己的成果给别人免费使用和学习?</div>
<div class="content">
“Git的精髓在于让所有人的贡献无缝合并。而GitHub的天才之处,在于理解了Git的精髓。”来一句我们程序员们接地气的话:分享是一种快乐~
</div>
<div class="comment">2020-06-12 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 评论 5 点赞 12 转发
4
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="layui-row">
<div class="layui-form-item">
<div class="layui-input-inline layui-btn-container" style="width: auto;vertical-align:top;">
<button class="pear-btn pear-btn-sm pear-btn-primary layui-icon layui-icon-left" cropper-event="rotate"
data-option="-15" title="左旋15°"></button>
<button class="pear-btn pear-btn-sm pear-btn-primary layui-icon layui-icon-right" cropper-event="rotate"
data-option="15" title="右旋15°"></button>
<button class="pear-btn pear-btn-sm pear-btn-danger layui-icon layui-icon-refresh" cropper-event="reset"
title="重置"></button>
<label for="uploadPicture" class="pear-btn pear-btn-sm pear-btn-primary layui-icon layui-icon-upload"
title="选择图片"></label>
<input class="layui-upload-file" id="uploadPicture" type="file" value="选择图片">
</div>
<div class="layui-form-mid layui-word-aux">建议:图片的尺寸宽高比为1:1,大小在5m以内。</div>
</div>
</div>
</body>
{% include 'admin/common/footer.html' %} {% include 'admin/common/footer.html' %}
<script> <script>
layui.use(['jquery', 'layer', 'cropper', 'popup'], function () { layui.use(['element', 'jquery', 'layer', 'form'], function () {
let $ = layui.jquery let $ = layui.jquery
let layer = layui.layer let layer = layui.layer
let cropper = layui.cropper let form = layui.form
let popup = layui.popup // 修改密码页面
$('.edit-password').click(function () {
let options = { layer.open({
aspectRatio: 1 / 1, // 裁剪框比例 type: 2,
preview: '#previewImage', // 预览div title: '修改密码',
viewmode: 1 shade: 0.1,
} area: ['550px', '280px'],
content: '/users/editPassword'
$('#sourceImage').attr('src', parent.layui.$('#avatar').attr('src'))
$('#sourceImage').cropper(options)
window.submitForm = function () {
$('#sourceImage').crossOrigin = 'anonymous'//解决跨域图片问题
$('#sourceImage').cropper('getCroppedCanvas', {
width: 280,
height: 140
}).toBlob(function (blob) {
let timeStamp = Date.parse(new Date())
let fileName = timeStamp + '.jpg'
let formData = new FormData()
formData.append('file', blob, fileName)
formData.append('fileName', fileName)
formData.append('fileToken', timeStamp)
$.ajax({
method: 'post',
url: '/admin/file/upload', //用于文件上传的服务器端请求地址
data: formData,
processData: false,
contentType: false,
success: function (result) {
if (result.success) {
// 修改 avatar 字段
$.ajax({
method: 'put',
url: '/admin/user/updateAvatar',
data: JSON.stringify({ avatar: result.data }),
dataType: 'json',
contentType: 'application/json',
success: function (res) {
if (res.success) {
// 关闭当前弹层
parent.layui.$('#avatar').attr('src', result.data.src)
top.layui.$('#avatar').attr('src', result.data.src)
parent.layer.close(parent.layer.getFrameIndex(window.name))
} else {
popup.failure('上传失败')
}
}
})
} else {
popup.failure('上传失败')
}
}
})
}) })
} return false
})
$('.pear-btn').on('click', function () { // 修改头像
let event = $(this).attr('cropper-event') $('#avatar').click(function () {
if (event === 'rotate') { layer.open({
let option = $(this).attr('data-option') type: 2,
$('#sourceImage').cropper('rotate', option) title: '更换图片',
} else if (event === 'reset') { shade: 0.1,
$('#sourceImage').cropper('reset') area: ['900px', '500px'],
} content: '/users/avatar',
$('#uploadPicture').change(function () { btn: ['确定', '取消'],
let r = new FileReader() yes: function (index, layero) {
let f = this.files[0] window['layui-layer-iframe' + index].submitForm()
let uploadFileSize = f.size / 1024
if (uploadFileSize > 5120) {
parent.layer.msg('上传文件不得超过5m', { icon: 5 })
return false
}
r.readAsDataURL(f)
r.onload = function (e) {
$('#sourceImage')
.cropper('destroy')
.attr('src', this.result)
.cropper(options)
} }
}) })
}) })
// 表单提交更改个人信息数据
form.on('submit(user-update)', function (data) {
$.ajax({
url: '/users/updateInfo',
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'put',
success: function (result) {
if (result.success) {
layer.msg('修改成功', { icon: 1, time: 1000 })
} else {
layer.msg('修改失败', { icon: 2, time: 1000 })
}
}
})
return false
})
}) })
</script> </script>
</body>
</html>
+2 -2
View File
@@ -1,4 +1,4 @@
from applications.view.admin import register_admin_views from applications.view.system import register_system_views
from applications.view.index import register_index_views from applications.view.index import register_index_views
from applications.view.passport import register_passport_views from applications.view.passport import register_passport_views
from applications.view.rights import register_rights_view from applications.view.rights import register_rights_view
@@ -6,7 +6,7 @@ from applications.view.department import register_dept_views
def init_view(app): def init_view(app):
register_admin_views(app) register_system_views(app)
register_index_views(app) register_index_views(app)
register_rights_view(app) register_rights_view(app)
register_passport_views(app) register_passport_views(app)
-21
View File
@@ -1,21 +0,0 @@
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
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)
-58
View File
@@ -1,58 +0,0 @@
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, LogSchema
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():
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()
data = model_to_dicts(Schema=LogSchema, model=log.items)
return table_api(data=data, count=count)
# ==========================================================
# 操作日志
# ==========================================================
@admin_log.get('/operateLog')
@authorize("admin:log:main")
def operate_log():
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()
data = model_to_dicts(Schema=LogSchema, model=log.items)
return table_api(data=data, count=count)
-8
View File
@@ -1,8 +0,0 @@
from flask import Blueprint
admin_curd = Blueprint('adminCurd', __name__, url_prefix='/admin/curd')
@admin_curd.route('/')
def index():
return "功能开发中"
-179
View File
@@ -1,179 +0,0 @@
from flask import Blueprint, render_template, request, jsonify
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.models import DictType, DictData
from applications.common.admin import dict_curd
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():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
type_name = xss_escape(request.args.get('typeName', type=str))
data, count = dict_curd.get_dict_type(page=page, limit=limit, type_name=type_name)
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
res = dict_curd.save_dict_type(req_json=req_json)
if res 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
dict_curd.update_dict_type(req_json)
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 = dict_curd.enable_dict_type_status(_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 = dict_curd.disable_dict_type_status(_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 = dict_curd.delete_type_by_id(_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():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
type_code = xss_escape(request.args.get('typeCode', type=str))
data, count = dict_curd.get_dict_data(page=page, limit=limit, type_code=type_code)
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.get('/dictData/save')
@authorize("admin:dict:add", log=True)
def dict_data_save():
req_json = request.json
res = dict_curd.save_dict_data(req_json=req_json)
if not res:
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 = DictData.query.filter_by(id=_id).first()
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
dict_curd.update_dict_data(req_json)
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 = dict_curd.enable_dict_data_status(_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 = dict_curd.disable_dict_data_status(_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 = dict_curd.delete_data_by_id(id)
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
-82
View File
@@ -1,82 +0,0 @@
import os
from flask import Blueprint, request, render_template, jsonify, current_app
from applications.common.utils.http import fail_api, success_api, table_api
from applications.common.utils.rights import authorize
from applications.extensions import db
from applications.models import Photo
from applications.common.admin import file_curd
admin_file = Blueprint('adminFile', __name__, url_prefix='/admin/file')
# 图片管理
@admin_file.get('/')
@authorize("admin:file:main", log=True)
def index():
return render_template('admin/photo/photo.html')
# 图片数据
@admin_file.get('/table')
@authorize("admin:file:main", log=True)
def table():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
data, count = file_curd.get_photo(page=page, limit=limit)
return table_api(data=data, count=count)
# 上传
@admin_file.get('/upload')
@authorize("admin:file:add", log=True)
def upload():
return render_template('admin/photo/photo_add.html')
# 上传接口
@admin_file.post('/upload')
@authorize("admin:file:add", log=True)
def upload_api():
if 'file' in request.files:
photo = request.files['file']
mime = request.files['file'].content_type
file_url = file_curd.upload_one(photo=photo, mime=mime)
res = {
"msg": "上传成功",
"code": 0,
"success": True,
"data":
{"src": file_url}
}
return jsonify(res)
return fail_api()
# 图片删除
@admin_file.route('/delete', methods=['GET', 'POST'])
@authorize("admin:file:delete", log=True)
def delete():
_id = request.form.get('id')
res = file_curd.delete_photo_by_id(_id)
if res:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
# 图片批量删除
@admin_file.route('/batchRemove', methods=['GET', 'POST'])
@authorize("admin:file:delete", log=True)
def batch_remove():
ids = request.form.getlist('ids[]')
photo_name = Photo.query.filter(Photo.id.in_(ids)).all()
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
for p in photo_name:
os.remove(upload_url + '/' + p.name)
photo = Photo.query.filter(Photo.id.in_(ids)).delete(synchronize_session=False)
db.session.commit()
if photo:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
-91
View File
@@ -1,91 +0,0 @@
import os
import platform
import re
from datetime import datetime
import time
import psutil
from flask import Blueprint, render_template, jsonify
from flask_marshmallow import Marshmallow
from applications.common.utils.rights import authorize
ma = Marshmallow()
admin_monitor_bp = Blueprint('adminMonitor', __name__, url_prefix='/admin/monitor')
# 系统监控
@admin_monitor_bp.get('/')
@authorize("admin:monitor:main", log=True)
def main():
# 主机名称
hostname = platform.node()
# 系统版本
system_version = platform.platform()
# python版本
python_version = platform.python_version()
# 逻辑cpu数量
cpu_count = psutil.cpu_count()
# cup使用率
cpus_percent = psutil.cpu_percent(interval=0.1)
# 内存
memory_information = psutil.virtual_memory()
# 内存使用率
memory_usage = memory_information.percent
memory_used = str(round(memory_information.used / 1024 / 1024))
memory_total = str(round(memory_information.total / 1024 / 1024))
memory_free = str(round(memory_information.free / 1024 / 1024))
# 磁盘信息
disk_partitions_list = []
# 判断是否在容器中
if not os.path.exists('/.dockerenv'):
disk_partitions = psutil.disk_partitions()
for i in disk_partitions:
a = psutil.disk_usage(i.device)
disk_partitions_dict = {
'device': i.device,
'fstype': i.fstype,
'total': str(round(a.total / 1024 / 1024)),
'used': str(round(a.used / 1024 / 1024)),
'free': str(round(a.free / 1024 / 1024)),
'percent': a.percent
}
disk_partitions_list.append(disk_partitions_dict)
# 开机时间
boot_time = datetime.fromtimestamp(psutil.boot_time()).replace(microsecond=0)
up_time = datetime.now().replace(microsecond=0) - boot_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])
# 当前时间
time_now = time.strftime('%H:%M:%S ', time.localtime(time.time()))
return render_template('admin/monitor.html',
hostname=hostname,
system_version=system_version,
python_version=python_version,
cpus_percent=cpus_percent,
memory_usage=memory_usage,
cpu_count=cpu_count,
memory_used=memory_used,
memory_total=memory_total,
memory_free=memory_free,
boot_time=boot_time,
up_time_format=up_time_format,
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)
-118
View File
@@ -1,118 +0,0 @@
from flask import Blueprint, render_template, request, jsonify
from applications.common.admin import power_curd
from applications.common.utils.http import success_api, fail_api
from applications.common.utils.rights import authorize
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.get('/data')
@authorize("admin:power:main", log=True)
def data():
power_data = power_curd.get_power_dict()
res = {
"data": power_data
}
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_data = power_curd.select_parent()
res = {
"status": {"code": 200, "message": "默认"},
"data": power_data
}
return jsonify(res)
# 增加
@admin_power.post('/save')
@authorize("admin:power:add", log=True)
def save():
req = request.json
power_curd.save_power(req)
return success_api(msg="成功")
# 权限编辑
@admin_power.get('/edit/<int:_id>')
@authorize("admin:power:edit", log=True)
def edit(_id):
power = power_curd.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.put('/update')
@authorize("admin:power:edit", log=True)
def update():
res = power_curd.update_power(request.json)
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 = power_curd.enable_status(_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 = power_curd.disable_status(_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):
r = power_curd.remove_power(_id)
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[]')
power_curd.batch_remove(ids)
return success_api(msg="批量删除成功")
-145
View File
@@ -1,145 +0,0 @@
from flask import Blueprint, render_template, request, jsonify
from flask_login import login_required
from applications.common.admin import role_curd
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
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():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
role_name = xss_escape(request.args.get('roleName', type=str))
role_code = xss_escape(request.args.get('roleCode', type=str))
filters = {}
if role_name:
filters["name"] = ('%' + role_name + '%')
if role_code:
filters["code"] = ('%' + role_code + '%')
data, count = role_curd.get_role_data_dict(page=page, limit=limit, filters=filters)
return table_api(data=data, count=count)
# 角色增加
@admin_role.get('/add')
@authorize("admin:role:add", log=True)
@login_required
def add():
return render_template('admin/role/add.html')
# 角色增加
@admin_role.post('/save')
@authorize("admin:role:add", log=True)
def save():
req = request.json
role_curd.add_role(req=req)
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):
powers = role_curd.get_role_power(id)
res = {
"data": powers,
"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_curd.update_role_power(id=role_id, power_list=power_list)
return success_api(msg="授权成功")
# 角色编辑
@admin_role.get('/edit/<int:_id>')
@authorize("admin:role:edit", log=True)
def edit(_id):
role = role_curd.get_role_by_id(_id)
return render_template('admin/role/edit.html', role=role)
# 更新角色
@admin_role.put('/update')
@authorize("admin:role:edit", log=True)
def update():
res = role_curd.update_role(request.json)
if not res:
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')
# print(id)
if id:
res = role_curd.enable_status(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 = role_curd.disable_status(_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):
res = role_curd.remove_role(_id)
if not res:
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[]')
role_curd.batch_remove(ids)
return success_api(msg="批量删除成功")
-190
View File
@@ -1,190 +0,0 @@
from flask import Blueprint, render_template, request
from flask_login import login_required, current_user
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.models import User
from applications.models import Role
from applications.common.admin import user_curd
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():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
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)
filters = {}
if real_name:
filters["realname"] = ('%' + real_name + '%')
if username:
filters["username"] = ('%' + username + '%')
user_data, count = user_curd.get_user_data_dict(page=page, limit=limit, filters=filters, deptId=dept_id)
return table_api(data=user_data, 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 user_curd.is_user_exists(username):
return fail_api(msg="用户已经存在")
_id = user_curd.add_user(username, real_name, password)
user_curd.add_user_role(_id, role_ids)
return success_api(msg="增加成功")
# 删除用户
@admin_user.delete('/remove/<int:_id>')
@authorize("admin:user:remove", log=True)
def delete(_id):
res = user_curd.delete_by_id(_id)
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 = 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.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_curd.update_user(id, username, real_name, dept_id)
user_curd.update_user_role(_id, role_ids)
return success_api(msg="更新成功")
# 个人中心
@admin_user.get('/center')
@login_required
def center():
user_info = current_user
user_logs = user_curd.get_current_user_logs()
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")
if not user_curd.update_avatar(url):
return fail_api(msg="出错啦")
return success_api(msg="修改成功")
# 修改当前用户信息
@admin_user.put('/updateInfo')
@login_required
def update_info():
res_json = request.json
if not user_curd.update_current_user_info(req_json=res_json):
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
return user_curd.edit_password(res_json=res_json)
# 启用用户
@admin_user.put('/enable')
@authorize("admin:user:edit", log=True)
def enable():
_id = request.json.get('userId')
if _id:
res = user_curd.enable_status(_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 = user_curd.disable_status(_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[]')
user_curd.batch_remove(ids)
return success_api(msg="批量删除成功")
+100 -69
View File
@@ -1,13 +1,16 @@
from flask import Blueprint, render_template, request, jsonify from flask import Blueprint, render_template, jsonify, make_response
from marshmallow import INCLUDE from flask_restful import marshal, Api, Resource, reqparse
from applications.common.serialization import dept_fields
from applications.common.utils.http import success_api, fail_api from applications.common.utils.http import success_api, fail_api
from applications.common.utils.rights import authorize from applications.common.utils.rights import authorize
from applications.common.utils.validate import check_data from applications.extensions import db
from applications.models import DeptSchema from applications.models import Dept, User
from applications.common.admin import dept_curd as dept_curd
# from applications.view.company.department import dept_curd
dept_bp = Blueprint('dept', __name__, url_prefix='/dept') dept_bp = Blueprint('dept', __name__, url_prefix='/dept')
dept_api = Api(dept_bp)
def register_dept_views(app): def register_dept_views(app):
@@ -17,96 +20,124 @@ def register_dept_views(app):
@dept_bp.get('/') @dept_bp.get('/')
@authorize("admin:dept:main", log=True) @authorize("admin:dept:main", log=True)
def main(): def main():
return render_template('admin/dept/main.html') return render_template('admin/department/main.html')
@dept_bp.get('/data') @dept_bp.get('/data')
@authorize("admin:dept:main", log=True) @authorize("admin:dept:main", log=True)
def data(): def data():
power_data = dept_curd.get_dept_dict() dept_data = Dept.query.order_by(Dept.sort).all()
res = { res = {
"data": power_data "data": marshal(dept_data, dept_fields)
} }
return jsonify(res) return jsonify(res)
@dept_bp.get('/add') @dept_api.resource('/add')
@authorize("admin:dept:add", log=True) class AddDepartment(Resource):
def add(): @authorize("admin:dept:add", log=True)
return render_template('admin/dept/add.html') def get(self):
return make_response(render_template('admin/department/add.html'))
@authorize("admin:dept:add", log=True)
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('address', type=str)
parser.add_argument('deptName', type=str, dest='dept_name')
parser.add_argument('email', type=str)
parser.add_argument('leader', type=str)
parser.add_argument('parentId', type=int, dest='parent_id')
parser.add_argument('phone', type=str)
parser.add_argument('sort', type=int)
parser.add_argument('status', type=int)
res = parser.parse_args()
dept = Dept(
parent_id=res.parent_id,
dept_name=res.dept_name,
sort=res.sort,
leader=res.leader,
phone=res.phone,
email=res.email,
status=res.status,
address=res.address
)
db.session.add(dept)
db.session.commit()
return success_api(msg="成功")
@dept_bp.get('/tree') @dept_bp.get('/tree')
@authorize("admin:dept:main", log=True) @authorize("admin:dept:main", log=True)
def tree(): def tree():
power_data = dept_curd.get_dept_dict() dept_data = Dept.query.order_by(Dept.sort).all()
res = { res = {
"status": {"code": 200, "message": "默认"}, "status": {"code": 200, "message": "默认"},
"data": power_data "data": marshal(dept_data, dept_fields)
} }
return jsonify(res) return jsonify(res)
@dept_bp.post('/save')
@authorize("admin:dept:add", log=True)
def save():
req = request.json
check_data(DeptSchema(unknown=INCLUDE), req)
dept_curd.save_dept(req)
return success_api(msg="成功")
@dept_bp.get('/edit')
@authorize("admin:dept:edit", log=True)
def edit():
_id = request.args.get("deptId")
dept = dept_curd.get_dept_by_id(_id)
return render_template('admin/dept/edit.html', dept=dept)
# 启用 # 启用
@dept_bp.put('/enable') @dept_bp.put('/enable')
@authorize("admin:dept:edit", log=True) @authorize("admin:dept:edit", log=True)
def enable(): def enable():
_id = request.json.get('deptId') parser = reqparse.RequestParser()
if id: parser.add_argument('deptId', type=int, dest='dept_id', required=True)
res = dept_curd.enable_status(_id) parser.add_argument('operate', type=int, choices=[0, 1], required=True)
res = parser.parse_args()
d = Dept.query.filter_by(id=res.dept_id).update({"status": res.operate})
if d:
db.session.commit()
message = '启用成功' if res.operate else '禁用成功'
return success_api(msg=message)
return fail_api(msg="出错啦")
@dept_api.resource('/edit/<int:dept_id>')
class DeptURD(Resource):
@authorize("admin:dept:edit", log=True)
def get(self, dept_id):
dept = Dept.query.filter_by(id=dept_id).first()
return make_response(render_template('admin/department/edit.html', dept=dept))
@authorize("admin:dept:edit", log=True)
def put(self, dept_id):
parser = reqparse.RequestParser()
parser.add_argument('address', type=str)
parser.add_argument('deptName', type=str, dest='dept_name')
parser.add_argument('email', type=str)
parser.add_argument('leader', type=str)
parser.add_argument('phone', type=str)
parser.add_argument('sort', type=int)
parser.add_argument('status', type=int)
res = parser.parse_args()
data = {
"dept_name": res.dept_name,
"sort": res.sort,
"leader": res.leader,
"phone": res.phone,
"email": res.email,
"status": res.status,
"address": res.address
}
res = Dept.query.filter_by(id=dept_id).update(data)
if not res: if not res:
return fail_api(msg="出错啦") return fail_api(msg="更新失败")
return success_api(msg="启用成功") db.session.commit()
return fail_api(msg="数据错误") return success_api(msg="更新成功")
@authorize("admin:dept:remove", log=True)
# 禁用 def delete(self, dept_id):
@dept_bp.put('/disable') ret = Dept.query.filter_by(id=dept_id).delete()
@authorize("admin:dept:edit", log=True) User.query.filter_by(dept_id=dept_id).update({"dept_id": None})
def dis_enable(): db.session.commit()
_id = request.json.get('deptId') if ret:
if id: return success_api(msg="删除成功")
res = dept_curd.disable_status(_id)
if not res:
return fail_api(msg="出错啦")
return success_api(msg="禁用成功")
return fail_api(msg="数据错误")
@dept_bp.put('/update')
@authorize("admin:dept:edit", log=True)
def update():
req = request.json
check_data(DeptSchema(unknown=INCLUDE), req)
res = dept_curd.update_dept(req)
if not res:
return fail_api(msg="更新失败")
return success_api(msg="更新成功")
@dept_bp.delete('/remove/<int:_id>')
@authorize("admin:dept:remove", log=True)
def remove(_id):
res = dept_curd.remove_dept(_id)
if res:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败") return fail_api(msg="删除失败")
View File
@@ -1,18 +1,8 @@
import os import os
from flask import current_app from flask import current_app
from sqlalchemy import desc
from applications.common.utils.upload import photos from applications.common.utils.upload import photos
from applications.extensions import db from applications.extensions import db
from applications.models import Photo, PhotoSchema from applications.models import Photo
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=PhotoSchema, model=photo.items)
return data, count
def upload_one(photo, mime): def upload_one(photo, mime):
+83
View File
@@ -0,0 +1,83 @@
import os
from flask import Blueprint, request, render_template, jsonify, current_app, make_response
from flask_restful import Api, Resource, marshal
from sqlalchemy import desc
from applications.common.serialization import photo_fields
from applications.common.utils.http import fail_api, success_api, table_api
from applications.common.utils.rights import authorize
from applications.extensions import db
from applications.models import Photo
from applications.view.file._utils import delete_photo_by_id, upload_one
file_bp = Blueprint('file', __name__, url_prefix='/file')
file_api = Api(file_bp)
# 图片管理
@file_bp.get('/')
@authorize("admin:file:main", log=True)
def index():
return render_template('admin/file/photo.html')
# 图片数据
@file_bp.get('/table')
@authorize("admin:file:main", log=True)
def table():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
photo_paginate = Photo.query.order_by(desc(Photo.create_time)).paginate(page=page, per_page=limit, error_out=False)
data = marshal(photo_paginate.items, photo_fields)
return table_api(data=data, count=photo_paginate.total)
@file_api.resource('/upload')
class Upload(Resource):
@authorize("admin:file:add", log=True)
def get(self):
return make_response(render_template('admin/file/photo_add.html'))
@authorize("admin:file:add", log=True)
def post(self):
if 'file' in request.files:
photo = request.files['file']
mime = request.files['file'].content_type
file_url = upload_one(photo=photo, mime=mime)
res = {
"msg": "上传成功",
"code": 0,
"success": True,
"data":
{"src": file_url}
}
return jsonify(res)
return fail_api()
@authorize("admin:file:delete", log=True)
def delete(self):
_id = request.form.get('id')
res = delete_photo_by_id(_id)
if res:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
# 图片批量删除
@file_bp.route('/batchRemove', methods=['GET', 'POST'])
@authorize("admin:file:delete", log=True)
def batch_remove():
ids = request.form.getlist('ids[]')
photo_name = Photo.query.filter(Photo.id.in_(ids)).all()
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
for p in photo_name:
os.remove(upload_url + '/' + p.name)
photo = Photo.query.filter(Photo.id.in_(ids)).delete(synchronize_session=False)
db.session.commit()
if photo:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
+1 -1
View File
@@ -1,6 +1,6 @@
from flask import Blueprint from flask import Blueprint
index_bp = Blueprint('Index', __name__, url_prefix='/') index_bp = Blueprint('index', __name__, url_prefix='/')
from . import index from . import index
+36 -39
View File
@@ -1,12 +1,13 @@
from flask import Blueprint, session, redirect, url_for, render_template, request from flask import Blueprint, session, redirect, url_for, render_template, request, make_response
from flask_login import current_user, login_user, login_required, logout_user from flask_login import current_user, login_user, login_required, logout_user
from flask_restful import Resource, Api, reqparse
from applications.common.admin import index_curd
from applications.common.admin_log import login_log from applications.common.admin_log import login_log
from applications.common.utils.http import fail_api, success_api from applications.common.utils.http import fail_api, success_api
from applications.models import User from applications.models import User
from ._utils import get_captcha_image, add_auth_session
passport_bp = Blueprint('passport', __name__, url_prefix='/passport') passport_bp = Blueprint('passport', __name__, url_prefix='/passport')
passport_api = Api(passport_bp)
def register_passport_views(app): def register_passport_views(app):
@@ -16,54 +17,50 @@ def register_passport_views(app):
# 获取验证码 # 获取验证码
@passport_bp.get('/getCaptcha') @passport_bp.get('/getCaptcha')
def get_captcha(): def get_captcha():
resp, code = index_curd.get_captcha() resp, code = get_captcha_image()
session["code"] = code session["code"] = code
return resp return resp
# 登录 @passport_api.resource('/login')
@passport_bp.get('/login') class Login(Resource):
def login(): login_req = reqparse.RequestParser(bundle_errors=True)
if current_user.is_authenticated: login_req.add_argument('username', type=str, help='请输入用户名', required=True)
return redirect(url_for('admin.index')) login_req.add_argument('password', type=str, help='请输入密码', required=True)
return render_template('admin/login.html') login_req.add_argument('captcha', type=str, help='请输入验证码', required=True)
def get(self):
if current_user.is_authenticated:
return redirect(url_for('admin.index'))
# 登录 return make_response(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')
if not username or not password or not code: def post(self):
return fail_api(msg="用户名或密码没有输入")
s_code = session.get("code", None)
if not all([code, s_code]): req = self.login_req.parse_args()
return fail_api(msg="参数错误")
if code != s_code: s_code = session.get("code", None)
return fail_api(msg="验证码错误")
user = User.query.filter_by(username=username).first()
if user is None: if req.captcha != s_code:
return fail_api(msg="不存在的用户") return fail_api(msg="验证码错误")
user = User.query.filter_by(username=req.username).first()
if user.enable is 0: if user is None:
return fail_api(msg="用户被暂停使用") return fail_api(msg="不存在的用户")
if username == user.username and user.validate_password(password): if user.enable is 0:
# 登录 return fail_api(msg="用户被暂停使用")
login_user(user)
# 记录登录日志 if user.validate_password(req.password):
login_log(request, uid=user.id, is_access=True) # 登录
# 存入权限 login_user(user)
index_curd.add_auth_session() # 记录登录日志
return success_api(msg="登录成功") login_log(request, uid=user.id, is_access=True)
login_log(request, uid=user.id, is_access=False) # 存入权限
return fail_api(msg="用户名或密码错误") add_auth_session()
return success_api(msg="登录成功")
login_log(request, uid=user.id, is_access=False)
return fail_api(msg="用户名或密码错误")
# 退出登录 # 退出登录
+43
View File
@@ -0,0 +1,43 @@
from captcha.image import ImageCaptcha
from io import BytesIO
from PIL import Image
from random import choices
from flask import session, make_response
from flask_login import current_user
def gen_captcha(content='0123456789'):
""" 生成验证码 """
image = ImageCaptcha()
# 获取字符串
captcha_text = "".join(choices(content, k=4))
# 生成图像
captcha_image = Image.open(image.generate(captcha_text))
return captcha_text, captcha_image
# 生成验证码
def get_captcha_image():
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
# 授权路由存入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
+3
View File
@@ -1,8 +1,11 @@
from flask import Blueprint, Flask from flask import Blueprint, Flask
from flask_restful import Api
rights_bp = Blueprint('rights', __name__, url_prefix='/rights') rights_bp = Blueprint('rights', __name__, url_prefix='/rights')
rights_api = Api(rights_bp)
from . import routes from . import routes
from . import right
def register_rights_view(app: Flask): def register_rights_view(app: Flask):
@@ -1,83 +1,15 @@
from io import BytesIO import copy
from flask import session, make_response, current_app from collections import OrderedDict
from flask import current_app
from flask_login import current_user from flask_login import current_user
from flask_restful import marshal
from applications.common.utils.gen_captcha import gen_captcha from applications.common.serialization import power_fields
from applications.models.admin_power import PowerSchema from applications.extensions import db
from applications.models import Power, Role, User
from applications.common.serialization import power2_fields
# 授权路由存入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
def get_render_config(): def get_render_config():
@@ -150,3 +82,73 @@ def get_render_config():
"autoHead": False "autoHead": False
}, header=False) }, header=False)
return config return config
# 生成菜单树
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 p.type == 0 or p.type == 1:
powers.append(p)
power_dict = marshal(powers, power2_fields) # 生成可序列化对象
power_dict.sort(key=lambda x: 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']])
# 删除子节点
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 menu_dict.get(0)
# 选择父节点
def select_power_dict():
power = Power.query.all()
res = marshal(power, power_fields)
res.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
return res
def get_power_dict():
power = Power.query.all()
res = marshal(power, power_fields)
return res
# 删除权限(目前没有判断父节点自动删除子节点)
def remove_power(power_id):
power = Power.query.filter_by(id=power_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=power_id).delete()
db.session.commit()
return r
# 批量删除权限
def batch_remove_power(ids):
for _id in ids:
remove_power(_id)
+161
View File
@@ -0,0 +1,161 @@
from flask import render_template, request, jsonify, make_response
from flask_restful import Resource, reqparse
from . import rights_bp, rights_api
from ._utils import select_power_dict, get_power_dict, batch_remove_power
from ...common.utils.http import success_api, fail_api
from ...common.utils.rights import authorize
from ...extensions import db
from ...models import Power, Role
parser_power = reqparse.RequestParser(bundle_errors=True)
parser_power.add_argument('icon', type=str)
parser_power.add_argument('openType', type=str, dest='open_type')
parser_power.add_argument('parentId', type=str, dest='parent_id')
parser_power.add_argument('powerCode', type=str, dest='power_code')
parser_power.add_argument('powerName', type=str, dest='power_name')
parser_power.add_argument('powerType', type=str, dest='power_type')
parser_power.add_argument('powerUrl', type=str, dest='power_url')
parser_power.add_argument('sort', type=int, dest='sort')
@rights_bp.get('/')
@authorize("admin:power:main", log=True)
def index():
return render_template('admin/rights/main.html')
@rights_bp.get('/data')
@authorize("admin:power:main", log=True)
def data():
power_data = get_power_dict()
res = {
"data": power_data
}
return jsonify(res)
@rights_bp.get('/selectParent')
@authorize("admin:power:main", log=True)
def select_parent():
"""获取选择父节点"""
power_data = select_power_dict()
res = {
"status": {"code": 200, "message": "默认"},
"data": power_data
}
return jsonify(res)
# 启用权限
@rights_bp.put('/enable')
@authorize("admin:power:edit", log=True)
def enable():
parser = reqparse.RequestParser(bundle_errors=True)
parser.add_argument('powerId', dest='power_id', type=int, required=True)
parser.add_argument('operate', type=int, required=True, choices=[0, 1])
res = parser.parse_args()
power = Power.query.filter_by(id=res.power_id).update({"enable": res.operate})
if power:
db.session.commit()
message = "启用成功" if res.operate else '禁用成功'
return success_api(msg=message)
else:
return fail_api(msg="出错啦")
# 批量删除
@rights_bp.delete('/batchRemove')
@authorize("admin:power:remove", log=True)
def batch_remove_view():
ids = request.form.getlist('ids[]')
batch_remove_power(ids)
return success_api(msg="批量删除成功")
@rights_api.resource('/power')
class AddRight(Resource):
@authorize("admin:power:add", log=True)
def get(self):
"""获取增加视图"""
return make_response(render_template('admin/rights/add.html'))
@authorize("admin:power:add", log=True)
def post(self):
res = parser_power.parse_args()
power = Power(
icon=res.icon,
open_type=res.open_type,
parent_id=res.parent_id,
code=res.power_code,
name=res.power_name,
type=res.power_type,
url=res.power_url,
sort=res.sort,
enable=1
)
try:
db.session.add(power)
db.session.commit()
except Exception as e:
print(e)
return fail_api(msg='数据提交失败')
return success_api(msg="成功")
@rights_api.resource('/power/<int:power_id>')
class RightsURD(Resource):
@authorize("admin:power:edit", log=True)
def get(self, power_id):
power = Power.query.filter_by(id=power_id).first()
icon = str(power.icon).split()
if len(icon) == 2:
icon = icon[1]
else:
icon = None
return make_response(render_template('admin/rights/edit.html', power=power, icon=icon))
@authorize("admin:power:remove", log=True)
def delete(self, power_id):
# 删除权限(目前没有判断父节点自动删除子节点)
power = Power.query.filter_by(id=power_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=power_id).delete()
db.session.commit()
if r:
return success_api(msg="删除成功")
else:
return fail_api(msg="删除失败")
@authorize("admin:power:edit", log=True)
def put(self, power_id):
res = parser_power.parse_args()
data = {
"icon": res.icon,
"open_type": res.open_type,
"parent_id": res.parent_id,
"code": res.power_code,
"name": res.power_name,
"type": res.power_type,
"url": res.power_url,
"sort": res.sort
}
power = Power.query.filter_by(id=power_id).update(data)
db.session.commit()
if not power:
return fail_api(msg="更新权限失败")
return success_api(msg="更新权限成功")
+4 -3
View File
@@ -3,18 +3,19 @@ from flask import jsonify
from flask_login import login_required from flask_login import login_required
from . import rights_bp from . import rights_bp
from ...common.admin import index_curd
from ._utils import get_render_config, make_menu_tree
@rights_bp.get('/configs') @rights_bp.get('/configs')
@login_required @login_required
def configs(): def configs():
return index_curd.get_render_config() return get_render_config()
# 菜单 # 菜单
@rights_bp.get('/menu') @rights_bp.get('/menu')
@login_required @login_required
def menu(): def menu():
menu_tree = index_curd.make_menu_tree() menu_tree = make_menu_tree()
return jsonify(menu_tree) return jsonify(menu_tree)
View File
+30
View File
@@ -0,0 +1,30 @@
from applications.extensions import db
from applications.models import Power, Role, User
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_role(ids):
""" 批量删除 """
for _id in ids:
remove_role(_id)
+223
View File
@@ -0,0 +1,223 @@
from flask import Blueprint, render_template, jsonify, make_response
from flask_login import login_required
from flask_restful import Resource, Api, reqparse
from applications.extensions import db
from applications.models import Role, Power
from applications.common.utils.http import table_api, success_api, fail_api
from applications.common.utils.rights import authorize
from flask_restful import reqparse, marshal
from applications.common.serialization import power_fields
from applications.view.roles._utils import remove_role, batch_remove_role
role_bp = Blueprint('role', __name__, url_prefix='/admin/role')
role_api = Api(role_bp)
# 角色而管理
@role_bp.get('/')
@authorize("admin:role:main", log=True)
def main():
return render_template('admin/role/main.html')
# 表格数据
@role_bp.get('/data')
@authorize("admin:role:main", log=True)
def table():
parser = reqparse.RequestParser(bundle_errors=True)
parser.add_argument('page', type=int, default=1)
parser.add_argument('limit', type=int, default=10)
parser.add_argument('roleName', type=str, dest='role_name', default="")
parser.add_argument('roleCode', type=str, dest='role_code', default="")
res = parser.parse_args()
filters = []
if res.role_name:
filters.append(Role.name.like('%' + res.role_name + '%'))
if res.role_code:
filters.append(Role.code.like('%' + res.role_code + '%'))
paginate = Role.query.filter(*filters).paginate(page=res.page, per_page=res.limit, error_out=False)
return table_api(data=[
{
'id': item.id,
'roleName': item.name,
'roleCode': item.code,
'enable': item.enable,
'remark': item.remark,
'details': item.details,
'sort': item.sort,
'create_at': item.create_time,
} for item in paginate.items
], count=paginate.total)
@role_api.resource('/add')
class AddRole(Resource):
@authorize("admin:role:add", log=True)
def get(self):
return make_response(render_template('admin/role/add.html'))
@authorize("admin:role:add", log=True)
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('details', type=str)
parser.add_argument('enable', type=int)
parser.add_argument('roleCode', type=str, dest='role_code')
parser.add_argument('roleName', type=str, dest='role_name')
parser.add_argument('sort', type=int)
res = parser.parse_args()
role = Role(
details=res.details,
enable=res.enable,
code=res.role_code,
name=res.role_name,
sort=res.sort
)
db.session.add(role)
db.session.commit()
return success_api(msg="成功")
# 角色授权操作
@role_bp.get('/power/<int:_id>')
@authorize("admin:role:power", log=True)
def power(_id):
return render_template('admin/role/power.html', id=_id)
@role_api.resource('/role_power/<int:role_id>')
class RolePower(Resource):
@authorize("admin:role:main", log=True)
def get(self, role_id):
# 获取角色权限
role = Role.query.filter_by(id=role_id).first()
# 获取权限列表的 id
check_powers_list = [rp.id for rp in role.power]
powers = Power.query.all() # 获取所有的权限
# power_schema = PowerSchema2(many=True) # 用已继承 ma.ModelSchema 类的自定制类生成序列化类
# 将所有的权限生产可序列化对象 json
# powers = power_schema.dump(powers) # 生成可序列化对象
powers = marshal(powers, power_fields)
for i in powers:
if int(i.get("powerId")) in check_powers_list:
i["checkArr"] = "1"
else:
i["checkArr"] = "0"
res = {
"data": powers,
"status": {"code": 200, "message": "默认"}
}
return jsonify(res)
# 保存角色权限
@authorize("admin:role:edit", log=True)
def put(self, role_id):
parser = reqparse.RequestParser()
parser.add_argument('powerIds', dest='power_ids')
parser.add_argument('roleId', dest='role_id')
res = parser.parse_args()
power_list = res.power_ids.split(',')
""" 更新角色权限 """
role = Role.query.filter_by(id=role_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)
powers = Power.query.filter(Power.id.in_(power_list)).all()
for p in powers:
role.power.append(p)
db.session.commit()
return success_api(msg="授权成功")
# 角色删除
@authorize("admin:role:remove", log=True)
def delete(self, role_id):
print(role_id)
res = remove_role(role_id)
print(res)
if not res:
return fail_api(msg="角色删除失败")
return success_api(msg="角色删除成功")
@role_api.resource('/edit/<int:role_id>')
class EditRole(Resource):
# 角色编辑
@authorize("admin:role:edit", log=True)
def get(self, role_id):
role = Role.query.filter_by(id=role_id).first()
return make_response(render_template('admin/role/edit.html', role=role))
# 更新角色
@authorize("admin:role:edit", log=True)
def put(self, role_id):
parser = reqparse.RequestParser()
parser.add_argument('roleId', dest='role_id', type=int)
parser.add_argument('roleCode', dest='role_code', type=str)
parser.add_argument('roleName', dest='role_name', type=str)
parser.add_argument('sort', type=int)
parser.add_argument('enable', type=int)
parser.add_argument('details', type=str)
res = parser.parse_args()
data = {
"code": res.role_code,
"name": res.role_name,
"sort": res.sort,
"enable": res.enable,
"details": res.details
}
role = Role.query.filter_by(id=role_id).update(data)
db.session.commit()
if not role:
return fail_api(msg="更新角色失败")
return success_api(msg="更新角色成功")
# 启用用户
@role_bp.put('/enable')
@authorize("admin:role:edit", log=True)
def enable():
parser = reqparse.RequestParser()
parser.add_argument('roleId', dest='role_id', required=True, type=int)
parser.add_argument('operate', required=True, type=int)
res = parser.parse_args()
ret = Role.query.filter_by(id=res.role_id).update({"enable": res.operate})
db.session.commit()
message = "启动成功" if res.operate else "禁用成功"
if not ret:
return fail_api(msg="出错啦")
return success_api(msg=message)
# 批量删除
@role_bp.delete('/batchRemove')
@authorize("admin:role:remove", log=True)
@login_required
def batch_remove():
parser = reqparse.RequestParser()
parser.add_argument('ids[]', action='append', dest='ids')
res = parser.parse_args()
batch_remove_role(res.ids)
return success_api(msg="批量删除成功")
+15
View File
@@ -0,0 +1,15 @@
from flask import Flask
from applications.view.system.logs import admin_log
from applications.view.system.index import admin_bp
from applications.view.file.file import file_bp
from applications.view.roles.role import role_bp
from applications.view.users import users_bp
def register_system_views(app: Flask):
app.register_blueprint(admin_bp)
app.register_blueprint(users_bp)
app.register_blueprint(file_bp)
app.register_blueprint(admin_log)
app.register_blueprint(role_bp)
@@ -16,4 +16,4 @@ def index():
@admin_bp.get('/welcome') @admin_bp.get('/welcome')
@login_required @login_required
def welcome(): def welcome():
return render_template('admin/console/console.html') return render_template('admin/console/welcome.html')
+43
View File
@@ -0,0 +1,43 @@
from flask import Blueprint, request, render_template
from sqlalchemy import desc
from flask_restful import marshal
from applications.common.serialization import log_fields
from applications.common.utils.http import table_api
from applications.common.utils.rights import authorize
from applications.models import AdminLog
admin_log = Blueprint('logs', __name__, url_prefix='/logs')
@admin_log.get('/')
@authorize("admin:log:main")
def index():
return render_template('admin/admin_log/main.html')
@admin_log.get('/login_log')
@authorize("admin:log:main")
def login_log():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
log_paginate = AdminLog.query.filter_by(
url='/passport/login').order_by(
desc(AdminLog.create_time)).paginate(
page=page, per_page=limit, error_out=False)
data = marshal(log_paginate.items, log_fields)
return table_api(data=data, count=log_paginate.total)
@admin_log.get('/access_log')
@authorize("admin:log:main")
def operate_log():
page = request.args.get('page', type=int)
limit = request.args.get('limit', type=int)
log_paginate = AdminLog.query.filter(
AdminLog.url != '/passport/login').order_by(
desc(AdminLog.create_time)).paginate(
page=page, per_page=limit, error_out=False)
data = marshal(log_paginate.items, log_fields)
return table_api(data=data, count=log_paginate.total)
+60
View File
@@ -0,0 +1,60 @@
from flask import Blueprint, request, render_template
from flask_restful import Api, reqparse
from applications.common.utils.http import table_api
from applications.common.utils.rights import authorize
from applications.models import Role, User, Dept
users_bp = Blueprint('users', __name__, url_prefix='/users')
user_api = Api(users_bp)
# 用户分页查询
@users_bp.get('/data')
@authorize("admin:user:main", log=True)
def data():
parser = reqparse.RequestParser()
parser.add_argument('page', type=int, default=1)
parser.add_argument('limit', type=int, default=10)
parser.add_argument('realName', type=str, dest='real_name')
parser.add_argument('username', type=str)
parser.add_argument('deptId', type=int, dest='dept_id', default=0)
res = parser.parse_args()
filters = []
if res.real_name:
filters.append(User.realname.like('%' + res.real_name + '%'))
if res.username:
filters.append(User.username.like('%' + res.username + '%'))
if res.dept_id:
filters.append(User.dept_id == res.dept_id)
paginate = User.query.filter(*filters).paginate(page=res.page,
per_page=res.limit,
error_out=False)
dept_name = lambda dept_id: Dept.query.filter_by(id=dept_id).first().dept_name
user_data = [{
'id': item.id,
'username': item.username,
'realname': item.realname,
'enable': item.enable,
'create_at': item.create_at,
'update_at': item.update_at,
'dept': dept_name(item.dept_id),
} for item in paginate.items]
return table_api(data=user_data, count=paginate.total)
# 用户增加
@users_bp.get('/')
@authorize("admin:user:main", log=True)
def get():
return render_template('admin/user/main.html')
from . import profile, _utils
from . import user_view
+55
View File
@@ -0,0 +1,55 @@
from flask import jsonify
from flask_login import current_user
from sqlalchemy import and_, desc
from applications.extensions import db
from applications.models import User
from applications.models import Role
from applications.models import AdminLog
def get_current_user_logs():
""" 获取当前用户日志 """
log = AdminLog.query.filter_by(url='/passport/login').filter_by(uid=current_user.id).order_by(
desc(AdminLog.create_time)).limit(10)
return log
def is_user_exists(username):
""" 判断用户是否存在 """
res = User.query.filter_by(username=username).count()
return bool(res)
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 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()
+86
View File
@@ -0,0 +1,86 @@
# 个人中心
from flask import render_template, request, make_response, jsonify
from flask_login import login_required, current_user
from flask_restful import Resource, reqparse
from sqlalchemy import desc
from applications.common.utils.http import fail_api, success_api
from applications.extensions import db
from applications.models import User, AdminLog
from applications.view.users import users_bp, user_api
@users_bp.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/profile.html', user_info=user_info, user_logs=user_logs)
@user_api.resource('/avatar')
class Avatar(Resource):
"""修改头像"""
def get(self):
return make_response(render_template('admin/user/avatar.html'))
def put(self):
url = request.json.get("avatar").get("src")
ret = User.query.filter_by(id=current_user.id).update({"avatar": url})
db.session.commit()
if not ret:
return fail_api(msg="出错啦")
return success_api(msg="修改成功")
# 修改当前用户信息
@users_bp.put('/updateInfo')
@login_required
def update_info():
parser = reqparse.RequestParser()
parser.add_argument('realname', type=str, dest='real_name')
parser.add_argument('remark', type=str)
parser.add_argument('details', type=str)
res = parser.parse_args()
ret = User.query.filter_by(
id=current_user.id).update({"realname": res.real_name,
"remark": res.details})
if not ret:
return fail_api(msg="出错啦")
return success_api(msg="更新成功")
@user_api.resource('/editPassword')
class EditPassword(Resource):
"""前用户密码"""
@login_required
def get(self):
return make_response(render_template('admin/user/edit_password.html'))
@login_required
def put(self):
parser = reqparse.RequestParser()
parser.add_argument('oldPassword', type=str, required=True, help='旧密码不得为空')
parser.add_argument('newPassword', type=str, required=True, help='新密码不得为空')
parser.add_argument('confirmPassword', type=str, required=True, help='确认密码不能为空')
res = parser.parse_args()
if res.newPassword != res.confirmPassword:
return fail_api(msg='确认密码不一致')
""" 修改当前用户密码 """
is_right = current_user.validate_password(res.oldPassword)
if not is_right:
return jsonify(success=False, msg="旧密码错误")
current_user.set_password(res.newPassword)
db.session.add(current_user)
db.session.commit()
return jsonify(success=True, msg="更改成功")
+129
View File
@@ -0,0 +1,129 @@
from flask import render_template, request, make_response
from flask_restful import Resource, reqparse
from applications.extensions import db
from applications.common.utils.http import fail_api, success_api
from applications.common.utils.rights import authorize
from applications.models import User, Role
from applications.view.users import user_api, users_bp, _utils
@user_api.resource('/add')
class Users(Resource):
"""用户列表数据操作"""
@authorize("admin:user:add", log=True)
def get(self):
roles = Role.query.all()
return make_response(render_template('admin/user/add.html', roles=roles))
@authorize("admin:user:add", log=True)
def post(self):
"""新建单个用户"""
parser = reqparse.RequestParser()
parser.add_argument("roleIds", type=str, dest='role_ids')
parser.add_argument("username", type=str, required=True, help="用户名不能为空")
parser.add_argument("realName", type=str, required=True, help="真实姓名不能为空", dest='real_name')
parser.add_argument("password", type=str, required=True, help="密码不得为空")
res = parser.parse_args()
role_ids = res.role_ids.split(',')
if _utils.is_user_exists(res.username):
return fail_api(msg="用户已经存在")
user = User()
user.username = res.username
user.realname = res.real_name
user.set_password(res.password)
db.session.add(user)
db.session.commit()
""" 增加用户角色 """
user = User.query.filter_by(id=user.id).first()
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="增加成功")
@user_api.resource('/<user_id>')
class CURDUser(Resource):
"""修改用户数据"""
@authorize("admin:user:edit", log=True)
def get(self, user_id):
# 获取编辑用户信息
user = User.query.filter_by(id=user_id).first()
roles = Role.query.all()
checked_roles = []
for r in user.role:
checked_roles.append(r.id)
return make_response(
render_template('admin/user/edit_users.html', user=user, roles=roles, checked_roles=checked_roles))
@authorize("admin:user:remove", log=True)
def delete(self, user_id):
# 删除用户
res = _utils.delete_by_id(user_id)
if not res:
return fail_api(msg="删除失败")
return success_api(msg="删除成功")
@authorize("admin:user:edit", log=True)
def put(self, user_id):
parser = reqparse.RequestParser()
parser.add_argument('roleIds', type=str, dest='role_ids')
parser.add_argument('userId', type=str, dest='user_id')
parser.add_argument('username', type=str)
parser.add_argument('realName', type=str, dest='real_name')
parser.add_argument('deptId', type=str, dest='dept_id')
res = parser.parse_args()
role_ids = res.role_ids.split(',')
# 更新用户数据
User.query.filter_by(id=user_id).update({'username': res.username,
'realname': res.real_name,
'dept_id': res.dept_id})
db.session.commit()
_utils.update_user_role(user_id, role_ids)
return success_api(msg="更新成功")
# 批量删除
@users_bp.delete('/batchRemove')
@authorize("admin:user:remove", log=True)
def batch_remove():
ids = request.form.getlist('ids[]')
_utils.batch_remove(ids)
return success_api(msg="批量删除成功")
@users_bp.put('/enable')
def user_enable():
# 启用或者禁用用户 enable disable
parser = reqparse.RequestParser()
parser.add_argument('userId', type=int, required=True, dest='user_id')
parser.add_argument('operate', type=int, required=True, dest='operate', choices=[0, 1])
res = parser.parse_args()
if res.operate == 1:
user = User.query.filter_by(id=res.user_id).update({"enable": res.operate})
message = success_api(msg="启动成功")
else:
user = User.query.filter_by(id=res.user_id).update({"enable": res.operate})
message = success_api(msg="禁用成功")
if user:
db.session.commit()
else:
return fail_api(msg="出错啦")
return message
-1
View File
@@ -1 +0,0 @@
Generic single-database configuration.
-45
View File
@@ -1,45 +0,0 @@
# A generic, single database configuration.
[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
-96
View File
@@ -1,96 +0,0 @@
from __future__ import with_statement
import logging
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from flask import current_app
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the configs file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
config.set_main_option(
'sqlalchemy.url',
str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%'))
target_metadata = current_app.extensions['migrate'].db.metadata
# other values from the configs, defined by the needs of env.py,
# can be acquired:
# my_important_option = configs.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
# this callback is used to prevent an auto-migration from being generated
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
-24
View File
@@ -1,24 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

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