Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e98b4e656 | ||
|
|
dd97ccf8c4 | ||
|
|
43d2ffa132 | ||
|
|
f4420c211e | ||
|
|
6757c5d595 | ||
|
|
2455351347 | ||
|
|
0672a13c0a | ||
|
|
cc6a96fc94 | ||
|
|
d97b600f88 | ||
|
|
94b05256b3 | ||
|
|
c15d5aff66 | ||
|
|
229cb88179 | ||
|
|
03df5e8014 | ||
|
|
a521fcb7dc | ||
|
|
51f1201bca | ||
|
|
4c8917fe05 | ||
|
|
115d0c0c5a | ||
|
|
328458eda9 | ||
|
|
3dcdf8e1a3 | ||
|
|
d2a2b35317 | ||
|
|
3cf62d4305 |
@@ -1,69 +0,0 @@
|
||||
[tool.commitizen]
|
||||
name = "cz_customize"
|
||||
tag_format = "$version"
|
||||
version_scheme = "pep440"
|
||||
version_provider = "poetry"
|
||||
update_changelog_on_bump = true
|
||||
major_version_zero = true
|
||||
|
||||
[tool.commitizen.customize]
|
||||
message_template = "{{change_type}}{% if scope %}({{scope}}){% endif %}:{% if subject %} {{subject}}{% endif %} {% if footer %} {{footer}}{% endif %}"
|
||||
example = "feature: this feature enable customize through config file"
|
||||
schema = "<type>: <body>"
|
||||
schema_pattern = "(feature|fix|docs|style|refactor|perf|test|revert|build).*?:(\\s.*)"
|
||||
bump_pattern = "^(break|new|fix|hotfix)"
|
||||
bump_map = { "break" = "MAJOR", "new" = "MINOR", "fix" = "PATCH", "hotfix" = "PATCH" }
|
||||
change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"]
|
||||
info_path = "cz_customize_info.txt"
|
||||
info = """
|
||||
This is customized info
|
||||
"""
|
||||
commit_parser = "^(?P<change_type>feature|fix|docs|style|refactor|perf|test|revert|build).*?:\\s(?P<message>.*)?"
|
||||
changelog_pattern = "^(feature|fix|docs|style|refactor|perf|test|revert|build)?(!)?"
|
||||
change_type_map = { "feature" = "Feat", "bug fix" = "Fix" }
|
||||
|
||||
|
||||
# 自定义提交指令
|
||||
[[tool.commitizen.customize.questions]]
|
||||
type = "list"
|
||||
name = "change_type"
|
||||
choices = [
|
||||
{ value = "feature", name = "feature: 新功能" },
|
||||
{ value = "fix", name = "fix: 修复 bug" },
|
||||
{ value = "docs", name = "docs: 文档的修改" },
|
||||
{ value = "style", name = "style: 格式化变动,不影响代码逻辑" },
|
||||
{ value = "refactor", name = "refactor: 重构,即不是新增功能,也不是修改 bug 的代码变动" },
|
||||
{ value = "perf", name = "perf: 性能优化" },
|
||||
{ value = "test", name = "test: 增加测试" },
|
||||
{ value = "revert", name = "revert: 回退" },
|
||||
{ value = "build", name = "build: 打包" },
|
||||
]
|
||||
# choices = ["feature", "fix"] # short version
|
||||
message = "请选择提交类型:"
|
||||
|
||||
|
||||
# 修改内容的范围
|
||||
[[tool.commitizen.customize.questions]]
|
||||
type = "input"
|
||||
name = "scope"
|
||||
message = "请输入修改内容的范围(可选):"
|
||||
|
||||
|
||||
# 自己的提交内容
|
||||
[[tool.commitizen.customize.questions]]
|
||||
type = "input"
|
||||
name = "subject"
|
||||
message = "请输入简要描述(必填):"
|
||||
|
||||
|
||||
# 自己的提交内容
|
||||
[[tool.commitizen.customize.questions]]
|
||||
type = "input"
|
||||
name = "body"
|
||||
message = "请输入详细描述(可选):"
|
||||
|
||||
|
||||
[[tool.commitizen.customize.questions]]
|
||||
type = "input"
|
||||
name = "footer"
|
||||
message = "请输入需要关闭的 issue(可选):"
|
||||
@@ -1,4 +1,3 @@
|
||||
FLASK_DEBUG=True # 开启调试模式
|
||||
FLASK_RUN_PORT=5000 # 设置运行的端口
|
||||
FLASK_RUN_HOST=0.0.0.0 # 设置监听的 ip
|
||||
FLASK_APP="pear_admin:create_app()" # 运行程序
|
||||
FLASK_APP=main.py
|
||||
FLASK_ENV=development
|
||||
FLASK_DEBUG=1
|
||||
+116
-3
@@ -1,5 +1,118 @@
|
||||
.idea/
|
||||
.venv/
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
migrations/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
#ide
|
||||
.idea/
|
||||
@@ -1,28 +0,0 @@
|
||||
repos:
|
||||
- repo: https://github.com/python/black
|
||||
rev: 23.9.1
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.10
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
args: [ "--profile", "black", "--filter-files" ]
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v3.0.3"
|
||||
hooks:
|
||||
- id: prettier
|
||||
exclude: >
|
||||
(?x)^(
|
||||
.*.yaml|
|
||||
package-lock.json|
|
||||
yarn.lock|
|
||||
^.+\.min\.(js|css)$
|
||||
)$
|
||||
stages: [ commit ]
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
rev: 3.5.3
|
||||
hooks:
|
||||
- id: commitizen
|
||||
stages: [ commit-msg ]
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 mkg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,55 +1,212 @@
|
||||
## 本地启动
|
||||
<div align="center">
|
||||
<br/>
|
||||
<br/>
|
||||
<img src="https://gitee.com/pear-admin/Pear-Admin-Layui/raw/master/admin/images/logo.png" width="90px" style="margin-top:30px;"/>
|
||||
<h1 align="center">
|
||||
Pear Admin Flask
|
||||
</h1>
|
||||
<h4 align="center">
|
||||
开 箱 即 用 的 Flask 快 速 开 发 平 台
|
||||
</h4>
|
||||
|
||||
环境要求:python 3.10 以上,安装了 poetry
|
||||
[预 览](http://flask.pearadmin.com) | [官 网](http://www.pearadmin.com/) | [群聊](https://jq.qq.com/?_wv=1027&k=5OdSmve) | [社区](http://forum.pearadmin.com/)
|
||||
|
||||
初始化环境
|
||||
|
||||
```shell
|
||||
poetry install
|
||||
<p align="center">
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/pear%20admin%20flask-1.0.0-green" alt="Pear Admin Layui Version">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Python-3.6+-green.svg" alt="Python Version">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img src="https://img.shields.io/badge/Mysql-5.3.2+-green.svg" alt="Mysql Version">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img width="92%" style="border-radius:10px;margin-top:20px;margin-bottom:20px;box-shadow: 2px 0 6px gray;" src="https://images.gitee.com/uploads/images/2020/1019/104805_042b888c_4835367.png" />
|
||||
</div>
|
||||
|
||||
#### 项目简介
|
||||
>Pear Admin Flask 基于 Flask 的后台管理系统,拥抱应用广泛的python语言,通过使用本系统,即可快速构建你的功能业务
|
||||
>
|
||||
>项目旨在为python开发者提供一个后台管理系统的模板,成为您构建信息管理系统,物联网后台....等等应用时灵活,简单的工具
|
||||
>
|
||||
>各位Python爱好者多多指教
|
||||
|
||||
Pear Admin Flask 分为 Common / Simple 两个版本:
|
||||
|
||||
[** Common 通用版本 **](https://gitee.com/pear-admin/pear-admin-flask/tree/master/)
|
||||
|
||||
[** Simple 简洁版本 **](https://gitee.com/pear-admin/pear-admin-flask/tree/simple/)
|
||||
|
||||
#### 内置功能
|
||||
|
||||
- [x] 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
- [x] 权限管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||
- [x] 角色管理:角色菜单权限分配。
|
||||
- [x] 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
- [x] 登录日志:系统登录日志记录查询包含登录异常。
|
||||
- [x] 服务监控:监视当前系统CPU、内存、磁盘、python版本,运行时长等相关信息。
|
||||
- [x] 文件上传: 图片上传示例
|
||||
- [ ] 代码生成: 构想中....
|
||||
|
||||
#### 项目结构
|
||||
|
||||
```
|
||||
Pear Admin Flask
|
||||
│
|
||||
├─applications
|
||||
│ │
|
||||
│ ├─config
|
||||
│ │ │
|
||||
│ │ ├─ common.py #普通配置
|
||||
│ │ │
|
||||
│ │ └─ database.py #数据库配置
|
||||
│ │
|
||||
│ ├─models
|
||||
│ │ │
|
||||
│ │ └─admin.py #基本模型
|
||||
│ │
|
||||
│ ├─service
|
||||
│ │ │
|
||||
│ │ ├─admin
|
||||
│ │ │ │
|
||||
│ │ │ ├─ file.py #file视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ ├─ index.py #index视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ ├─ power.py #power视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ ├─ role.py #role视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ └─ user.py #user视图的数据操作
|
||||
│ │ │
|
||||
│ │ ├─ admin_log.py #存储日志
|
||||
│ │ │
|
||||
│ │ ├─ deBug.py #deBug工具栏初始化
|
||||
│ │ │
|
||||
│ │ ├─ login.py #flask_login初始化
|
||||
│ │ │
|
||||
│ │ ├─ CaptchaTool.py #验证码
|
||||
│ │ │
|
||||
│ │ ├─ OriginalDb.py #原生sql查询封装
|
||||
│ │ │
|
||||
│ │ ├─ route_auth.py #权限
|
||||
│ │ │
|
||||
│ │ └─ upload.py #上传
|
||||
│ │
|
||||
│ └─views
|
||||
│ │
|
||||
│ ├─admin #前台视图
|
||||
│ │ │
|
||||
│ │ ├─index.py #主视图
|
||||
│ │ │
|
||||
│ │ ├─user.py #用户视图
|
||||
│ │ │
|
||||
│ │ ├─role.py #角色视图
|
||||
│ │ │
|
||||
│ │ ├─power.py #权限视图
|
||||
│ │ │
|
||||
│ │ ├─monitor.py #系统监控
|
||||
│ │ │
|
||||
│ │ ├─file.py #文件上传
|
||||
│ │ │
|
||||
│ │ ├─admin_log.py #系统日志
|
||||
│ │ │
|
||||
│ │ ├─context_processor.py #全局模板函数注册
|
||||
│ │ │
|
||||
│ │ ├─error.py #错误处理
|
||||
│ │ │
|
||||
│ │ └─init.py #蓝图注册
|
||||
│ │
|
||||
│ └─index #前台视图
|
||||
│
|
||||
├─dev #数据库初始化
|
||||
│
|
||||
├─migrations
|
||||
│
|
||||
├─readmes
|
||||
│
|
||||
├─static #静态资源
|
||||
│ │
|
||||
│ └─upload #文件上传地址
|
||||
│
|
||||
└─templates
|
||||
│
|
||||
├─admin #前台模板
|
||||
│
|
||||
├─errors #错误模板
|
||||
│
|
||||
└─index #前台模板
|
||||
```
|
||||
|
||||
初始化数据库
|
||||
|
||||
```shell
|
||||
flask init
|
||||
|
||||
#### 项目安装
|
||||
|
||||
```bash
|
||||
# 下 载
|
||||
git clone https://gitee.com/pear-admin/pear-admin-flask
|
||||
|
||||
# 安 装
|
||||
pip install -r requirement.txt
|
||||
|
||||
# 配 置
|
||||
applications\config\database.py
|
||||
|
||||
```
|
||||
|
||||
启动
|
||||
#### 修改配置
|
||||
|
||||
```python
|
||||
# 主 机
|
||||
HOST = '127.0.0.1'
|
||||
|
||||
# 端 口
|
||||
PORT = '3306'
|
||||
|
||||
# 数 据 库
|
||||
DATABASE = 'PearAdminFlask'
|
||||
|
||||
# 账 户
|
||||
USERNAME = 'root'
|
||||
|
||||
# 密 码
|
||||
PASSWORD = 'root'
|
||||
|
||||
```shell
|
||||
flask run
|
||||
```
|
||||
|
||||
## 贡献指南
|
||||
#### Venv 安装
|
||||
|
||||
如果想参与项目的贡献,提交代码之前需要启用 pre-commit、commitizen 对代码进行校验,运行以下指令即可。
|
||||
```bash
|
||||
python -m venv venv
|
||||
|
||||
初始化 pre-commit
|
||||
|
||||
```shell
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
检查代码是否符合规范
|
||||
#### 运行项目
|
||||
|
||||
```bash
|
||||
# 进 入 目 录
|
||||
cd dev
|
||||
|
||||
# 初 始 化 数 据 库
|
||||
python initDb.py
|
||||
|
||||
# 如 果 报 模 块 路 径 错 误
|
||||
python dev/initDb.py
|
||||
|
||||
```shell
|
||||
git add .
|
||||
```
|
||||
|
||||
```shell
|
||||
pre-commit run --all-files
|
||||
```
|
||||
执行 flask run 命令启动项目
|
||||
|
||||
初始化 commitizen
|
||||
|
||||
```shell
|
||||
pre-commit install -t commit-msg
|
||||
```
|
||||
#### 预览项目
|
||||
|
||||
使用
|
||||
|
||||
```shell
|
||||
cz commit
|
||||
```
|
||||
|
||||
代替 `git commit` 进行提交
|
||||
| | |
|
||||
|---------------------|---------------------|
|
||||
|  |  |
|
||||
| |  |
|
||||
|  |  |
|
||||
@@ -0,0 +1,9 @@
|
||||
# 密钥配置(flask session需要) 记得修改
|
||||
import os
|
||||
|
||||
SECRET_KEY = 'pear-admin-flask'
|
||||
|
||||
UPLOADED_PHOTOS_DEST = 'static/upload'
|
||||
UPLOADED_FILES_ALLOW = ['gif','jpg']
|
||||
|
||||
JSON_AS_ASCII = False
|
||||
@@ -0,0 +1,13 @@
|
||||
# 数据库连接
|
||||
HOST = '127.0.0.1'
|
||||
PORT = '3306'
|
||||
DATABASE = 'PearAdminFlask'
|
||||
USERNAME = 'root'
|
||||
PASSWORD = 'root'
|
||||
|
||||
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,password=PASSWORD, host=HOST,port=PORT, db=DATABASE)
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = DB_URI
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
SQLALCHEMY_ECHO = False
|
||||
SQLALCHEMY_POOL_RECYCLE=8
|
||||
@@ -1,5 +1,3 @@
|
||||
from flask_migrate import Migrate
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
db = SQLAlchemy()
|
||||
migrate = Migrate()
|
||||
@@ -0,0 +1,93 @@
|
||||
import datetime
|
||||
from flask_login import UserMixin
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from applications.models import db
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
__tablename__ = 'admin_user'
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='用户ID')
|
||||
username = db.Column(db.String(20), comment='用户名')
|
||||
realname =db.Column(db.String(20), comment='真实名字')
|
||||
password_hash = db.Column(db.String(128), comment='哈希密码')
|
||||
enable = db.Column(db.Integer, default=0, comment='启用')
|
||||
create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||
update_at = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='创建时间')
|
||||
role = db.relationship('Role',secondary="admin_user_role", backref=db.backref('user'),lazy = 'dynamic')
|
||||
# power = db.relationship('Power',secondary="admin_user_role", backref=db.backref('user'))
|
||||
|
||||
def set_password(self, password):
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def validate_password(self, password):
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
|
||||
# 创建中间表
|
||||
user_role = db.Table(
|
||||
"admin_user_role", # 中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
|
||||
db.Column("user_id", db.Integer, db.ForeignKey("admin_user.id"), comment='用户编号'), # 属性 外键
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
|
||||
)
|
||||
|
||||
|
||||
class Role(db.Model):
|
||||
__tablename__ = 'admin_role'
|
||||
id = db.Column(db.Integer, primary_key=True, comment='角色ID')
|
||||
name = db.Column(db.String(255), comment='角色名称')
|
||||
code = db.Column(db.String(255), comment='角色标识')
|
||||
enable = db.Column(db.Integer, comment='是否启用')
|
||||
remark = db.Column(db.String(255), comment='备注')
|
||||
details = db.Column(db.String(255), comment='详情')
|
||||
sort = db.Column(db.Integer, comment='排序')
|
||||
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||
power = db.relationship('Power',secondary="admin_role_power", backref=db.backref('role'))
|
||||
|
||||
# 创建中间表
|
||||
role_power = db.Table(
|
||||
"admin_role_power", # 中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
|
||||
db.Column("power_id", db.Integer, db.ForeignKey("admin_power.id"), comment='用户编号'), # 属性 外键
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
|
||||
)
|
||||
|
||||
|
||||
class Power(db.Model):
|
||||
__tablename__ = 'admin_power'
|
||||
id = db.Column(db.Integer, primary_key=True, comment='权限编号')
|
||||
name = db.Column(db.String(255), comment='权限名称')
|
||||
type = db.Column(db.String(1), comment='权限类型')
|
||||
code = db.Column(db.String(30), comment='权限标识')
|
||||
url = db.Column(db.String(255), comment='权限路径')
|
||||
open_type = db.Column(db.String(10), comment='打开方式')
|
||||
parent_id = db.Column(db.Integer, comment='父类编号')
|
||||
icon = db.Column(db.String(128), comment='图标')
|
||||
sort = db.Column(db.Integer, comment='排序')
|
||||
create_time = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||
update_time = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||
enable = db.Column(db.Integer, comment='是否开启')
|
||||
|
||||
|
||||
class AdminLog(db.Model):
|
||||
__tablename__ = 'admin_admin_log'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
method = db.Column(db.String(10))
|
||||
uid = db.Column(db.Integer)
|
||||
url = db.Column(db.String(255))
|
||||
desc = db.Column(db.Text)
|
||||
ip = db.Column(db.String(255))
|
||||
success = db.Column(db.Integer)
|
||||
user_agent = db.Column(db.Text)
|
||||
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
|
||||
|
||||
|
||||
class Photo(db.Model):
|
||||
__tablename__ = 'admin_photo'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(255), nullable=False)
|
||||
href = db.Column(db.String(255))
|
||||
mime = db.Column(db.CHAR(50), nullable=False)
|
||||
size = db.Column(db.CHAR(30), nullable=False)
|
||||
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
|
||||
@@ -0,0 +1,26 @@
|
||||
from captcha.image import ImageCaptcha
|
||||
from PIL import Image
|
||||
import random
|
||||
|
||||
|
||||
# 定义随机方法
|
||||
def random_captcha():
|
||||
# 定义一个容器
|
||||
captcha_text = []
|
||||
for i in range(4):
|
||||
# 定义验证码字符
|
||||
c = random.choice(['0', '1', '2', '4', '6', '8'])
|
||||
captcha_text.append(c)
|
||||
# 返回一个随机生成的字符串
|
||||
return ''.join(captcha_text)
|
||||
|
||||
|
||||
# 生成验证码方法
|
||||
def gen_captcha():
|
||||
# 定义图片对象
|
||||
image = ImageCaptcha()
|
||||
# 获取字符串
|
||||
captcha_text = random_captcha()
|
||||
# 生成图像
|
||||
captcha_image = Image.open(image.generate(captcha_text))
|
||||
return captcha_text, captcha_image
|
||||
@@ -0,0 +1,106 @@
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import pymysql
|
||||
from applications.config.database import HOST, USERNAME, PASSWORD, DATABASE, PORT
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": HOST,
|
||||
"port": int(PORT),
|
||||
"user": USERNAME,
|
||||
"password": PASSWORD,
|
||||
"db": DATABASE,
|
||||
"charset": "utf8"
|
||||
}
|
||||
|
||||
|
||||
class SQLManager(object):
|
||||
|
||||
# 初始化实例方法
|
||||
def __init__(self):
|
||||
self.conn = None
|
||||
self.cursor = None
|
||||
self.connect()
|
||||
|
||||
# 连接数据库
|
||||
def connect(self):
|
||||
self.conn = pymysql.connect(
|
||||
host=DB_CONFIG.get('host'),
|
||||
port=DB_CONFIG.get('port'),
|
||||
user=DB_CONFIG.get('user'),
|
||||
passwd=DB_CONFIG.get('password'),
|
||||
db=DB_CONFIG.get('db'),
|
||||
)
|
||||
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
|
||||
|
||||
# 查询多条数据
|
||||
|
||||
def get_list(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchall()
|
||||
return result
|
||||
|
||||
# 查询多条数据后关闭
|
||||
def get_list_close(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchall()
|
||||
self.close()
|
||||
return result
|
||||
|
||||
# 查询单条数据
|
||||
def get_one(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchone()
|
||||
return result
|
||||
|
||||
# 查询单条数据后关闭
|
||||
def get_one_close(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchone()
|
||||
self.close()
|
||||
return result
|
||||
|
||||
# 执行单条SQL语句
|
||||
def moddify(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
self.conn.commit()
|
||||
|
||||
# 执行多条SQL语句
|
||||
def multi_modify(self, sql, args=None):
|
||||
self.cursor.executemany(sql, args)
|
||||
self.conn.commit()
|
||||
|
||||
# 创建单条记录的语句
|
||||
def create(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
self.conn.commit()
|
||||
last_id = self.cursor.lastrowid
|
||||
return last_id
|
||||
|
||||
# 关闭数据库cursor和连接
|
||||
def close(self):
|
||||
self.cursor.close()
|
||||
self.conn.close()
|
||||
|
||||
# 进入with语句自动执行
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
# 退出with语句块自动执行
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
|
||||
"""
|
||||
原生查询dict的datetime解析器
|
||||
用法
|
||||
json.dumps(dictxxx, cls=DateEncoder)
|
||||
"""
|
||||
|
||||
|
||||
class DateEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
@@ -0,0 +1,47 @@
|
||||
import os
|
||||
from flask import current_app
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from sqlalchemy import desc
|
||||
from applications.models import db
|
||||
from applications.models.admin import Photo
|
||||
from applications.service.upload import photos
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
class PhotoSchema(ma.Schema):
|
||||
id = fields.Integer()
|
||||
name = fields.Str()
|
||||
href = fields.Str()
|
||||
mime = fields.Str()
|
||||
size = fields.Str()
|
||||
ext = fields.Str()
|
||||
create_time = fields.DateTime()
|
||||
|
||||
|
||||
def get_photo(page, limit):
|
||||
photo = Photo.query.order_by(desc(Photo.create_time)).paginate(page=page, per_page=limit, error_out=False)
|
||||
count = Photo.query.count()
|
||||
role_schema = PhotoSchema(many=True)
|
||||
output = role_schema.dump(photo.items)
|
||||
return output, count
|
||||
|
||||
|
||||
def upload_one(photo,mime):
|
||||
filename = photos.save(photo)
|
||||
file_url = photos.url(filename)
|
||||
|
||||
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
|
||||
size = os.path.getsize(upload_url + '/' + filename)
|
||||
photo = Photo(name=filename, href=file_url, mime=mime, size=size)
|
||||
db.session.add(photo)
|
||||
db.session.commit()
|
||||
return upload_url
|
||||
|
||||
def delete_photo_by_id(id):
|
||||
photo_name = Photo.query.filter_by(id=id).first().name
|
||||
photo = Photo.query.filter_by(id=id).delete()
|
||||
db.session.commit()
|
||||
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
|
||||
os.remove(upload_url + '/' + photo_name)
|
||||
return photo
|
||||
@@ -0,0 +1,93 @@
|
||||
from io import BytesIO
|
||||
from flask import session, make_response
|
||||
from flask_login import current_user
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from applications.models.admin import Power
|
||||
from applications.service.CaptchaTool import gen_captcha
|
||||
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
# 权限models序列化类
|
||||
class PowerSchema(ma.Schema):
|
||||
id = fields.Integer()
|
||||
title = fields.Str(attribute="name")
|
||||
type = fields.Str()
|
||||
code = fields.Str()
|
||||
href = fields.Str(attribute="url")
|
||||
open_type = fields.Str()
|
||||
parent_id = fields.Integer()
|
||||
icon = fields.Str()
|
||||
sort = fields.Integer()
|
||||
create_time = fields.DateTime()
|
||||
update_time = fields.DateTime()
|
||||
enable = fields.Integer()
|
||||
|
||||
|
||||
# 授权路由存入session
|
||||
def add_auth_session():
|
||||
role = current_user.role
|
||||
user_power = []
|
||||
for i in role:
|
||||
if i.enable == 0:
|
||||
continue
|
||||
for p in i.power:
|
||||
if p.enable == 0:
|
||||
continue
|
||||
user_power.append(p.code)
|
||||
session['permissions'] = user_power
|
||||
|
||||
|
||||
# 生成菜单树
|
||||
def make_menu_tree():
|
||||
# power0 = Power.query.filter(
|
||||
# Power.type == 0,
|
||||
# ).all()
|
||||
# power1 = Power.query.filter(
|
||||
# Power.type == 1
|
||||
# ).all()
|
||||
role = current_user.role
|
||||
power0 = []
|
||||
power1 = []
|
||||
for i in role:
|
||||
if i.enable == 0:
|
||||
continue
|
||||
for p in i.power:
|
||||
if p.enable == 0:
|
||||
continue
|
||||
if int(p.type) == 0:
|
||||
power0.append(p)
|
||||
else:
|
||||
power1.append(p)
|
||||
|
||||
power_schema = PowerSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
|
||||
power0_dict = power_schema.dump(power0) # 生成可序列化对象
|
||||
power1_dict = power_schema.dump(power1) # 生成可序列化对象
|
||||
power0_dict = sorted(power0_dict, key=lambda i: i['sort'])
|
||||
power1_dict = sorted(power1_dict, key=lambda i: i['sort'])
|
||||
# print(power0)
|
||||
# print(power1)
|
||||
|
||||
menu = []
|
||||
|
||||
for p0 in power0_dict:
|
||||
for p1 in power1_dict:
|
||||
if p0.get('id') == p1.get('parent_id'):
|
||||
if p0.get("children") is None:
|
||||
p0['children'] = []
|
||||
p0['children'].append(p1)
|
||||
menu.append(p0)
|
||||
return menu
|
||||
|
||||
|
||||
# 生成验证码
|
||||
def get_captcha():
|
||||
code, image = gen_captcha()
|
||||
out = BytesIO()
|
||||
session["code"] = code
|
||||
image.save(out, 'png')
|
||||
out.seek(0)
|
||||
resp = make_response(out.read())
|
||||
resp.content_type = 'image/png'
|
||||
return resp, code
|
||||
@@ -0,0 +1,130 @@
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
|
||||
from applications.models import db
|
||||
from applications.models.admin import Power, Role
|
||||
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
class PowerSchema(ma.Schema): # 序列化类
|
||||
powerId = fields.Str(attribute="id")
|
||||
powerName = fields.Str(attribute="name")
|
||||
powerType = fields.Str(attribute="type")
|
||||
powerUrl = fields.Str(attribute="url")
|
||||
openType = fields.Str(attribute="pen_type")
|
||||
parentId = fields.Str(attribute="parent_id")
|
||||
icon = fields.Str()
|
||||
sort = fields.Integer()
|
||||
create_time = fields.DateTime()
|
||||
update_time = fields.DateTime()
|
||||
enable = fields.Integer()
|
||||
|
||||
|
||||
def get_power_dict():
|
||||
power = Power.query.all()
|
||||
power_schema = PowerSchema(many=True)
|
||||
power_dict = power_schema.dump(power)
|
||||
return power_dict
|
||||
|
||||
|
||||
# 选择父节点
|
||||
def select_parent():
|
||||
power = Power.query.all()
|
||||
power_schema = PowerSchema(many=True)
|
||||
power_dict = power_schema.dump(power)
|
||||
power_dict.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
|
||||
return power_dict
|
||||
|
||||
|
||||
# 增加权限
|
||||
def save_power(req):
|
||||
icon = req.get("icon")
|
||||
openType = req.get("openType")
|
||||
parentId = req.get("parentId")
|
||||
powerCode = req.get("powerCode")
|
||||
powerName = req.get("powerName")
|
||||
powerType = req.get("powerType")
|
||||
powerUrl = req.get("powerUrl")
|
||||
sort = req.get("sort")
|
||||
power = Power(
|
||||
icon=icon,
|
||||
open_type=openType,
|
||||
parent_id=parentId,
|
||||
code=powerCode,
|
||||
name=powerName,
|
||||
type=powerType,
|
||||
url=powerUrl,
|
||||
sort=sort,
|
||||
enable=1
|
||||
)
|
||||
r = db.session.add(power)
|
||||
db.session.commit()
|
||||
return r
|
||||
|
||||
|
||||
# 根据id查询权限
|
||||
def get_power_by_id(id):
|
||||
p = Power.query.filter_by(id=id).first()
|
||||
return p
|
||||
|
||||
|
||||
# 更新权限
|
||||
def update_power(req_json):
|
||||
id = req_json.get("powerId")
|
||||
data = {
|
||||
"icon": req_json.get("icon"),
|
||||
"open_type": req_json.get("openType"),
|
||||
"parent_id": req_json.get("parentId"),
|
||||
"code": req_json.get("powerCode"),
|
||||
"name": req_json.get("powerName"),
|
||||
"type": req_json.get("powerType"),
|
||||
"url": req_json.get("powerUrl"),
|
||||
"sort": req_json.get("sort")
|
||||
}
|
||||
print(data)
|
||||
power = Power.query.filter_by(id=id).update(data)
|
||||
db.session.commit()
|
||||
print(power)
|
||||
return power
|
||||
|
||||
|
||||
# 启动权限
|
||||
def enable_status(id):
|
||||
enable = 1
|
||||
user = Power.query.filter_by(id=id).update({"enable": enable})
|
||||
if user:
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 停用权限
|
||||
def disable_status(id):
|
||||
enable = 0
|
||||
user = Power.query.filter_by(id=id).update({"enable": enable})
|
||||
if user:
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 删除权限(目前没有判断父节点自动删除子节点)
|
||||
def remove_power(id):
|
||||
power = Power.query.filter_by(id=id).first()
|
||||
role_id_list = []
|
||||
roles = power.role
|
||||
for role in roles:
|
||||
role_id_list.append(role.id)
|
||||
roles = Role.query.filter(Role.id.in_(role_id_list)).all()
|
||||
for p in roles:
|
||||
power.role.remove(p)
|
||||
r = Power.query.filter_by(id=id).delete()
|
||||
db.session.commit()
|
||||
return r
|
||||
|
||||
|
||||
# 批量删除权限
|
||||
def batch_remove(ids):
|
||||
for id in ids:
|
||||
remove_power(id)
|
||||
@@ -0,0 +1,180 @@
|
||||
from flask import jsonify
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from sqlalchemy import and_
|
||||
|
||||
from applications.models import db
|
||||
from applications.models.admin import Role, Power, User
|
||||
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
class RoleSchema(ma.Schema):
|
||||
id = fields.Integer()
|
||||
roleName = fields.Str(attribute="name")
|
||||
roleCode = fields.Str(attribute="code")
|
||||
enable = fields.Str()
|
||||
remark = fields.Str()
|
||||
details = fields.Str()
|
||||
sort = fields.Integer()
|
||||
create_at = fields.DateTime()
|
||||
update_at = fields.DateTime()
|
||||
|
||||
|
||||
class PowerSchema(ma.Schema): # 序列化类
|
||||
powerId = fields.Str(attribute="id")
|
||||
powerName = fields.Str(attribute="name")
|
||||
powerType = fields.Str(attribute="type")
|
||||
powerUrl = fields.Str(attribute="url")
|
||||
openType = fields.Str(attribute="pen_type")
|
||||
parentId = fields.Str(attribute="parent_id")
|
||||
icon = fields.Str()
|
||||
sort = fields.Integer()
|
||||
create_time = fields.DateTime()
|
||||
update_time = fields.DateTime()
|
||||
enable = fields.Integer()
|
||||
|
||||
|
||||
# 获取角色对象
|
||||
def get_role_data(page, limit, filters):
|
||||
role = Role.query.filter(and_(*[getattr(Role, k).like(v) for k, v in filters.items()])).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
count = Role.query.count()
|
||||
return role, count
|
||||
|
||||
|
||||
# 获取角色dict
|
||||
def get_role_data_dict(page, limit, filters):
|
||||
role, count = get_role_data(page, limit, filters)
|
||||
role_schema = RoleSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
|
||||
output = role_schema.dump(role.items) # 生成可序列化对象
|
||||
return output, count
|
||||
|
||||
|
||||
# 增加角色
|
||||
def add_role(req):
|
||||
details = req.get("details")
|
||||
enable = req.get("enable")
|
||||
roleCode = req.get("roleCode")
|
||||
roleName = req.get("roleName")
|
||||
sort = req.get("sort")
|
||||
role = Role(
|
||||
details=details,
|
||||
enable=enable,
|
||||
code=roleCode,
|
||||
name=roleName,
|
||||
sort=sort
|
||||
)
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
# 通过id获取角色
|
||||
def get_role_by_id(id):
|
||||
r = Role.query.filter_by(id=id).first()
|
||||
return r
|
||||
|
||||
|
||||
# 更新角色
|
||||
def update_role(req_json):
|
||||
id = req_json.get("roleId")
|
||||
data = {
|
||||
"code": req_json.get("roleCode"),
|
||||
"name": req_json.get("roleName"),
|
||||
"sort": req_json.get("sort"),
|
||||
"enable": req_json.get("enable"),
|
||||
"details": req_json.get("details")
|
||||
}
|
||||
print(data)
|
||||
role = Role.query.filter_by(id=id).update(data)
|
||||
db.session.commit()
|
||||
return role
|
||||
|
||||
|
||||
# 获取角色的权限
|
||||
def get_role_power(id):
|
||||
role = Role.query.filter_by(id=id).first()
|
||||
check_powers = role.power
|
||||
check_powers_list = []
|
||||
for cp in check_powers:
|
||||
check_powers_list.append(cp.id)
|
||||
powers = Power.query.all()
|
||||
power_schema = PowerSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
|
||||
output = power_schema.dump(powers) # 生成可序列化对象
|
||||
for i in output:
|
||||
if int(i.get("powerId")) in check_powers_list:
|
||||
i["checkArr"] = "1"
|
||||
else:
|
||||
i["checkArr"] = "0"
|
||||
return output
|
||||
|
||||
|
||||
# 更新角色权限
|
||||
def update_role_power(id, power_list):
|
||||
role = Role.query.filter_by(id=id).first()
|
||||
power_id_list = []
|
||||
for p in role.power:
|
||||
power_id_list.append(p.id)
|
||||
print(p.id)
|
||||
print(power_id_list)
|
||||
powers = Power.query.filter(Power.id.in_(power_id_list)).all()
|
||||
for p in powers:
|
||||
role.power.remove(p)
|
||||
powers = Power.query.filter(Power.id.in_(power_list)).all()
|
||||
for p in powers:
|
||||
role.power.append(p)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
# 启动角色
|
||||
def enable_status(id):
|
||||
enable = 1
|
||||
role = Role.query.filter_by(id=id).update({"enable": enable})
|
||||
if role:
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 停用角色
|
||||
def disable_status(id):
|
||||
enable = 0
|
||||
role = Role.query.filter_by(id=id).update({"enable": enable})
|
||||
if role:
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
# 删除角色
|
||||
def remove_role(id):
|
||||
role = Role.query.filter_by(id=id).first()
|
||||
# 删除该角色的权限
|
||||
power_id_list = []
|
||||
for p in role.power:
|
||||
power_id_list.append(p.id)
|
||||
|
||||
powers = Power.query.filter(Power.id.in_(power_id_list)).all()
|
||||
for p in powers:
|
||||
role.power.remove(p)
|
||||
user_id_list = []
|
||||
for u in role.user:
|
||||
user_id_list.append(u.id)
|
||||
users = User.query.filter(User.id.in_(user_id_list)).all()
|
||||
for u in users:
|
||||
role.user.remove(u)
|
||||
r = Role.query.filter_by(id=id).delete()
|
||||
db.session.commit()
|
||||
return r
|
||||
|
||||
|
||||
# 批量删除
|
||||
def batch_remove(ids):
|
||||
# role = Role.query.filter(Role.id.in_(ids)).delete(synchronize_session=False)
|
||||
# db.session.commit()
|
||||
for id in ids:
|
||||
remove_role(id)
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
from flask import jsonify
|
||||
from flask_login import current_user
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy.sql import exists
|
||||
|
||||
from applications.models import db
|
||||
from applications.models.admin import User, Role
|
||||
from sqlalchemy.orm.dynamic import AppenderQuery
|
||||
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
# 用户models的序列化类
|
||||
class UserSchema(ma.Schema):
|
||||
id = fields.Integer()
|
||||
username = fields.Str()
|
||||
realname = fields.Str()
|
||||
enable = fields.Integer()
|
||||
create_at = fields.DateTime()
|
||||
update_at = fields.DateTime()
|
||||
|
||||
|
||||
'''
|
||||
获取用户的sqlalchemy对象
|
||||
分页器
|
||||
'''
|
||||
|
||||
|
||||
def get_user_data(page, limit, filters):
|
||||
user = User.query.filter(and_(*[getattr(User, k).like(v) for k, v in filters.items()])).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
count = User.query.count()
|
||||
return user, count
|
||||
|
||||
|
||||
'''
|
||||
获取用户的dict数据
|
||||
分页器
|
||||
'''
|
||||
|
||||
|
||||
def get_user_data_dict(page, limit, filters):
|
||||
user, count = get_user_data(page, limit, filters)
|
||||
user_schema = UserSchema(many=True) # 用已继承ma.ModelSchema类的自定制类生成序列化类
|
||||
output = user_schema.dump(user.items) # 生成可序列化对象
|
||||
return output, count
|
||||
|
||||
|
||||
'''
|
||||
通过名称获取用户
|
||||
'''
|
||||
|
||||
|
||||
def get_user_by_name(username):
|
||||
return User.query.filter_by(username=username).first()
|
||||
|
||||
|
||||
# 判断用户是否存在
|
||||
def is_user_exists(username):
|
||||
res = User.query.filter_by(username=username).count()
|
||||
return bool(res)
|
||||
|
||||
|
||||
# 增加用户
|
||||
def add_user(username, realName, password):
|
||||
user = User(username=username, realname=realName)
|
||||
user.set_password(password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return user.id
|
||||
|
||||
|
||||
# 增加用户角色
|
||||
def add_user_role(id, roles_list):
|
||||
user = User.query.filter_by(id=id).first()
|
||||
roles = Role.query.filter(Role.id.in_(roles_list)).all()
|
||||
for r in roles:
|
||||
user.role.append(r)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
# 更新用户信息
|
||||
def update_user(id, username, realname):
|
||||
user = User.query.filter_by(id=id).update({'username': username, 'realname': realname})
|
||||
db.session.commit()
|
||||
return user
|
||||
|
||||
|
||||
def delete_by_id(id):
|
||||
user = User.query.filter_by(id=id).first()
|
||||
roles_id = []
|
||||
for role in user.role:
|
||||
roles_id.append(role.id)
|
||||
roles = Role.query.filter(Role.id.in_(roles_id)).all()
|
||||
for r in roles:
|
||||
user.role.remove(r)
|
||||
res = User.query.filter_by(id=id).delete()
|
||||
db.session.commit()
|
||||
return res
|
||||
|
||||
|
||||
# 启动用户
|
||||
def enable_status(id):
|
||||
enable = 1
|
||||
user = User.query.filter_by(id=id).update({"enable": enable})
|
||||
if user:
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 停用用户
|
||||
def disable_status(id):
|
||||
enable = 0
|
||||
user = User.query.filter_by(id=id).update({"enable": enable})
|
||||
if user:
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 批量删除
|
||||
def batch_remove(ids):
|
||||
for id in ids:
|
||||
delete_by_id(id)
|
||||
|
||||
|
||||
def update_user_role(id, roles_list):
|
||||
user = User.query.filter_by(id=id).first()
|
||||
roles_id = []
|
||||
for role in user.role:
|
||||
roles_id.append(role.id)
|
||||
roles = Role.query.filter(Role.id.in_(roles_id)).all()
|
||||
for r in roles:
|
||||
user.role.remove(r)
|
||||
roles = Role.query.filter(Role.id.in_(roles_list)).all()
|
||||
for r in roles:
|
||||
user.role.append(r)
|
||||
db.session.commit()
|
||||
@@ -0,0 +1,56 @@
|
||||
from flask_login import current_user
|
||||
|
||||
from applications.models import db
|
||||
from applications.models.admin import AdminLog
|
||||
|
||||
|
||||
def login_log(request,uid, is_access):
|
||||
info = {
|
||||
'method': request.method,
|
||||
'url': request.path,
|
||||
'ip': request.remote_addr,
|
||||
'user_agent': request.headers.get('User-Agent'),
|
||||
'desc': request.form.get('username'),
|
||||
'uid': uid,
|
||||
'success': int(is_access)
|
||||
|
||||
}
|
||||
log = AdminLog(
|
||||
url=info.get('url'),
|
||||
ip=info.get('ip'),
|
||||
user_agent=info.get('user_agent'),
|
||||
desc=info.get('desc'),
|
||||
uid=info.get('uid'),
|
||||
method=info.get('method'),
|
||||
success = info.get('success')
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.flush()
|
||||
db.session.commit()
|
||||
return log.id
|
||||
|
||||
|
||||
def admin_log(request, is_access):
|
||||
info = {
|
||||
'method': request.method,
|
||||
'url': request.path,
|
||||
'ip': request.remote_addr,
|
||||
'user_agent': request.headers.get('User-Agent'),
|
||||
'desc': str(dict(request.values)),
|
||||
'uid': current_user.id,
|
||||
'success':int(is_access)
|
||||
|
||||
}
|
||||
log = AdminLog(
|
||||
url=info.get('url'),
|
||||
ip=info.get('ip'),
|
||||
user_agent=info.get('user_agent'),
|
||||
desc=info.get('desc'),
|
||||
uid=info.get('uid'),
|
||||
method=info.get('method'),
|
||||
success=info.get('success')
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
|
||||
return log.id
|
||||
@@ -0,0 +1,6 @@
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
|
||||
def OpenDug(app):
|
||||
toolbar = DebugToolbarExtension()
|
||||
toolbar.init_app(app)
|
||||
@@ -0,0 +1,15 @@
|
||||
from flask_login import LoginManager
|
||||
|
||||
|
||||
def init_flask_login(app):
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
login_manager.login_view = 'adminIndex.login'
|
||||
login_manager.login_message = u'请登录以访问此页面'
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
from applications.models.admin import User
|
||||
user = User.query.get(int(user_id))
|
||||
return user
|
||||
@@ -0,0 +1,40 @@
|
||||
from functools import wraps
|
||||
from flask import abort, request, jsonify, session
|
||||
from flask_login import login_required
|
||||
from applications.service.admin_log import admin_log
|
||||
|
||||
|
||||
def authorize(power: str):
|
||||
def decorator(func):
|
||||
@login_required
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not power in session.get('permissions'):
|
||||
if request.method == 'GET':
|
||||
abort(403)
|
||||
else:
|
||||
return jsonify(success=False, msg="权限不足!")
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def authorize_and_log(power: str):
|
||||
def decorator(func):
|
||||
@login_required
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not power in session['permissions']:
|
||||
admin_log(request=request, is_access=False)
|
||||
if request.method == 'GET':
|
||||
abort(403)
|
||||
else:
|
||||
return jsonify(success=False, msg="权限不足!")
|
||||
admin_log(request=request, is_access=True)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,4 @@
|
||||
from flask_uploads import UploadSet, IMAGES
|
||||
|
||||
|
||||
photos = UploadSet('photos', IMAGES)
|
||||
@@ -0,0 +1,7 @@
|
||||
from applications.views.admin import init_adminViews
|
||||
from applications.views.index import init_indexViews
|
||||
|
||||
|
||||
def init_view(app):
|
||||
init_adminViews(app)
|
||||
init_indexViews(app)
|
||||
@@ -0,0 +1,22 @@
|
||||
from applications.views.admin.index import admin_index
|
||||
from applications.views.admin.user import admin_user
|
||||
from applications.views.admin.file import admin_file
|
||||
from applications.views.admin.monitor import admin_Monitor
|
||||
from applications.views.admin.admin_log import admin_log
|
||||
from applications.views.admin.power import admin_power
|
||||
from applications.views.admin.role import admin_role
|
||||
"""
|
||||
初始化蓝图
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def init_adminViews(app):
|
||||
|
||||
app.register_blueprint(admin_index)
|
||||
app.register_blueprint(admin_user)
|
||||
app.register_blueprint(admin_file)
|
||||
app.register_blueprint(admin_Monitor)
|
||||
app.register_blueprint(admin_log)
|
||||
app.register_blueprint(admin_power)
|
||||
app.register_blueprint(admin_role)
|
||||
@@ -0,0 +1,88 @@
|
||||
from flask import Blueprint, request, render_template, jsonify
|
||||
from flask_login import login_required
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from sqlalchemy import desc
|
||||
from applications.models.admin import AdminLog
|
||||
|
||||
ma = Marshmallow()
|
||||
admin_log = Blueprint('adminLog', __name__, url_prefix='/admin/log')
|
||||
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# ------------------------- 日志管理 --------------------------
|
||||
# ----------------------------------------------------------
|
||||
|
||||
|
||||
@admin_log.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
return render_template('admin/admin_log/main.html')
|
||||
|
||||
|
||||
class LogSchema(ma.Schema): # 序列化类
|
||||
id = fields.Integer()
|
||||
method = fields.Str()
|
||||
uid = fields.Str()
|
||||
url = fields.Str()
|
||||
desc = fields.Str()
|
||||
ip = fields.Str()
|
||||
user_agent = fields.Str()
|
||||
success = fields.Bool()
|
||||
create_time = fields.DateTime()
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# 登录日志
|
||||
# ==========================================================
|
||||
|
||||
|
||||
@admin_log.route('/loginLog')
|
||||
@login_required
|
||||
def loginLog():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
log = AdminLog.query.filter_by(url='/admin/login').order_by(desc(AdminLog.create_time)).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
count = AdminLog.query.filter_by(url='/admin/login').count()
|
||||
role_schema = LogSchema(many=True)
|
||||
output = role_schema.dump(log.items)
|
||||
res = {
|
||||
'msg': "",
|
||||
'code': 0,
|
||||
'data': output,
|
||||
'count': count,
|
||||
'limit': "10"
|
||||
|
||||
}
|
||||
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# 操作日志
|
||||
# ==========================================================
|
||||
|
||||
|
||||
@admin_log.route('/operateLog')
|
||||
@login_required
|
||||
def operateLog():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
log = AdminLog.query.filter(AdminLog.url != '/admin/login').order_by(desc(AdminLog.create_time)).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
count = AdminLog.query.filter(AdminLog.url != '/admin/login').count()
|
||||
role_schema = LogSchema(many=True)
|
||||
output = role_schema.dump(log.items)
|
||||
res = {
|
||||
'msg': "",
|
||||
'code': 0,
|
||||
'data': output,
|
||||
'count': count,
|
||||
'limit': "10"
|
||||
|
||||
}
|
||||
|
||||
return jsonify(res)
|
||||
@@ -0,0 +1,11 @@
|
||||
from flask import session
|
||||
|
||||
|
||||
def init_template_global(app):
|
||||
@app.template_global()
|
||||
def authorize(power):
|
||||
# print(power)
|
||||
# print(session.get('permissions'))
|
||||
return bool(power in session.get('permissions'))
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
from flask import render_template
|
||||
|
||||
|
||||
def init_error(app):
|
||||
|
||||
@app.errorhandler(403)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/403.html'), 403
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return render_template('errors/500.html'), 500
|
||||
@@ -0,0 +1,83 @@
|
||||
import os
|
||||
from flask import Blueprint, request, render_template, jsonify, current_app
|
||||
from flask_login import login_required
|
||||
from applications.models import db
|
||||
from applications.models.admin import Photo
|
||||
from applications.service.admin.file import get_photo, upload_one, delete_photo_by_id
|
||||
from applications.service.route_auth import authorize_and_log
|
||||
|
||||
admin_file = Blueprint('adminFile', __name__, url_prefix='/admin/file')
|
||||
|
||||
|
||||
# 图片管理
|
||||
@admin_file.route('/')
|
||||
@authorize_and_log("admin:file:main")
|
||||
def index():
|
||||
return render_template('admin/photo/photo.html')
|
||||
|
||||
|
||||
# 图片数据
|
||||
@admin_file.route('/table')
|
||||
@authorize_and_log("admin:file:main")
|
||||
def table():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
data, count = get_photo(page=page, limit=limit)
|
||||
res = {
|
||||
'msg': "",
|
||||
'code': 0,
|
||||
'data': data,
|
||||
'count': count,
|
||||
'limit': "10"
|
||||
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
# 上传接口
|
||||
@admin_file.route('/upload', methods=['GET', 'POST'])
|
||||
@authorize_and_log("admin:file:add")
|
||||
def upload():
|
||||
if request.method == 'POST' and '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,
|
||||
"data":
|
||||
{"src": file_url}
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
return render_template('admin/photo/photo_add.html')
|
||||
|
||||
|
||||
# 图片删除
|
||||
@admin_file.route('/delete', methods=['GET', 'POST'])
|
||||
@authorize_and_log("admin:file:delete")
|
||||
def delete():
|
||||
id = request.form.get('id')
|
||||
res = delete_photo_by_id(id)
|
||||
if res:
|
||||
return jsonify(msg="删除成功", code=200)
|
||||
else:
|
||||
return jsonify(msg="删除失败", code=999)
|
||||
|
||||
|
||||
# 图片批量删除
|
||||
@admin_file.route('/batchRemove', methods=['GET', 'POST'])
|
||||
@authorize_and_log("admin:file:delete")
|
||||
def batchRemove():
|
||||
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 jsonify(msg="删除成功", code=200)
|
||||
else:
|
||||
res = {"msg": "删除失败", "code": 999}
|
||||
return res
|
||||
@@ -0,0 +1,89 @@
|
||||
from flask import Blueprint, render_template, jsonify, request, session, redirect, url_for
|
||||
from flask_login import login_user, login_required, logout_user, current_user
|
||||
from applications.service.admin.index import add_auth_session, make_menu_tree, get_captcha
|
||||
from applications.service.admin_log import login_log
|
||||
from applications.models.admin import User
|
||||
|
||||
admin_index = Blueprint('adminIndex', __name__, url_prefix='/admin')
|
||||
|
||||
|
||||
# 首页
|
||||
@admin_index.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
realname = current_user.realname
|
||||
return render_template('admin/index.html', realname=realname)
|
||||
|
||||
|
||||
# 获取验证码
|
||||
@admin_index.route('/getCaptcha', methods=["GET"])
|
||||
def getCaptcha():
|
||||
resp,code=get_captcha()
|
||||
session["code"] = code
|
||||
return resp
|
||||
|
||||
|
||||
# 登录
|
||||
@admin_index.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
req = request.form
|
||||
username = req.get('username')
|
||||
password = req.get('password')
|
||||
code = req.get('captcha')
|
||||
|
||||
if not username or not password or not code:
|
||||
return jsonify(msg="用户名或密码没有输入", code=0,success=False)
|
||||
s_code = session.get("code", None)
|
||||
|
||||
if not all([code, s_code]):
|
||||
return jsonify(msg="参数错误", code=0,success=False)
|
||||
|
||||
if code != s_code:
|
||||
return jsonify(msg="验证码错误", code=0,success=False)
|
||||
user = User.query.filter_by(username=username).first()
|
||||
|
||||
if user is None:
|
||||
return jsonify(msg="不存在的用户", code=0,success=False)
|
||||
|
||||
if user.enable is 0:
|
||||
return jsonify(msg="用户被暂停使用", code=0,success=False)
|
||||
|
||||
if username == user.username and user.validate_password(password):
|
||||
# 登录
|
||||
login_user(user)
|
||||
# 记录登录日志
|
||||
login_log(request, uid=user.id, is_access=True)
|
||||
# 存入权限
|
||||
add_auth_session()
|
||||
return jsonify(msg="登录成功", code=1,success=True)
|
||||
login_log(request,uid=user.id, is_access=False)
|
||||
return jsonify(msg="用户名或密码错误", code=0,success=False)
|
||||
print(current_user.is_authenticated)
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('adminIndex.index'))
|
||||
return render_template('admin/login.html')
|
||||
|
||||
|
||||
# 退出登录
|
||||
@admin_index.route('/logout', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
session.pop('permissions')
|
||||
return jsonify(msg="注销成功", success=True)
|
||||
|
||||
|
||||
# 菜单
|
||||
@admin_index.route('/menu')
|
||||
@login_required
|
||||
def menu():
|
||||
menu_tree = make_menu_tree()
|
||||
return jsonify(menu_tree)
|
||||
|
||||
|
||||
# 控制台页面
|
||||
@admin_index.route('/welcome')
|
||||
@login_required
|
||||
def welcome():
|
||||
return render_template('admin/console/console.html')
|
||||
@@ -0,0 +1,74 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
import psutil
|
||||
from flask import Blueprint, render_template
|
||||
from flask_marshmallow import Marshmallow
|
||||
|
||||
from applications.service.route_auth import authorize_and_log
|
||||
|
||||
ma = Marshmallow()
|
||||
admin_Monitor = Blueprint('adminMonitor', __name__, url_prefix='/admin/monitor')
|
||||
|
||||
|
||||
# 系统监控
|
||||
@admin_Monitor.route('/')
|
||||
@authorize_and_log("admin:monitor:main")
|
||||
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])
|
||||
|
||||
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
|
||||
)
|
||||
@@ -0,0 +1,121 @@
|
||||
from flask import Blueprint, render_template, request, jsonify
|
||||
from flask_login import login_required
|
||||
from applications.service.admin.power import get_power_dict, select_parent, save_power, remove_power, get_power_by_id, \
|
||||
update_power, enable_status, disable_status, batch_remove
|
||||
from applications.service.route_auth import authorize_and_log
|
||||
|
||||
admin_power = Blueprint('adminPower', __name__, url_prefix='/admin/power')
|
||||
|
||||
|
||||
@admin_power.route('/')
|
||||
@authorize_and_log("admin:power:main")
|
||||
def index():
|
||||
return render_template('admin/power/main.html')
|
||||
|
||||
|
||||
@admin_power.route('/data')
|
||||
@authorize_and_log("admin:power:main")
|
||||
def data():
|
||||
power_data = get_power_dict()
|
||||
res = {
|
||||
"data": power_data
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
@admin_power.route('/add')
|
||||
@authorize_and_log("admin:power:add")
|
||||
def add():
|
||||
return render_template('admin/power/add.html')
|
||||
|
||||
|
||||
@admin_power.route('/selectParent')
|
||||
@authorize_and_log("admin:power:main")
|
||||
def selectParent():
|
||||
power_data = select_parent()
|
||||
res = {
|
||||
"status": {"code": 200, "message": "默认"},
|
||||
"data": power_data
|
||||
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
# 增加
|
||||
@admin_power.route('/save', methods=['POST'])
|
||||
@authorize_and_log("admin:power:add")
|
||||
def save():
|
||||
req = request.json
|
||||
save_power(req)
|
||||
return jsonify(msg="成功", success=True)
|
||||
|
||||
|
||||
# 权限编辑
|
||||
@admin_power.route('/edit/<int:id>', methods=['GET', 'POST'])
|
||||
@authorize_and_log("admin:power:edit")
|
||||
def edit(id):
|
||||
power = get_power_by_id(id)
|
||||
icon = str(power.icon).split()
|
||||
if len(icon) == 2:
|
||||
icon = icon[1]
|
||||
else:
|
||||
icon = None
|
||||
return render_template('admin/power/edit.html', power=power, icon=icon)
|
||||
|
||||
|
||||
# 权限更新
|
||||
@admin_power.route('/update', methods=['PUT'])
|
||||
@authorize_and_log("admin:power:edit")
|
||||
def update():
|
||||
res = update_power(request.json)
|
||||
if not res:
|
||||
return jsonify(success=False, msg="更新权限失败")
|
||||
return jsonify(success=True, msg="更新权限成功")
|
||||
|
||||
|
||||
# 启用用户
|
||||
@admin_power.route('/enable', methods=['PUT'])
|
||||
@authorize_and_log("admin:power:edit")
|
||||
def enable():
|
||||
id = request.json.get('powerId')
|
||||
print(id)
|
||||
if id:
|
||||
res = enable_status(id)
|
||||
if not res:
|
||||
return jsonify(msg="出错啦", success=False)
|
||||
return jsonify(msg="启动成功", success=True)
|
||||
return jsonify(msg="数据错误", success=False)
|
||||
|
||||
|
||||
# 禁用用户
|
||||
@admin_power.route('/disable', methods=['PUT'])
|
||||
@authorize_and_log("admin:power:edit")
|
||||
def disenable():
|
||||
id = request.json.get('powerId')
|
||||
print(id)
|
||||
if id:
|
||||
res = disable_status(id)
|
||||
if not res:
|
||||
return jsonify(msg="出错啦", success=False)
|
||||
return jsonify(msg="禁用成功", success=True)
|
||||
return jsonify(msg="数据错误", success=False)
|
||||
|
||||
|
||||
# 权限删除
|
||||
@admin_power.route('/remove/<int:id>', methods=['DELETE'])
|
||||
@authorize_and_log("admin:power:remove")
|
||||
def remove(id):
|
||||
r = remove_power(id)
|
||||
if r:
|
||||
return jsonify(success=True, msg="删除成功")
|
||||
else:
|
||||
return jsonify(success=False, msg="删除失败")
|
||||
|
||||
|
||||
# 批量删除
|
||||
@admin_power.route('/batchRemove', methods=['DELETE'])
|
||||
@authorize_and_log("admin:power:remove")
|
||||
def batchRemove():
|
||||
ids = request.form.getlist('ids[]')
|
||||
batch_remove(ids)
|
||||
return jsonify(success=True, msg="批量删除成功")
|
||||
@@ -0,0 +1,153 @@
|
||||
from flask import Blueprint, render_template, request, jsonify
|
||||
from flask_login import login_required
|
||||
|
||||
from applications.service.admin.role import get_role_data_dict, add_role, get_role_power, update_role_power, \
|
||||
get_role_by_id, update_role, remove_role, batch_remove, enable_status, disable_status
|
||||
from applications.service.route_auth import authorize_and_log
|
||||
|
||||
admin_role = Blueprint('adminRole', __name__, url_prefix='/admin/role')
|
||||
|
||||
|
||||
# 用户管理
|
||||
@admin_role.route('/')
|
||||
@authorize_and_log("admin:role:main")
|
||||
def main():
|
||||
return render_template('admin/role/main.html')
|
||||
|
||||
|
||||
# 表格数据
|
||||
@admin_role.route('/data')
|
||||
@authorize_and_log("admin:role:main")
|
||||
def table():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
roleName = request.args.get('roleName', type=str)
|
||||
roleCode = request.args.get('roleCode', type=str)
|
||||
filters = {}
|
||||
if roleName:
|
||||
filters["name"] = ('%' + roleName + '%')
|
||||
if roleCode:
|
||||
filters["code"] = ('%' + roleCode + '%')
|
||||
data, count = get_role_data_dict(page=page, limit=limit, filters=filters)
|
||||
res = {
|
||||
'msg': "",
|
||||
'code': 0,
|
||||
'data': data,
|
||||
'count': count,
|
||||
'limit': "10"
|
||||
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
# 角色增加
|
||||
@admin_role.route('/add')
|
||||
@authorize_and_log("admin:role:add")
|
||||
@login_required
|
||||
def add():
|
||||
return render_template('admin/role/add.html')
|
||||
|
||||
|
||||
# 角色增加
|
||||
@admin_role.route('/save', methods=['POST'])
|
||||
@authorize_and_log("admin:role:add")
|
||||
def save():
|
||||
req = request.json
|
||||
add_role(req=req)
|
||||
return jsonify(msg="成功", success=True)
|
||||
|
||||
|
||||
# 角色授权
|
||||
@admin_role.route('/power/<int:id>')
|
||||
@authorize_and_log("admin:role:power")
|
||||
def power(id):
|
||||
return render_template('admin/role/power.html', id=id)
|
||||
|
||||
|
||||
# 获取角色权限
|
||||
@admin_role.route('/getRolePower/<int:id>')
|
||||
@authorize_and_log("admin:role:main")
|
||||
def getRolePower(id):
|
||||
powers = get_role_power(id)
|
||||
res = {
|
||||
"data": powers,
|
||||
"status": {"code": 200, "message": "默认"}
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
# 保存角色权限
|
||||
@admin_role.route('/saveRolePower', methods=['PUT'])
|
||||
@authorize_and_log("admin:role:edit")
|
||||
def saveRolePower():
|
||||
req_form = request.form
|
||||
powerIds = req_form.get("powerIds")
|
||||
power_list = powerIds.split(',')
|
||||
roleId = req_form.get("roleId")
|
||||
update_role_power(id=roleId, power_list=power_list)
|
||||
return jsonify(success=True, msg="授权成功")
|
||||
|
||||
|
||||
# 角色编辑
|
||||
@admin_role.route('/edit/<int:id>', methods=['GET', 'POST'])
|
||||
@authorize_and_log("admin:role:edit")
|
||||
def edit(id):
|
||||
role = get_role_by_id(id)
|
||||
return render_template('admin/role/edit.html', role=role)
|
||||
|
||||
|
||||
# 更新角色
|
||||
@admin_role.route('/update', methods=['PUT'])
|
||||
@authorize_and_log("admin:role:edit")
|
||||
def update():
|
||||
res = update_role(request.json)
|
||||
if not res:
|
||||
return jsonify(success=False, msg="更新角色失败")
|
||||
return jsonify(success=True, msg="更新角色成功")
|
||||
|
||||
# 启用用户
|
||||
@admin_role.route('/enable', methods=['PUT'])
|
||||
@authorize_and_log("admin:role:edit")
|
||||
def enable():
|
||||
id = request.json.get('roleId')
|
||||
print(id)
|
||||
if id:
|
||||
res = enable_status(id)
|
||||
if not res:
|
||||
return jsonify(msg="出错啦", success=False)
|
||||
return jsonify(msg="启动成功", success=True)
|
||||
return jsonify(msg="数据错误", success=False)
|
||||
|
||||
|
||||
# 禁用用户
|
||||
@admin_role.route('/disable', methods=['PUT'])
|
||||
@authorize_and_log("admin:role:edit")
|
||||
def disenable():
|
||||
id = request.json.get('roleId')
|
||||
print(id)
|
||||
if id:
|
||||
res = disable_status(id)
|
||||
if not res:
|
||||
return jsonify(msg="出错啦", success=False)
|
||||
return jsonify(msg="禁用成功", success=True)
|
||||
return jsonify(msg="数据错误", success=False)
|
||||
|
||||
|
||||
# 角色删除
|
||||
@admin_role.route('/remove/<int:id>', methods=['DELETE'])
|
||||
@authorize_and_log("admin:role:remove")
|
||||
def remove(id):
|
||||
res = remove_role(id)
|
||||
if not res:
|
||||
return jsonify(success=False, msg="角色删除失败")
|
||||
return jsonify(success=True, msg="角色删除成功")
|
||||
|
||||
|
||||
# 批量删除
|
||||
@admin_role.route('/batchRemove', methods=['DELETE'])
|
||||
@authorize_and_log("admin:role:remove")
|
||||
@login_required
|
||||
def batchRemove():
|
||||
ids = request.form.getlist('ids[]')
|
||||
batch_remove(ids)
|
||||
return jsonify(success=True,msg="批量删除成功")
|
||||
@@ -0,0 +1,144 @@
|
||||
from flask import Blueprint, render_template, request, jsonify
|
||||
from flask_login import login_required
|
||||
from applications.models.admin import User, Role
|
||||
from applications.service.admin.user import get_user_data_dict, add_user, delete_by_id, \
|
||||
batch_remove, update_user, update_user_role, is_user_exists, add_user_role, enable_status, disable_status
|
||||
from applications.service.route_auth import authorize_and_log
|
||||
|
||||
admin_user = Blueprint('adminUser', __name__, url_prefix='/admin/user')
|
||||
|
||||
|
||||
# 用户管理
|
||||
@admin_user.route('/')
|
||||
@authorize_and_log("admin:user:main")
|
||||
def main():
|
||||
return render_template('admin/user/main.html')
|
||||
|
||||
|
||||
# 用户分页查询
|
||||
@admin_user.route('/data')
|
||||
@authorize_and_log("admin:user:main")
|
||||
def data():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
realName = request.args.get('realName', type=str)
|
||||
username = request.args.get('username', type=str)
|
||||
filters = {}
|
||||
if realName:
|
||||
filters["realname"] = ('%' + realName + '%')
|
||||
if username:
|
||||
filters["username"] = ('%' + username + '%')
|
||||
data, count = get_user_data_dict(page=page, limit=limit, filters=filters)
|
||||
res = {
|
||||
'msg': "",
|
||||
'code': 0,
|
||||
'data': data,
|
||||
'count': count,
|
||||
'limit': "10"
|
||||
|
||||
}
|
||||
return jsonify(res)
|
||||
|
||||
|
||||
# 用户增加
|
||||
@admin_user.route('/add')
|
||||
@authorize_and_log("admin:user:add")
|
||||
def add():
|
||||
roles = Role.query.all()
|
||||
return render_template('admin/user/add.html', roles=roles)
|
||||
|
||||
|
||||
@admin_user.route('/save', methods=['POST'])
|
||||
@authorize_and_log("admin:user:add")
|
||||
def save():
|
||||
req_json = request.json
|
||||
a = req_json.get("roleIds")
|
||||
username = req_json.get('username')
|
||||
realName = req_json.get('realName')
|
||||
password = req_json.get('password')
|
||||
role_ids = a.split(',')
|
||||
|
||||
if not username or not realName or not password:
|
||||
return jsonify(success=False, msg="账号姓名密码不得为空")
|
||||
|
||||
if is_user_exists(username):
|
||||
return jsonify(success=False, msg="用户已经存在")
|
||||
|
||||
id = add_user(username, realName, password)
|
||||
add_user_role(id, role_ids)
|
||||
|
||||
return jsonify(success=True, msg="增加成功")
|
||||
|
||||
|
||||
# 删除用户
|
||||
@admin_user.route('/remove/<int:id>', methods=['DELETE'])
|
||||
@authorize_and_log("admin:user:remove")
|
||||
def delete(id):
|
||||
res = delete_by_id(id)
|
||||
if not res:
|
||||
return jsonify(msg="删除失败", success=False)
|
||||
return jsonify(msg="删除成功", success=True)
|
||||
|
||||
|
||||
# 编辑用户
|
||||
@admin_user.route('/edit/<int:id>', methods=['GET', 'POST'])
|
||||
@authorize_and_log("admin:user:edit")
|
||||
def edit(id):
|
||||
user = User.query.filter_by(id=id).first()
|
||||
roles = Role.query.all()
|
||||
checked_roles = []
|
||||
for r in user.role:
|
||||
checked_roles.append(r.id)
|
||||
return render_template('admin/user/edit.html', user=user, roles=roles, checked_roles=checked_roles)
|
||||
|
||||
# 编辑用户
|
||||
@admin_user.route('/update', methods=['PUT'])
|
||||
@authorize_and_log("admin:user:edit")
|
||||
def update():
|
||||
req_json = request.json
|
||||
a = req_json.get("roleIds")
|
||||
id = req_json.get("userId")
|
||||
username = req_json.get('username')
|
||||
realName = req_json.get('realName')
|
||||
role_ids = a.split(',')
|
||||
update_user(id, username, realName)
|
||||
update_user_role(id, role_ids)
|
||||
return jsonify(success=True, msg="更新成功")
|
||||
|
||||
|
||||
# 启用用户
|
||||
@admin_user.route('/enable', methods=['PUT'])
|
||||
@authorize_and_log("admin:user:edit")
|
||||
def enable():
|
||||
id = request.json.get('userId')
|
||||
print(id)
|
||||
if id:
|
||||
res = enable_status(id)
|
||||
if not res:
|
||||
return jsonify(msg="出错啦", success=False)
|
||||
return jsonify(msg="启动成功", success=True)
|
||||
return jsonify(msg="数据错误", success=False)
|
||||
|
||||
|
||||
# 禁用用户
|
||||
@admin_user.route('/disable', methods=['PUT'])
|
||||
@authorize_and_log("admin:user:edit")
|
||||
def disenable():
|
||||
id = request.json.get('userId')
|
||||
print(id)
|
||||
if id:
|
||||
res = disable_status(id)
|
||||
if not res:
|
||||
return jsonify(msg="出错啦", success=False)
|
||||
return jsonify(msg="禁用成功", success=True)
|
||||
return jsonify(msg="数据错误", success=False)
|
||||
|
||||
|
||||
# 批量删除
|
||||
@admin_user.route('/batchRemove', methods=['DELETE'])
|
||||
@authorize_and_log("admin:user:remove")
|
||||
def batchRemove():
|
||||
ids = request.form.getlist('ids[]')
|
||||
batch_remove(ids)
|
||||
return jsonify(success=True, msg="批量删除成功")
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
from applications.views.index.index import index_index
|
||||
|
||||
|
||||
def init_indexViews(app):
|
||||
"""
|
||||
初始化蓝图
|
||||
|
||||
"""
|
||||
|
||||
app.register_blueprint(index_index)
|
||||
@@ -0,0 +1,11 @@
|
||||
from flask import Blueprint,render_template
|
||||
|
||||
index_index = Blueprint('Index', __name__, url_prefix='/')
|
||||
from flask_marshmallow import Marshmallow
|
||||
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
@index_index.route('/')
|
||||
def index():
|
||||
return render_template('index/index.html')
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
class BaseConfig:
|
||||
SECRET_KEY = os.getenv("SECRET_KEY", "pear-admin-flask")
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = ""
|
||||
|
||||
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
JWT_TOKEN_LOCATION = ["headers"]
|
||||
JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=7)
|
||||
|
||||
|
||||
class DevelopmentConfig(BaseConfig):
|
||||
"""开发配置"""
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///pear_admin.db"
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
|
||||
class TestingConfig(BaseConfig):
|
||||
"""测试配置"""
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" # 内存数据库
|
||||
|
||||
|
||||
class ProductionConfig(BaseConfig):
|
||||
"""生成环境配置"""
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "mysql://root:root@127.0.0.1:3306/pear_admin"
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
|
||||
config = {"dev": DevelopmentConfig, "test": TestingConfig, "prod": ProductionConfig}
|
||||
@@ -0,0 +1,57 @@
|
||||
import sys
|
||||
sys.path.append('../')
|
||||
import sqlparse
|
||||
import pymysql
|
||||
from applications.config.database import HOST, USERNAME, PASSWORD, DATABASE,PORT
|
||||
|
||||
|
||||
def is_exist_database():
|
||||
db = pymysql.connect(host=HOST,port = int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4')
|
||||
cursor1 = db.cursor()
|
||||
sql = "select * from information_schema.SCHEMATA WHERE SCHEMA_NAME = '%s' ; " % DATABASE
|
||||
res = cursor1.execute(sql)
|
||||
db.close()
|
||||
return res
|
||||
|
||||
|
||||
def init_database():
|
||||
db = pymysql.connect(host=HOST,port = int(PORT), user=USERNAME, password=PASSWORD, charset='utf8mb4')
|
||||
cursor1 = db.cursor()
|
||||
sql = "CREATE DATABASE IF NOT EXISTS %s" % DATABASE
|
||||
res = cursor1.execute(sql)
|
||||
db.close()
|
||||
return res
|
||||
|
||||
|
||||
def execute_fromfile(filename):
|
||||
db = pymysql.connect(host=HOST, user=USERNAME, password=PASSWORD, database=DATABASE, charset='utf8mb4')
|
||||
fd = open(filename, 'r', encoding='utf-8')
|
||||
cursor = db.cursor()
|
||||
sqlfile = fd.read()
|
||||
sqlfile = sqlparse.format(sqlfile, strip_comments=True).strip()
|
||||
|
||||
sqlcommamds = sqlfile.split(';')
|
||||
|
||||
for command in sqlcommamds:
|
||||
try:
|
||||
cursor.execute(command)
|
||||
db.commit()
|
||||
|
||||
except Exception as msg:
|
||||
|
||||
db.rollback()
|
||||
db.close()
|
||||
|
||||
|
||||
def init_db():
|
||||
if is_exist_database():
|
||||
print('数据库已经存在,为防止误初始化,请收动删除 %s 数据库' % str(DATABASE))
|
||||
return
|
||||
if init_database():
|
||||
print('数据库%s创建成功' % str(DATABASE))
|
||||
execute_fromfile('pear.sql')
|
||||
print('表创建成功')
|
||||
print('欢迎使用pear-admin-flask,请使用 flask run 命令启动程序')
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_db()
|
||||
+1144
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
from flask import Flask, session
|
||||
from flask_uploads import configure_uploads, patch_request_class
|
||||
from applications.config import database, common
|
||||
from applications.service.deBug import OpenDug
|
||||
from applications.service.upload import photos
|
||||
from applications.views import init_view
|
||||
from applications.models import db
|
||||
from applications.service.login import init_flask_login
|
||||
from applications.views.admin.error import init_error
|
||||
from applications.views.admin.context_processor import init_template_global
|
||||
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
# 注册路由
|
||||
init_view(app)
|
||||
# 注册错误页面
|
||||
init_error(app)
|
||||
# 注册自定义上下文函数
|
||||
init_template_global(app)
|
||||
# 引入数据库配置
|
||||
app.config.from_object(database)
|
||||
app.config.from_object(common)
|
||||
# sqlalchemy初始化
|
||||
db.init_app(app)
|
||||
# flask_login初始化
|
||||
init_flask_login(app)
|
||||
# 文件上传
|
||||
configure_uploads(app, photos)
|
||||
patch_request_class(app)
|
||||
# 调试工具栏
|
||||
OpenDug(app)
|
||||
# logo
|
||||
logo()
|
||||
return app
|
||||
|
||||
|
||||
def logo():
|
||||
print('''
|
||||
_____ _ _ ______ _ _
|
||||
| __ \ /\ | | (_) | ____| | | |
|
||||
| |__) |__ __ _ _ __ / \ __| |_ __ ___ _ _ __ | |__ | | __ _ ___| | __
|
||||
| ___/ _ \/ _` | '__| / /\ \ / _` | '_ ` _ \| | '_ \ | __| | |/ _` / __| |/ /
|
||||
| | | __/ (_| | | / ____ \ (_| | | | | | | | | | | | | | | (_| \__ \ <
|
||||
|_| \___|\__,_|_| /_/ \_\__,_|_| |_| |_|_|_| |_| |_| |_|\__,_|___/_|\_\\
|
||||
|
||||
''')
|
||||
|
||||
|
||||
app = create_app()
|
||||
@@ -0,0 +1,18 @@
|
||||
from flask_migrate import Migrate, MigrateCommand
|
||||
from flask_script import Manager, Server
|
||||
|
||||
from main import app
|
||||
from applications.models import db
|
||||
|
||||
manager = Manager(app)
|
||||
Migrate(app=app, db=db)
|
||||
manager.add_command('db', MigrateCommand) # 创建数据库映射命令
|
||||
|
||||
# import models不能省
|
||||
from applications.models import *
|
||||
|
||||
manager.add_command('start', Server(port=8080, use_debugger=True)) # 创建启动命令
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
manager.run()
|
||||
@@ -0,0 +1 @@
|
||||
Generic single-database configuration.
|
||||
@@ -0,0 +1,45 @@
|
||||
# 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
|
||||
@@ -0,0 +1,96 @@
|
||||
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 config 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 config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.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()
|
||||
@@ -0,0 +1,24 @@
|
||||
"""${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"}
|
||||
@@ -0,0 +1,28 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 58fc2c4c30a5
|
||||
Revises: 715a8ecc5856
|
||||
Create Date: 2021-03-19 21:31:06.761526
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '58fc2c4c30a5'
|
||||
down_revision = '715a8ecc5856'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('admin_role', sa.Column('enable', sa.Integer(), nullable=True, comment='是否启用'))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('admin_role', 'enable')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,96 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 59add0790978
|
||||
Revises:
|
||||
Create Date: 2021-03-11 22:39:09.829159
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '59add0790978'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('admin_admin_log',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('method', sa.String(length=10), nullable=True),
|
||||
sa.Column('uid', sa.Integer(), nullable=True),
|
||||
sa.Column('url', sa.String(length=255), nullable=True),
|
||||
sa.Column('desc', sa.Text(), nullable=True),
|
||||
sa.Column('ip', sa.String(length=255), nullable=True),
|
||||
sa.Column('user_agent', sa.Text(), nullable=True),
|
||||
sa.Column('create_time', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('admin_photo',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('href', sa.String(length=255), nullable=True),
|
||||
sa.Column('mime', sa.CHAR(length=50), nullable=False),
|
||||
sa.Column('size', sa.CHAR(length=30), nullable=False),
|
||||
sa.Column('create_time', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('admin_power',
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='权限编号'),
|
||||
sa.Column('name', sa.String(length=255), nullable=True, comment='权限名称'),
|
||||
sa.Column('type', sa.String(length=1), nullable=True, comment='权限类型'),
|
||||
sa.Column('code', sa.String(length=30), nullable=True, comment='权限标识'),
|
||||
sa.Column('url', sa.String(length=255), nullable=True, comment='权限路径'),
|
||||
sa.Column('open_type', sa.String(length=10), nullable=True, comment='打开方式'),
|
||||
sa.Column('parent_id', sa.String(length=19), nullable=True, comment='父类编号'),
|
||||
sa.Column('icon', sa.String(length=128), nullable=True, comment='图标'),
|
||||
sa.Column('sort', sa.Integer(), nullable=True, comment='排序'),
|
||||
sa.Column('create_time', sa.DateTime(), nullable=True, comment='创建时间'),
|
||||
sa.Column('update_time', sa.DateTime(), nullable=True, comment='更新时间'),
|
||||
sa.Column('enable', sa.Integer(), nullable=True, comment='是否开启'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('admin_role',
|
||||
sa.Column('id', sa.Integer(), nullable=False, comment='角色ID'),
|
||||
sa.Column('name', sa.String(length=255), nullable=True, comment='角色名称'),
|
||||
sa.Column('code', sa.String(length=255), nullable=True, comment='角色标识'),
|
||||
sa.Column('enable', sa.CHAR(length=1), nullable=True, comment='是否启用'),
|
||||
sa.Column('remark', sa.String(length=255), nullable=True, comment='备注'),
|
||||
sa.Column('details', sa.String(length=255), nullable=True, comment='详情'),
|
||||
sa.Column('sort', sa.Integer(), nullable=True, comment='排序'),
|
||||
sa.Column('create_time', sa.DateTime(), nullable=True, comment='创建时间'),
|
||||
sa.Column('update_time', sa.DateTime(), nullable=True, comment='更新时间'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('admin_user',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='用户ID'),
|
||||
sa.Column('username', sa.String(length=20), nullable=True, comment='用户名'),
|
||||
sa.Column('password_hash', sa.String(length=128), nullable=True, comment='哈希密码'),
|
||||
sa.Column('status', sa.Integer(), nullable=True, comment='启用'),
|
||||
sa.Column('create_at', sa.DateTime(), nullable=True, comment='创建时间'),
|
||||
sa.Column('update_at', sa.DateTime(), nullable=True, comment='创建时间'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('admin_user_role',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='标识'),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True, comment='用户编号'),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True, comment='角色编号'),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['admin_role.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['admin_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('admin_user_role')
|
||||
op.drop_table('admin_user')
|
||||
op.drop_table('admin_role')
|
||||
op.drop_table('admin_power')
|
||||
op.drop_table('admin_photo')
|
||||
op.drop_table('admin_admin_log')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,28 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 64de3323bc01
|
||||
Revises: 95b59a101b26
|
||||
Create Date: 2021-03-18 20:54:17.724297
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '64de3323bc01'
|
||||
down_revision = '95b59a101b26'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('admin_user', sa.Column('realname', sa.String(length=20), nullable=True, comment='真实名字'))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('admin_user', 'realname')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,28 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 715a8ecc5856
|
||||
Revises: 64de3323bc01
|
||||
Create Date: 2021-03-19 21:30:52.200812
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '715a8ecc5856'
|
||||
down_revision = '64de3323bc01'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('admin_role', 'enable')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('admin_role', sa.Column('enable', mysql.CHAR(collation='utf8_unicode_ci', length=1), nullable=True, comment='是否启用'))
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,28 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 816f66c2def4
|
||||
Revises: ce8825111f8f
|
||||
Create Date: 2021-03-14 22:57:37.092157
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '816f66c2def4'
|
||||
down_revision = 'ce8825111f8f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('admin_admin_log', sa.Column('success', sa.Integer(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('admin_admin_log', 'success')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,30 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 95b59a101b26
|
||||
Revises: 816f66c2def4
|
||||
Create Date: 2021-03-17 22:28:41.595865
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '95b59a101b26'
|
||||
down_revision = '816f66c2def4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('admin_user', sa.Column('enable', sa.Integer(), nullable=True, comment='启用'))
|
||||
op.drop_column('admin_user', 'status')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('admin_user', sa.Column('status', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True, comment='启用'))
|
||||
op.drop_column('admin_user', 'enable')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,35 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: ce8825111f8f
|
||||
Revises: 59add0790978
|
||||
Create Date: 2021-03-11 22:41:25.156430
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ce8825111f8f'
|
||||
down_revision = '59add0790978'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('admin_role_power',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='标识'),
|
||||
sa.Column('power_id', sa.Integer(), nullable=True, comment='用户编号'),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True, comment='角色编号'),
|
||||
sa.ForeignKeyConstraint(['power_id'], ['admin_power.id'], ),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['admin_role.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('admin_role_power')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
from flask import Flask, render_template
|
||||
|
||||
from configs import config
|
||||
from pear_admin.apis import register_apis
|
||||
from pear_admin.extensions import register_extensions
|
||||
from pear_admin.orms import UserORM
|
||||
from pear_admin.views import register_views
|
||||
|
||||
|
||||
def create_app(config_name="dev"):
|
||||
app = Flask("pear-admin-flask")
|
||||
|
||||
app.config.from_object(config[config_name])
|
||||
|
||||
register_extensions(app)
|
||||
register_apis(app)
|
||||
|
||||
register_views(app)
|
||||
|
||||
# @app.errorhandler(403)
|
||||
# def handle_404(e):
|
||||
# return render_template("error/403.html")
|
||||
#
|
||||
# @app.errorhandler(404)
|
||||
# def handle_403(e):
|
||||
# return render_template("error/404.html")
|
||||
#
|
||||
# @app.errorhandler(500)
|
||||
# def handle_500(e):
|
||||
# return render_template("error/500.html")
|
||||
|
||||
return app
|
||||
@@ -1,19 +0,0 @@
|
||||
from flask import Blueprint, Flask
|
||||
|
||||
from .department import department_api
|
||||
from .passport import passport_api
|
||||
from .rights import rights_api
|
||||
from .role import role_api
|
||||
from .user import user_api
|
||||
|
||||
|
||||
def register_apis(app: Flask):
|
||||
apis = Blueprint("api", __name__, url_prefix="/api/v1")
|
||||
|
||||
apis.register_blueprint(passport_api)
|
||||
apis.register_blueprint(rights_api)
|
||||
apis.register_blueprint(role_api)
|
||||
apis.register_blueprint(department_api)
|
||||
apis.register_blueprint(user_api)
|
||||
|
||||
app.register_blueprint(apis)
|
||||
@@ -1,64 +0,0 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
|
||||
from pear_admin.common.utils.responses import JsonResponse
|
||||
from pear_admin.extensions import db
|
||||
from pear_admin.orms import DepartmentORM
|
||||
|
||||
department_api = Blueprint("department", __name__)
|
||||
|
||||
|
||||
@department_api.get("/department")
|
||||
def department_list():
|
||||
page = request.args.get("page", default=1, type=int)
|
||||
per_page = request.args.get("limit", default=10, type=int)
|
||||
|
||||
_type = request.args.get("type")
|
||||
ret = []
|
||||
if _type == "tree":
|
||||
dept_obj: DepartmentORM = DepartmentORM.query.get(1)
|
||||
for child in dept_obj.children:
|
||||
child_data = child.json()
|
||||
child_data["children"] = []
|
||||
if child.children:
|
||||
child_data["isParent"] = True
|
||||
for son in child.children:
|
||||
son_data = son.json()
|
||||
# son_data["isParent"] = True
|
||||
child_data["children"].append(son_data)
|
||||
ret.append(child_data)
|
||||
return JsonResponse().set_body(ret).set_msg("请求权限数据成功").ok()
|
||||
q = db.select(DepartmentORM)
|
||||
|
||||
pages: Pagination = db.paginate(q, page=page, per_page=per_page)
|
||||
return JsonResponse().set_table(pages).set_msg("获取部门数据成功").ok()
|
||||
|
||||
|
||||
@department_api.post("/department")
|
||||
def create_department():
|
||||
data = request.get_json()
|
||||
if data["id"]:
|
||||
del data["id"]
|
||||
department = DepartmentORM(**data)
|
||||
department.save()
|
||||
return JsonResponse().set_msg("新增部门成功").ok()
|
||||
|
||||
|
||||
@department_api.put("/department/<int:rid>")
|
||||
def change_department(rid):
|
||||
data = request.get_json()
|
||||
del data["id"]
|
||||
|
||||
department_obj = DepartmentORM.query.get(rid)
|
||||
for column in DepartmentORM.__table__.columns:
|
||||
if column.name in data:
|
||||
setattr(department_obj, column.name, data[column.name])
|
||||
department_obj.save()
|
||||
return JsonResponse().set_msg("修改部门成功").ok()
|
||||
|
||||
|
||||
@department_api.delete("/department/<int:rid>")
|
||||
def del_department(rid):
|
||||
department_obj = DepartmentORM.query.get(rid)
|
||||
department_obj.delete()
|
||||
return JsonResponse().set_msg("删除删除成功").ok()
|
||||
@@ -1,80 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
|
||||
from flask import Blueprint, make_response, request
|
||||
from flask_jwt_extended import (
|
||||
create_access_token,
|
||||
create_refresh_token,
|
||||
get_current_user,
|
||||
jwt_required,
|
||||
)
|
||||
|
||||
from pear_admin.common.utils.responses import JsonResponse
|
||||
from pear_admin.extensions import db
|
||||
from pear_admin.orms import RightsORM, RoleORM, UserORM
|
||||
|
||||
passport_api = Blueprint("passport", __name__)
|
||||
|
||||
|
||||
@passport_api.post("/login")
|
||||
def login_in():
|
||||
data = request.get_json()
|
||||
|
||||
user: UserORM = db.session.execute(
|
||||
db.select(UserORM).where(UserORM.username == data["username"])
|
||||
).scalar()
|
||||
|
||||
if not user:
|
||||
return JsonResponse().set_msg("用户不存在").failure(), 401
|
||||
if not user.check_password(data["password"]):
|
||||
return JsonResponse().set_msg("用户密码错误").failure(), 401
|
||||
|
||||
access_token = create_access_token(user)
|
||||
refresh_token = create_refresh_token(user)
|
||||
|
||||
response = make_response(
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "登录成功",
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@passport_api.route("/logout", methods=["GET", "POST"])
|
||||
@jwt_required()
|
||||
def logout():
|
||||
return JsonResponse().set_msg("退出登录成功").ok()
|
||||
|
||||
|
||||
@passport_api.get("/menu")
|
||||
@jwt_required()
|
||||
def menus_api():
|
||||
rights_orm_list = set()
|
||||
current_user: UserORM = get_current_user()
|
||||
for role in current_user.role_list:
|
||||
for rights_orm in role.rights_list:
|
||||
if rights_orm.type != "auth":
|
||||
rights_orm_list.add(rights_orm)
|
||||
|
||||
rights_list = [rights_orm.menu_json() for rights_orm in rights_orm_list]
|
||||
rights_list.sort(key=lambda x: (x["pid"], x["id"]), reverse=True)
|
||||
|
||||
menu_dict_list = OrderedDict()
|
||||
for menu_dict in rights_list:
|
||||
if menu_dict["id"] in menu_dict_list.keys(): # 如果当前节点已经存在与字典中
|
||||
# 当前节点添加子节点
|
||||
menu_dict["children"] = deepcopy(menu_dict_list[menu_dict["id"]])
|
||||
menu_dict["children"].sort(key=lambda item: item["sort"])
|
||||
# 删除子节点
|
||||
del menu_dict_list[menu_dict["id"]]
|
||||
|
||||
if menu_dict["pid"] not in menu_dict_list:
|
||||
menu_dict_list[menu_dict["pid"]] = [menu_dict]
|
||||
else:
|
||||
menu_dict_list[menu_dict["pid"]].append(menu_dict)
|
||||
|
||||
return sorted(menu_dict_list.get(0), key=lambda item: item["sort"])
|
||||
@@ -1,103 +0,0 @@
|
||||
from copy import deepcopy
|
||||
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import jwt_required
|
||||
|
||||
from pear_admin.common.utils.responses import JsonResponse
|
||||
from pear_admin.extensions import db
|
||||
from pear_admin.orms import RightsORM
|
||||
|
||||
rights_api = Blueprint("rights", __name__)
|
||||
|
||||
|
||||
@rights_api.get("/rights")
|
||||
@jwt_required()
|
||||
def rights_list():
|
||||
t = request.args.get("type", default="")
|
||||
if t == "tree": # tree 组件数据,用于权限赋值
|
||||
# 1. 获取所有的权限
|
||||
rights_all = db.session.execute(db.select(RightsORM)).scalars()
|
||||
rights_list = [
|
||||
{"id": r.id, "pid": r.pid, "title": r.name, "sort": r.sort}
|
||||
for r in rights_all
|
||||
]
|
||||
# 2. 获取已有权限 id 集合
|
||||
# 3. 列表转属性组件
|
||||
rights_list.sort(key=lambda item: (item["pid"], item["id"]), reverse=True)
|
||||
tree_dict = {}
|
||||
for rights_dict in rights_list: # 遍历子节点
|
||||
# 2. 如果当前阶段已经存在于树状表格字典,则是父节点
|
||||
if rights_dict["id"] in tree_dict.keys():
|
||||
# 将之前的节点添加到父节点之下
|
||||
rights_dict["children"] = deepcopy(tree_dict[rights_dict["id"]])
|
||||
rights_dict["children"].sort(key=lambda item: item["sort"])
|
||||
del tree_dict[rights_dict["id"]]
|
||||
|
||||
# 1. 如果父节点未出现在树状字典里面,就新增子节点列表,否则就追加
|
||||
if rights_dict["pid"] not in tree_dict.keys():
|
||||
tree_dict[rights_dict["pid"]] = [rights_dict]
|
||||
else:
|
||||
tree_dict[rights_dict["pid"]].append(rights_dict)
|
||||
|
||||
return JsonResponse().set_body(tree_dict.get(0)).ok()
|
||||
|
||||
if t == "treetable": # tree table 数据
|
||||
page = request.args.get("page", type=int, default=1)
|
||||
per_page = request.args.get("per_page", type=int, default=10)
|
||||
|
||||
q = db.select(RightsORM).where(RightsORM.type == "menu")
|
||||
pages = db.paginate(q, page=page, per_page=per_page, error_out=False)
|
||||
|
||||
ret = []
|
||||
|
||||
# 构建树状表格的数据
|
||||
for page in pages.items:
|
||||
data = page.json()
|
||||
data["children"] = []
|
||||
for child in page.children:
|
||||
child_data = child.json()
|
||||
if child.children:
|
||||
child_data["children"] = [
|
||||
sub_child.json() for sub_child in child.children
|
||||
]
|
||||
child_data["isParent"] = True
|
||||
|
||||
data["children"].append(child_data)
|
||||
data["isParent"] = True
|
||||
ret.append(data)
|
||||
return JsonResponse().set_msg("请求权限数据成功").set_body(ret).ok()
|
||||
return JsonResponse().set_msg("请求权限数据成功").ok()
|
||||
|
||||
|
||||
@rights_api.post("/rights")
|
||||
@jwt_required()
|
||||
def create_rights():
|
||||
data = request.get_json()
|
||||
if not data["pid"]:
|
||||
data["pid"] = 0
|
||||
if not data["sort"]:
|
||||
data["sort"] = int(data["sort"])
|
||||
rights_obj = RightsORM(**data)
|
||||
rights_obj.save()
|
||||
return JsonResponse().set_msg("新增权限数据成功").ok()
|
||||
|
||||
|
||||
@rights_api.put("/rights/<int:rid>")
|
||||
@jwt_required()
|
||||
def change_rights(rid):
|
||||
data = request.get_json()
|
||||
del data["id"]
|
||||
|
||||
rights_obj = RightsORM.query.get(rid)
|
||||
for key, value in data.items():
|
||||
setattr(rights_obj, key, value)
|
||||
rights_obj.save()
|
||||
return JsonResponse().set_msg("修改权限数据成功").ok()
|
||||
|
||||
|
||||
@rights_api.delete("/rights/<int:rid>")
|
||||
@jwt_required()
|
||||
def delete_rights(rid):
|
||||
rights_obj = RightsORM.query.get(rid)
|
||||
rights_obj.delete()
|
||||
return JsonResponse().set_msg("删除权限数据成功").ok()
|
||||
@@ -1,73 +0,0 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
|
||||
from pear_admin.common.utils.responses import JsonResponse
|
||||
from pear_admin.extensions import db
|
||||
from pear_admin.orms import RightsORM, RoleORM
|
||||
|
||||
role_api = Blueprint("role", __name__)
|
||||
|
||||
|
||||
@role_api.get("/role")
|
||||
def role_list():
|
||||
page = request.args.get("page", default=1, type=int)
|
||||
per_page = request.args.get("limit", default=10, type=int)
|
||||
q = db.select(RoleORM)
|
||||
|
||||
pages: Pagination = db.paginate(q, page=page, per_page=per_page)
|
||||
return JsonResponse().set_table(pages).set_msg("获取角色数据成功").ok()
|
||||
|
||||
|
||||
@role_api.post("/role")
|
||||
def create_role():
|
||||
data = request.get_json()
|
||||
if data["id"]:
|
||||
del data["id"]
|
||||
role = RoleORM(**data)
|
||||
role.save()
|
||||
return JsonResponse().set_msg("新增角色成功").ok()
|
||||
|
||||
|
||||
@role_api.put("/role/<int:rid>")
|
||||
def change_role(rid):
|
||||
data = request.get_json()
|
||||
del data["id"]
|
||||
|
||||
role_obj = RoleORM.query.get(rid)
|
||||
for key, value in data.items():
|
||||
setattr(role_obj, key, value)
|
||||
role_obj.save()
|
||||
return JsonResponse().set_msg("修改角色权限成功").ok()
|
||||
|
||||
|
||||
@role_api.delete("/role/<int:rid>")
|
||||
def del_role(rid):
|
||||
role_obj = RoleORM.query.get(rid)
|
||||
role_obj.delete()
|
||||
return JsonResponse().set_msg("删除角色成功").ok()
|
||||
|
||||
|
||||
@role_api.get("/role/role_rights/<int:rid>")
|
||||
def role_rights(rid):
|
||||
role: RoleORM = db.session.execute(
|
||||
db.select(RoleORM).where(RoleORM.id == rid)
|
||||
).scalar()
|
||||
own_rights_list = [r.id for r in role.rights_list]
|
||||
|
||||
return JsonResponse().set_body(own_rights_list).set_msg("删除角色成功").ok()
|
||||
|
||||
|
||||
@role_api.put("/role/role_rights/<int:rid>")
|
||||
def change_role_rights(rid):
|
||||
rights_ids = request.json.get("rights_ids", "")
|
||||
|
||||
rights_list = rights_ids.split(",")
|
||||
role: RoleORM = db.session.execute(
|
||||
db.select(RoleORM).where(RoleORM.id == rid)
|
||||
).scalar()
|
||||
rights_obj_list = db.session.execute(
|
||||
db.select(RightsORM).where(RightsORM.id.in_(rights_list))
|
||||
).all()
|
||||
role.rights_list = [r[0] for r in rights_obj_list]
|
||||
role.save()
|
||||
return JsonResponse().set_msg("授权成功").ok()
|
||||
@@ -1,89 +0,0 @@
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import current_user, jwt_required
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
|
||||
from pear_admin.common.utils.responses import JsonResponse
|
||||
from pear_admin.extensions import db
|
||||
from pear_admin.orms import RoleORM, UserORM
|
||||
|
||||
user_api = Blueprint("user", __name__)
|
||||
|
||||
|
||||
@user_api.get("/user")
|
||||
def user_list():
|
||||
page = request.args.get("page", default=1, type=int)
|
||||
per_page = request.args.get("limit", default=10, type=int)
|
||||
q = db.select(UserORM)
|
||||
|
||||
pages: Pagination = db.paginate(q, page=page, per_page=per_page)
|
||||
|
||||
return JsonResponse().set_table(pages).set_msg("获取用户数据成功").ok()
|
||||
|
||||
|
||||
@user_api.post("/user")
|
||||
def create_user():
|
||||
data = request.get_json()
|
||||
if data["id"]:
|
||||
del data["id"]
|
||||
role = UserORM(**data)
|
||||
create_at = data["create_at"]
|
||||
if create_at:
|
||||
role.create_at = datetime.strptime(create_at, "%Y-%m-%d %H:%M:%S")
|
||||
role.password = "123456"
|
||||
role.save()
|
||||
return JsonResponse().set_msg("新增用户成功").ok()
|
||||
|
||||
|
||||
@user_api.put("/user/<int:uid>")
|
||||
def change_user(uid):
|
||||
data = request.get_json()
|
||||
del data["id"]
|
||||
|
||||
user_obj = UserORM.query.get(uid)
|
||||
for key, value in data.items():
|
||||
if key == "create_at":
|
||||
value = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
|
||||
setattr(user_obj, key, value)
|
||||
user_obj.save()
|
||||
return JsonResponse().set_msg("修改用户信息成功").ok()
|
||||
|
||||
|
||||
@user_api.delete("/user/<int:rid>")
|
||||
def del_user(rid):
|
||||
user_obj = UserORM.query.get(rid)
|
||||
user_obj.delete()
|
||||
return JsonResponse().set_msg("删除用户成功").ok()
|
||||
|
||||
|
||||
@user_api.get("/user/user_role/<int:uid>")
|
||||
def get_user_role(uid):
|
||||
user: UserORM = db.session.execute(
|
||||
db.select(UserORM).where(UserORM.id == uid)
|
||||
).scalar()
|
||||
|
||||
wn_role_list = [r.id for r in user.role_list]
|
||||
return JsonResponse().set_body(wn_role_list).set_msg("返回角色权限数据成功").ok()
|
||||
|
||||
|
||||
@user_api.put("/user/user_role/<int:rid>")
|
||||
def change_user_role(rid):
|
||||
role_ids = request.json.get("rights_ids", "")
|
||||
role_list = role_ids.split(",")
|
||||
|
||||
user: UserORM = db.session.execute(
|
||||
db.select(UserORM).where(UserORM.id == rid)
|
||||
).scalar()
|
||||
role_obj_list = db.session.execute(
|
||||
db.select(RoleORM).where(RoleORM.id.in_(role_list))
|
||||
).all()
|
||||
user.role_list = [r[0] for r in role_obj_list]
|
||||
user.save()
|
||||
return JsonResponse().set_msg("授权成功").ok()
|
||||
|
||||
|
||||
@user_api.get("/user/profile")
|
||||
@jwt_required()
|
||||
def user_profile():
|
||||
return JsonResponse().set_body(current_user.json()).set_msg("获取个人数据成功").ok()
|
||||
@@ -1,49 +0,0 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ResponseCodeEnum(Enum):
|
||||
"""状态码枚举类"""
|
||||
|
||||
ok = (0, "成功")
|
||||
|
||||
error = (400000, "请求错误")
|
||||
failure = (400000, "请求失败")
|
||||
image_code_err = (400001, "图形验证码错误")
|
||||
throttling_err = (400002, "访问过于频繁")
|
||||
necessary_param_err = (400003, "缺少必传参数")
|
||||
USER_ERR = (400004, "用户名错误")
|
||||
PWD_ERR = (400005, "密码错误")
|
||||
CPWD_ERR = (400006, "密码不一致")
|
||||
MOBILE_ERR = (400007, "手机号错误")
|
||||
SMS_CODE_ERR = (400008, "短信验证码有误")
|
||||
ALLOW_ERR = (400009, "未勾选协议")
|
||||
SESSION_ERR = (401000, "用户未登录")
|
||||
|
||||
forbid = (403000, "权限禁止")
|
||||
jwt_expired = (403001, "Token 已过期,请重新登录")
|
||||
unauthorized = (403003, "Token 已失效,请重新登录")
|
||||
not_found = (404000, "数据未找到")
|
||||
|
||||
SERVER_ERR = (500000, "服务器异常")
|
||||
DB_ERR = (500000, "数据错误")
|
||||
EMAIL_ERR = (500001, "邮箱错误")
|
||||
TEL_ERR = (500002, "固定电话错误")
|
||||
NODATA_ERR = (500003, "无数据")
|
||||
NEW_PWD_ERR = (500004, "新密码错误")
|
||||
OPENID_ERR = (500005, "无效的 openid")
|
||||
PARAM_ERR = (500006, "参数错误")
|
||||
STOCK_ERR = (500007, "库存不足")
|
||||
|
||||
@property
|
||||
def code(self):
|
||||
"""获取状态码"""
|
||||
return self.value[0]
|
||||
|
||||
@property
|
||||
def msg(self):
|
||||
"""获取状态码信息"""
|
||||
return self.value[1]
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
return self.value[0] // 1000
|
||||
@@ -1,105 +0,0 @@
|
||||
from pear_admin.common.utils.response_code import ResponseCodeEnum
|
||||
|
||||
|
||||
class JsonResponse:
|
||||
"""
|
||||
JsonResponse 响应体,统一返回前端的格式
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._data = None
|
||||
self._code = None
|
||||
self._msg = None
|
||||
self._status_code = None
|
||||
self._error_code = 0
|
||||
self._table_data = {}
|
||||
|
||||
def set_body(self, body):
|
||||
self._data = body
|
||||
return self
|
||||
|
||||
def set_msg(self, msg):
|
||||
self._msg = msg
|
||||
return self
|
||||
|
||||
def set_status_code(self, status_code):
|
||||
# 设置响应状态码
|
||||
self._status_code = status_code
|
||||
return self
|
||||
|
||||
def set_error_code(self, error_code):
|
||||
# 单独设置响应错误码
|
||||
self._error_code = error_code
|
||||
return self
|
||||
|
||||
def set_response_code_enum(self, code):
|
||||
# 设置响应状态码
|
||||
self._set_code(code)
|
||||
return self
|
||||
|
||||
def set_table(self, pages):
|
||||
self.set_body([item.json() for item in pages.items])
|
||||
self._table_data.update(
|
||||
{
|
||||
"count": pages.total,
|
||||
"page": pages.page,
|
||||
"per_page": pages.per_page,
|
||||
}
|
||||
)
|
||||
return self
|
||||
|
||||
def set_table_info(self, pages):
|
||||
self._table_data.update(
|
||||
{
|
||||
"count": pages.total,
|
||||
"page": pages.page,
|
||||
"per_page": pages.per_page,
|
||||
}
|
||||
)
|
||||
return self
|
||||
|
||||
def ok(self):
|
||||
self._set_code(ResponseCodeEnum.ok)
|
||||
return self._res_result()
|
||||
|
||||
def not_found(self):
|
||||
self._set_code(ResponseCodeEnum.not_found)
|
||||
return self._res_result()
|
||||
|
||||
def forbid(self):
|
||||
self._set_code(ResponseCodeEnum.forbid)
|
||||
return self._res_result()
|
||||
|
||||
def error(self):
|
||||
self._set_code(ResponseCodeEnum.error)
|
||||
return self._res_result()
|
||||
|
||||
def failure(self):
|
||||
self._set_code(ResponseCodeEnum.failure)
|
||||
return self._res_result()
|
||||
|
||||
def jwt_expired(self):
|
||||
self._set_code(ResponseCodeEnum.jwt_expired)
|
||||
return self._res_result()
|
||||
|
||||
def _set_code(self, code):
|
||||
# 在内部设置状态码
|
||||
if not self._msg:
|
||||
self._msg = code.msg
|
||||
if not self._status_code:
|
||||
self._status_code = code.status_code
|
||||
self._error_code = code.code
|
||||
return self
|
||||
|
||||
def _res_result(self):
|
||||
data = {"code": self._error_code, "msg": self._msg, "data": self._data}
|
||||
data.update(self._table_data)
|
||||
return data
|
||||
|
||||
def set_code(self, code):
|
||||
self._set_code(code)
|
||||
return self._res_result()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(JsonResponse().set_msg("请求过于频繁").ok())
|
||||
@@ -1,13 +0,0 @@
|
||||
from flask import Flask
|
||||
|
||||
from .init_db import db, migrate
|
||||
from .init_jwt import jwt
|
||||
from .init_script import register_script
|
||||
|
||||
|
||||
def register_extensions(app: Flask):
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db)
|
||||
jwt.init_app(app)
|
||||
|
||||
register_script(app)
|
||||
@@ -1,27 +0,0 @@
|
||||
from flask import redirect
|
||||
from flask_jwt_extended import JWTManager
|
||||
|
||||
from pear_admin.orms.user import UserORM
|
||||
|
||||
jwt = JWTManager()
|
||||
|
||||
|
||||
@jwt.user_identity_loader
|
||||
def user_identity_lookup(user):
|
||||
return user.id
|
||||
|
||||
|
||||
@jwt.user_lookup_loader
|
||||
def user_lookup_callback(_jwt_header, jwt_data):
|
||||
identity = jwt_data["sub"]
|
||||
return UserORM.query.filter(UserORM.id == identity).one_or_none()
|
||||
|
||||
|
||||
@jwt.expired_token_loader
|
||||
def expired_token_callback():
|
||||
return {"msg": "token 已过期,请重新登录", "code": -1}, 403
|
||||
|
||||
|
||||
@jwt.unauthorized_loader
|
||||
def missing_token_callback(error):
|
||||
return {"msg": "操作未授权,请重新登录", "code": -1}, 403
|
||||
@@ -1,64 +0,0 @@
|
||||
import csv
|
||||
import os
|
||||
|
||||
from flask import Flask, current_app
|
||||
|
||||
from pear_admin.extensions import db
|
||||
from pear_admin.orms import DepartmentORM, RightsORM, RoleORM, UserORM
|
||||
|
||||
|
||||
def dict_to_orm(d, o):
|
||||
for k, v in d.items():
|
||||
if k == "password":
|
||||
o.password = v
|
||||
else:
|
||||
setattr(o, k, v or None)
|
||||
|
||||
|
||||
def csv_to_databases(path, orm):
|
||||
with open(path, encoding="utf-8") as file:
|
||||
for d in csv.DictReader(file):
|
||||
o = orm()
|
||||
dict_to_orm(d, o)
|
||||
db.session.add(o)
|
||||
db.session.flush()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def register_script(app: Flask):
|
||||
@app.cli.command()
|
||||
def init():
|
||||
db.drop_all()
|
||||
db.create_all()
|
||||
|
||||
root = current_app.config.get("ROOT_PATH")
|
||||
|
||||
rights_data_path = os.path.join(root, "static", "data", "ums_rights.csv")
|
||||
csv_to_databases(rights_data_path, RightsORM)
|
||||
|
||||
role_data_path = os.path.join(root, "static", "data", "ums_role.csv")
|
||||
csv_to_databases(role_data_path, RoleORM)
|
||||
|
||||
with open(role_data_path, encoding="utf-8") as file:
|
||||
for d in csv.DictReader(file):
|
||||
role: RoleORM = RoleORM.query.get(d["id"])
|
||||
id_list = [int(_id) for _id in d["rights_ids"].split(":")]
|
||||
role.rights_list = RightsORM.query.filter(
|
||||
RightsORM.id.in_(id_list)
|
||||
).all()
|
||||
db.session.commit()
|
||||
|
||||
department_data_path = os.path.join(
|
||||
root, "static", "data", "ums_department.csv"
|
||||
)
|
||||
csv_to_databases(department_data_path, DepartmentORM)
|
||||
|
||||
user_data_path = os.path.join(root, "static", "data", "ums_user.csv")
|
||||
csv_to_databases(user_data_path, UserORM)
|
||||
|
||||
with open(user_data_path, encoding="utf-8") as file:
|
||||
for d in csv.DictReader(file):
|
||||
user: UserORM = UserORM.query.get(d["id"])
|
||||
id_list = [int(_id) for _id in d["role_ids"].split(":")]
|
||||
user.role_list = RoleORM.query.filter(RoleORM.id.in_(id_list)).all()
|
||||
db.session.commit()
|
||||
@@ -1,20 +0,0 @@
|
||||
from pear_admin.extensions import db
|
||||
|
||||
from .department import DepartmentORM
|
||||
from .rights import RightsORM
|
||||
from .role import RoleORM
|
||||
from .user import UserORM
|
||||
|
||||
user_role = db.Table(
|
||||
"ums_user_role", # 用户 - 角色中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment="标识"),
|
||||
db.Column("user_id", db.Integer, db.ForeignKey("ums_user.id"), comment="用户编号"),
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("ums_role.id"), comment="角色编号"),
|
||||
)
|
||||
|
||||
role_rights = db.Table(
|
||||
"ums_role_rights", # 用户 - 权限中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment="标识"),
|
||||
db.Column("rights_id", db.Integer, db.ForeignKey("ums_rights.id"), comment="用户编号"),
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("ums_role.id"), comment="角色编号"),
|
||||
)
|
||||
@@ -1,16 +0,0 @@
|
||||
from pear_admin.extensions import db
|
||||
|
||||
|
||||
class BaseORM(db.Model):
|
||||
__abstract__ = True
|
||||
|
||||
def save(self):
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
|
||||
def delete(self):
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
|
||||
def json(self):
|
||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
@@ -1,19 +0,0 @@
|
||||
from pear_admin.extensions import db
|
||||
|
||||
from ._base import BaseORM
|
||||
|
||||
|
||||
class DepartmentORM(BaseORM):
|
||||
__tablename__ = "ums_department"
|
||||
id = db.Column(db.Integer, primary_key=True, comment="部门 ID")
|
||||
name = db.Column(db.String(50), comment="部门名称")
|
||||
leader = db.Column(db.String(50), comment="负责人")
|
||||
enable = db.Column(db.Boolean, comment="状态 (1 开启,0 关闭)", default=True)
|
||||
|
||||
users = db.relationship("UserORM", backref="department")
|
||||
|
||||
pid = db.Column(db.Integer, db.ForeignKey("ums_department.id"), default=1)
|
||||
parent = db.relationship(
|
||||
"DepartmentORM", back_populates="children", remote_side=[id]
|
||||
) # 自关联
|
||||
children = db.relationship("DepartmentORM", back_populates="parent")
|
||||
@@ -1,45 +0,0 @@
|
||||
from sqlalchemy.orm import backref
|
||||
|
||||
from pear_admin.extensions import db
|
||||
|
||||
from ._base import BaseORM
|
||||
|
||||
|
||||
class RightsORM(BaseORM):
|
||||
__tablename__ = "ums_rights"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(20), nullable=False, comment="权限名称")
|
||||
code = db.Column(db.String(30), comment="权限标识")
|
||||
type = db.Column(db.String(30), comment="权限类型")
|
||||
url = db.Column(db.String(30), comment="路径地址")
|
||||
|
||||
icon_sign = db.Column(db.String(128), comment="图标")
|
||||
status = db.Column(db.Boolean, default=True, comment="是否开启")
|
||||
sort = db.Column(db.Integer, default=0)
|
||||
open_type = db.Column(db.String(128), comment="打开方式")
|
||||
pid = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("ums_rights.id"),
|
||||
default=0,
|
||||
comment="父类编号",
|
||||
)
|
||||
|
||||
parent = db.relationship(
|
||||
"RightsORM", back_populates="children", remote_side=[id]
|
||||
) # 自关联
|
||||
children = db.relationship("RightsORM", back_populates="parent")
|
||||
|
||||
def menu_json(self):
|
||||
type_map_dict = {"menu": 0, "path": 1}
|
||||
|
||||
return {
|
||||
"id": self.id,
|
||||
"pid": self.pid,
|
||||
"title": self.name,
|
||||
"type": type_map_dict[self.type],
|
||||
"href": self.url,
|
||||
"icon": self.icon_sign,
|
||||
"sort": self.sort,
|
||||
"openType": self.open_type,
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
from pear_admin.extensions import db
|
||||
|
||||
from ._base import BaseORM
|
||||
|
||||
|
||||
class RoleORM(BaseORM):
|
||||
__tablename__ = "ums_role"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(20), nullable=False, comment="角色名称")
|
||||
code = db.Column(db.String(20), nullable=False, comment="角色标识符")
|
||||
desc = db.Column(db.Text)
|
||||
|
||||
rights_ids = db.Column(
|
||||
db.String(512),
|
||||
comment="权限 ids,1,2,5。冗余字段,用户缓存用户权限",
|
||||
)
|
||||
|
||||
rights_list = db.relationship(
|
||||
"RightsORM", secondary="ums_role_rights", backref=db.backref("role")
|
||||
)
|
||||
@@ -1,52 +0,0 @@
|
||||
from datetime import datetime
|
||||
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
from pear_admin.extensions import db
|
||||
|
||||
from ._base import BaseORM
|
||||
|
||||
|
||||
class UserORM(BaseORM):
|
||||
__tablename__ = "ums_user"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, comment="自增 id")
|
||||
nickname = db.Column(db.String(128), nullable=False, comment="昵称")
|
||||
username = db.Column(db.String(128), nullable=False, comment="登录名")
|
||||
password_hash = db.Column(db.String(102), nullable=False, comment="登录密码")
|
||||
mobile = db.Column(db.String(32), nullable=False, comment="手机")
|
||||
email = db.Column(db.String(64), nullable=False, comment="邮箱")
|
||||
avatar = db.Column(db.Text, comment="头像地址")
|
||||
create_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
comment="创建时间",
|
||||
default=datetime.now,
|
||||
)
|
||||
department_id = db.Column(
|
||||
db.Integer, db.ForeignKey("ums_department.id"), default=1, comment="部门 id"
|
||||
)
|
||||
|
||||
role_list = db.relationship(
|
||||
"RoleORM",
|
||||
secondary="ums_user_role",
|
||||
backref=db.backref("user"),
|
||||
lazy="dynamic",
|
||||
)
|
||||
|
||||
def json(self):
|
||||
create_at = (self.create_at.strftime("%Y-%m-%d %H:%M:%S"),)
|
||||
data = super().json()
|
||||
data["create_at"] = create_at
|
||||
return data
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return self.password_hash
|
||||
|
||||
@password.setter
|
||||
def password(self, password):
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password_hash, password=password)
|
||||
@@ -1,9 +0,0 @@
|
||||
from flask import Flask
|
||||
|
||||
from .index import index_bp
|
||||
from .system import system_bp
|
||||
|
||||
|
||||
def register_views(app: Flask):
|
||||
app.register_blueprint(index_bp)
|
||||
app.register_blueprint(system_bp)
|
||||
@@ -1,38 +0,0 @@
|
||||
from flask import Blueprint, render_template, send_file, send_from_directory
|
||||
|
||||
index_bp = Blueprint("index", __name__)
|
||||
|
||||
|
||||
@index_bp.route("/")
|
||||
def index():
|
||||
return render_template("view/index.html")
|
||||
|
||||
|
||||
@index_bp.route("/view/login.html")
|
||||
def login():
|
||||
return render_template("view/login.html")
|
||||
|
||||
|
||||
@index_bp.route("/view/register.html")
|
||||
def register():
|
||||
return render_template("view/register.html")
|
||||
|
||||
|
||||
@index_bp.route("/view/console/index.html")
|
||||
def console1():
|
||||
return render_template("view/console/index.html")
|
||||
|
||||
|
||||
@index_bp.route("/view/analysis/index.html")
|
||||
def analysis():
|
||||
return render_template("view/analysis/index.html")
|
||||
|
||||
|
||||
@index_bp.route("/view/system/person.html")
|
||||
def person():
|
||||
return render_template("view/system/person.html")
|
||||
|
||||
|
||||
@index_bp.route("/favicon.ico")
|
||||
def fav_icon():
|
||||
return send_from_directory(directory="static", path="favicon.ico")
|
||||
@@ -1,23 +0,0 @@
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
system_bp = Blueprint("system", __name__)
|
||||
|
||||
|
||||
@system_bp.get("/system/<path1>/<path2>")
|
||||
def system_view(path1, path2):
|
||||
return render_template(f"system/{path1}/{path2}")
|
||||
|
||||
|
||||
@system_bp.get("/views/role.html")
|
||||
def role_view():
|
||||
return render_template("system/role/index.html")
|
||||
|
||||
|
||||
@system_bp.get("/views/department.html")
|
||||
def department_view():
|
||||
return render_template("system/department/index.html")
|
||||
|
||||
|
||||
@system_bp.get("/views/user.html")
|
||||
def user_view():
|
||||
return render_template("system/user/index.html")
|
||||
Generated
-935
@@ -1,935 +0,0 @@
|
||||
# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.12.1"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "alembic-1.12.1-py3-none-any.whl", hash = "sha256:47d52e3dfb03666ed945becb723d6482e52190917fdb47071440cfdba05d92cb"},
|
||||
{file = "alembic-1.12.1.tar.gz", hash = "sha256:bca5877e9678b454706347bc10b97cb7d67f300320fa5c3a94423e8266e2823f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Mako = "*"
|
||||
SQLAlchemy = ">=1.3.0"
|
||||
typing-extensions = ">=4"
|
||||
|
||||
[package.extras]
|
||||
tz = ["python-dateutil"]
|
||||
|
||||
[[package]]
|
||||
name = "argcomplete"
|
||||
version = "3.1.2"
|
||||
description = "Bash tab completion for argparse"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "argcomplete-3.1.2-py3-none-any.whl", hash = "sha256:d97c036d12a752d1079f190bc1521c545b941fda89ad85d15afa909b4d1b9a99"},
|
||||
{file = "argcomplete-3.1.2.tar.gz", hash = "sha256:d5d1e5efd41435260b8f85673b74ea2e883affcbec9f4230c582689e8e78251b"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["coverage", "mypy", "pexpect", "ruff", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "blinker"
|
||||
version = "1.6.3"
|
||||
description = "Fast, simple object-to-object and broadcast signaling"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "blinker-1.6.3-py3-none-any.whl", hash = "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa"},
|
||||
{file = "blinker-1.6.3.tar.gz", hash = "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.4.0"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
|
||||
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.3.1"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"},
|
||||
{file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"},
|
||||
{file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"},
|
||||
{file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"},
|
||||
{file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"},
|
||||
{file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"},
|
||||
{file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"},
|
||||
{file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "commitizen"
|
||||
version = "3.12.0"
|
||||
description = "Python commitizen client tool"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "commitizen-3.12.0-py3-none-any.whl", hash = "sha256:082f4733409bc4f01f987467295f8393ceb16b42cc648cf2f5a7a754c6d594db"},
|
||||
{file = "commitizen-3.12.0.tar.gz", hash = "sha256:7c313f1f85f45c9acf1a70f1637deab5c388150ae8660a0037ac260e77bb1492"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
argcomplete = ">=1.12.1,<3.2"
|
||||
charset-normalizer = ">=2.1.0,<4"
|
||||
colorama = ">=0.4.1,<0.5.0"
|
||||
decli = ">=0.6.0,<0.7.0"
|
||||
importlib_metadata = ">=4.13,<7"
|
||||
jinja2 = ">=2.10.3"
|
||||
packaging = ">=19"
|
||||
pyyaml = ">=3.08"
|
||||
questionary = ">=2.0,<3.0"
|
||||
termcolor = ">=1.1,<3"
|
||||
tomlkit = ">=0.5.3,<1.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "decli"
|
||||
version = "0.6.1"
|
||||
description = "Minimal, easy-to-use, declarative cli tool"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "decli-0.6.1-py3-none-any.whl", hash = "sha256:7815ac58617764e1a200d7cadac6315fcaacc24d727d182f9878dd6378ccf869"},
|
||||
{file = "decli-0.6.1.tar.gz", hash = "sha256:ed88ccb947701e8e5509b7945fda56e150e2ac74a69f25d47ac85ef30ab0c0f0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.7"
|
||||
description = "Distribution utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"},
|
||||
{file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.13.1"
|
||||
description = "A platform independent file lock."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
|
||||
{file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
|
||||
typing = ["typing-extensions (>=4.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "3.0.0"
|
||||
description = "A simple framework for building complex web applications."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"},
|
||||
{file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
blinker = ">=1.6.2"
|
||||
click = ">=8.1.3"
|
||||
itsdangerous = ">=2.1.2"
|
||||
Jinja2 = ">=3.1.2"
|
||||
Werkzeug = ">=3.0.0"
|
||||
|
||||
[package.extras]
|
||||
async = ["asgiref (>=3.2)"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-jwt-extended"
|
||||
version = "4.5.3"
|
||||
description = "Extended JWT integration with Flask"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4"
|
||||
files = [
|
||||
{file = "Flask-JWT-Extended-4.5.3.tar.gz", hash = "sha256:061ef3d25ed5743babe4964ab38f36d870e6d2fd8a126bab5d77ddef8a01932b"},
|
||||
{file = "Flask_JWT_Extended-4.5.3-py2.py3-none-any.whl", hash = "sha256:eaec42af107dcb919785a4b3766c09ffba9f286b92a8d58603933f28fd4db6a3"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Flask = ">=2.0,<4.0"
|
||||
PyJWT = ">=2.0,<3.0"
|
||||
Werkzeug = ">=0.14"
|
||||
|
||||
[package.extras]
|
||||
asymmetric-crypto = ["cryptography (>=3.3.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-migrate"
|
||||
version = "4.0.5"
|
||||
description = "SQLAlchemy database migrations for Flask applications using Alembic."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "Flask-Migrate-4.0.5.tar.gz", hash = "sha256:d3f437a8b5f3849d1bb1b60e1b818efc564c66e3fefe90b62e5db08db295e1b1"},
|
||||
{file = "Flask_Migrate-4.0.5-py3-none-any.whl", hash = "sha256:613a2df703998e78716cace68cd83972960834424457f5b67f56e74fff950aef"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
alembic = ">=1.9.0"
|
||||
Flask = ">=0.9"
|
||||
Flask-SQLAlchemy = ">=1.0"
|
||||
|
||||
[[package]]
|
||||
name = "flask-sqlalchemy"
|
||||
version = "3.1.1"
|
||||
description = "Add SQLAlchemy support to your Flask application."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"},
|
||||
{file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
flask = ">=2.2.5"
|
||||
sqlalchemy = ">=2.0.16"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.0.1"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"},
|
||||
{file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"},
|
||||
{file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"},
|
||||
{file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"},
|
||||
{file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"},
|
||||
{file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"},
|
||||
{file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"},
|
||||
{file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx"]
|
||||
test = ["objgraph", "psutil"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.31"
|
||||
description = "File identification library for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"},
|
||||
{file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
license = ["ukkonen"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "6.8.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"},
|
||||
{file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
perf = ["ipython"]
|
||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.1.2"
|
||||
description = "Safely pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
|
||||
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.2"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
|
||||
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "mako"
|
||||
version = "1.2.4"
|
||||
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"},
|
||||
{file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=0.9.2"
|
||||
|
||||
[package.extras]
|
||||
babel = ["Babel"]
|
||||
lingua = ["lingua"]
|
||||
testing = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.3"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
|
||||
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.8.0"
|
||||
description = "Node.js virtual environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||
files = [
|
||||
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
|
||||
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.11.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"},
|
||||
{file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.5.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"},
|
||||
{file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cfgv = ">=2.0.0"
|
||||
identify = ">=1.0.0"
|
||||
nodeenv = ">=0.11.1"
|
||||
pyyaml = ">=5.1"
|
||||
virtualenv = ">=20.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.36"
|
||||
description = "Library for building powerful interactive command lines in Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
files = [
|
||||
{file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"},
|
||||
{file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
wcwidth = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.8.0"
|
||||
description = "JSON Web Token implementation in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"},
|
||||
{file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
crypto = ["cryptography (>=3.4.0)"]
|
||||
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.0"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
|
||||
{file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "questionary"
|
||||
version = "2.0.1"
|
||||
description = "Python library to build pretty command line user prompts ⭐️"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2"},
|
||||
{file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
prompt_toolkit = ">=2.0,<=3.0.36"
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "68.2.2"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
|
||||
{file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.22"
|
||||
description = "Database Abstraction Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f146c61ae128ab43ea3a0955de1af7e1633942c2b2b4985ac51cc292daf33222"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:875de9414393e778b655a3d97d60465eb3fae7c919e88b70cc10b40b9f56042d"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13790cb42f917c45c9c850b39b9941539ca8ee7917dacf099cc0b569f3d40da7"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e04ab55cf49daf1aeb8c622c54d23fa4bec91cb051a43cc24351ba97e1dd09f5"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a42c9fa3abcda0dcfad053e49c4f752eef71ecd8c155221e18b99d4224621176"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:14cd3bcbb853379fef2cd01e7c64a5d6f1d005406d877ed9509afb7a05ff40a5"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-win32.whl", hash = "sha256:d143c5a9dada696bcfdb96ba2de4a47d5a89168e71d05a076e88a01386872f97"},
|
||||
{file = "SQLAlchemy-2.0.22-cp310-cp310-win_amd64.whl", hash = "sha256:ccd87c25e4c8559e1b918d46b4fa90b37f459c9b4566f1dfbce0eb8122571547"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f6ff392b27a743c1ad346d215655503cec64405d3b694228b3454878bf21590"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f776c2c30f0e5f4db45c3ee11a5f2a8d9de68e81eb73ec4237de1e32e04ae81c"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8f1792d20d2f4e875ce7a113f43c3561ad12b34ff796b84002a256f37ce9437"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80eeb5189d7d4b1af519fc3f148fe7521b9dfce8f4d6a0820e8f5769b005051"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:69fd9e41cf9368afa034e1c81f3570afb96f30fcd2eb1ef29cb4d9371c6eece2"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54bcceaf4eebef07dadfde424f5c26b491e4a64e61761dea9459103ecd6ccc95"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-win32.whl", hash = "sha256:7ee7ccf47aa503033b6afd57efbac6b9e05180f492aeed9fcf70752556f95624"},
|
||||
{file = "SQLAlchemy-2.0.22-cp311-cp311-win_amd64.whl", hash = "sha256:b560f075c151900587ade06706b0c51d04b3277c111151997ea0813455378ae0"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2c9bac865ee06d27a1533471405ad240a6f5d83195eca481f9fc4a71d8b87df8"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:625b72d77ac8ac23da3b1622e2da88c4aedaee14df47c8432bf8f6495e655de2"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b39a6e21110204a8c08d40ff56a73ba542ec60bab701c36ce721e7990df49fb9"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a766cb0b468223cafdf63e2d37f14a4757476157927b09300c8c5832d88560"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0e1ce8ebd2e040357dde01a3fb7d30d9b5736b3e54a94002641dfd0aa12ae6ce"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:505f503763a767556fa4deae5194b2be056b64ecca72ac65224381a0acab7ebe"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-win32.whl", hash = "sha256:154a32f3c7b00de3d090bc60ec8006a78149e221f1182e3edcf0376016be9396"},
|
||||
{file = "SQLAlchemy-2.0.22-cp312-cp312-win_amd64.whl", hash = "sha256:129415f89744b05741c6f0b04a84525f37fbabe5dc3774f7edf100e7458c48cd"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3940677d341f2b685a999bffe7078697b5848a40b5f6952794ffcf3af150c301"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55914d45a631b81a8a2cb1a54f03eea265cf1783241ac55396ec6d735be14883"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2096d6b018d242a2bcc9e451618166f860bb0304f590d205173d317b69986c95"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:19c6986cf2fb4bc8e0e846f97f4135a8e753b57d2aaaa87c50f9acbe606bd1db"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ac28bd6888fe3c81fbe97584eb0b96804bd7032d6100b9701255d9441373ec1"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-win32.whl", hash = "sha256:cb9a758ad973e795267da334a92dd82bb7555cb36a0960dcabcf724d26299db8"},
|
||||
{file = "SQLAlchemy-2.0.22-cp37-cp37m-win_amd64.whl", hash = "sha256:40b1206a0d923e73aa54f0a6bd61419a96b914f1cd19900b6c8226899d9742ad"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3aa1472bf44f61dd27987cd051f1c893b7d3b17238bff8c23fceaef4f1133868"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:56a7e2bb639df9263bf6418231bc2a92a773f57886d371ddb7a869a24919face"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccca778c0737a773a1ad86b68bda52a71ad5950b25e120b6eb1330f0df54c3d0"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6c3e9350f9fb16de5b5e5fbf17b578811a52d71bb784cc5ff71acb7de2a7f9"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:564e9f9e4e6466273dbfab0e0a2e5fe819eec480c57b53a2cdee8e4fdae3ad5f"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:af66001d7b76a3fab0d5e4c1ec9339ac45748bc4a399cbc2baa48c1980d3c1f4"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-win32.whl", hash = "sha256:9e55dff5ec115316dd7a083cdc1a52de63693695aecf72bc53a8e1468ce429e5"},
|
||||
{file = "SQLAlchemy-2.0.22-cp38-cp38-win_amd64.whl", hash = "sha256:4e869a8ff7ee7a833b74868a0887e8462445ec462432d8cbeff5e85f475186da"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9886a72c8e6371280cb247c5d32c9c8fa141dc560124348762db8a8b236f8692"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a571bc8ac092a3175a1d994794a8e7a1f2f651e7c744de24a19b4f740fe95034"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8db5ba8b7da759b727faebc4289a9e6a51edadc7fc32207a30f7c6203a181592"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b0b3f2686c3f162123adba3cb8b626ed7e9b8433ab528e36ed270b4f70d1cdb"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c1fea8c0abcb070ffe15311853abfda4e55bf7dc1d4889497b3403629f3bf00"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4bb062784f37b2d75fd9b074c8ec360ad5df71f933f927e9e95c50eb8e05323c"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-win32.whl", hash = "sha256:58a3aba1bfb32ae7af68da3f277ed91d9f57620cf7ce651db96636790a78b736"},
|
||||
{file = "SQLAlchemy-2.0.22-cp39-cp39-win_amd64.whl", hash = "sha256:92e512a6af769e4725fa5b25981ba790335d42c5977e94ded07db7d641490a85"},
|
||||
{file = "SQLAlchemy-2.0.22-py3-none-any.whl", hash = "sha256:3076740335e4aaadd7deb3fe6dcb96b3015f1613bd190a4e1634e1b99b02ec86"},
|
||||
{file = "SQLAlchemy-2.0.22.tar.gz", hash = "sha256:5434cc601aa17570d79e5377f5fd45ff92f9379e2abed0be5e8c2fba8d353d2b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
|
||||
typing-extensions = ">=4.2.0"
|
||||
|
||||
[package.extras]
|
||||
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
|
||||
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
|
||||
asyncio = ["greenlet (!=0.4.17)"]
|
||||
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
|
||||
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
|
||||
mssql = ["pyodbc"]
|
||||
mssql-pymssql = ["pymssql"]
|
||||
mssql-pyodbc = ["pyodbc"]
|
||||
mypy = ["mypy (>=0.910)"]
|
||||
mysql = ["mysqlclient (>=1.4.0)"]
|
||||
mysql-connector = ["mysql-connector-python"]
|
||||
oracle = ["cx-oracle (>=7)"]
|
||||
oracle-oracledb = ["oracledb (>=1.0.1)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
|
||||
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
|
||||
postgresql-psycopg = ["psycopg (>=3.0.7)"]
|
||||
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
|
||||
pymysql = ["pymysql"]
|
||||
sqlcipher = ["sqlcipher3-binary"]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "2.3.0"
|
||||
description = "ANSI color formatting for output in terminal"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"},
|
||||
{file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.12.1"
|
||||
description = "Style preserving TOML library"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"},
|
||||
{file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.8.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
|
||||
{file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.24.6"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"},
|
||||
{file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = ">=3.12.2,<4"
|
||||
platformdirs = ">=3.9.1,<4"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.9"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "wcwidth-0.2.9-py2.py3-none-any.whl", hash = "sha256:9a929bd8380f6cd9571a968a9c8f4353ca58d7cd812a4822bba831f8d685b223"},
|
||||
{file = "wcwidth-0.2.9.tar.gz", hash = "sha256:a675d1a4a2d24ef67096a04b85b02deeecd8e226f57b5e3a72dbb9ed99d27da8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "3.0.1"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"},
|
||||
{file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.1.1"
|
||||
|
||||
[package.extras]
|
||||
watchdog = ["watchdog (>=2.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.17.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
|
||||
{file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "e99a77db2a32e04474571eea6c88dad45078d1a0b2d146af67e1f360ecd4760f"
|
||||
@@ -1,24 +0,0 @@
|
||||
[tool.poetry]
|
||||
name = "pear-admin-flask"
|
||||
version = "0.1.0"
|
||||
description = "flask + pear admin layui"
|
||||
authors = ["zhengxinonly <pyxxponly@gmail.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
flask = "^3.0.0"
|
||||
flask-sqlalchemy = "^3.1.1"
|
||||
flask-jwt-extended = "^4.5.2"
|
||||
flask-migrate = "^4.0.5"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
python-dotenv = "^1.0.0"
|
||||
pre-commit = "^3.4.0"
|
||||
commitizen = "^3.10.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,29 @@
|
||||
alembic==1.5.4
|
||||
blinker==1.4
|
||||
captcha==0.3
|
||||
click==7.1.2
|
||||
Flask==1.1.2
|
||||
Flask-DebugToolbar==0.11.0
|
||||
Flask-Login==0.5.0
|
||||
flask-marshmallow==0.14.0
|
||||
Flask-Migrate==2.6.0
|
||||
Flask-Script==2.0.6
|
||||
Flask-SQLAlchemy==2.4.4
|
||||
-e git+https://github.com/maxcountryman/flask-uploads.git@f66d7dc93e684fa0a3a4350a38e41ae00483a796#egg=Flask_Uploads
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.3
|
||||
Mako==1.1.4
|
||||
MarkupSafe==1.1.1
|
||||
marshmallow==3.10.0
|
||||
marshmallow-sqlalchemy==0.24.2
|
||||
Pillow==8.1.0
|
||||
psutil==5.8.0
|
||||
PyMySQL==1.0.2
|
||||
python-dateutil==2.8.1
|
||||
python-dotenv==0.15.0
|
||||
python-editor==1.0.4
|
||||
PyYAML==5.4.1
|
||||
six==1.15.0
|
||||
SQLAlchemy==1.3.23
|
||||
sqlparse==0.4.1
|
||||
Werkzeug==1.0.1
|
||||
@@ -0,0 +1,443 @@
|
||||
html,
|
||||
body,
|
||||
.layui-layout {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pear-admin .layui-header,
|
||||
.pear-admin .layui-body,
|
||||
.pear-admin .layui-logo,
|
||||
.pear-admin .layui-side,
|
||||
.pear-admin .layui-header .layui-layout-left {
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.pear-admin .layui-logo .title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-layout-right .layui-nav-child {
|
||||
border: 1px solid whitesmoke;
|
||||
border-radius: 6px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-header {
|
||||
left: 230px;
|
||||
width: calc(100% - 230px);
|
||||
background-color: white;
|
||||
border-bottom: 1px solid whitesmoke;
|
||||
}
|
||||
|
||||
.pear-admin .layui-header .layui-nav-img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-layout-control {
|
||||
left: 120px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.pear-admin .layui-logo {
|
||||
width: 230px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
position: relative;
|
||||
background-color: #28333E;
|
||||
|
||||
}
|
||||
|
||||
.pear-admin .layui-logo img {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-logo .title {
|
||||
font-size: 21px;
|
||||
font-weight: 550;
|
||||
color: #5FB878;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-logo .logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pear-admin .layui-side {
|
||||
top: 0px;
|
||||
width: 230px;
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.pear-admin .layui-side-scroll::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-side-scroll {
|
||||
height: calc(100% - 60px) !important;
|
||||
background-color: #28333E;
|
||||
width: 247px;
|
||||
|
||||
}
|
||||
|
||||
.pear-admin .layui-header .layui-nav .layui-nav-item>a {
|
||||
color: black;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-body {
|
||||
left: 230px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-layout-left {
|
||||
left: 0px;
|
||||
|
||||
}
|
||||
|
||||
/** 隐 藏 布 局 样 式 */
|
||||
.pear-mini .layui-logo .title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pear-mini .layui-logo .logo {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pear-mini .layui-side {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.pear-mini .layui-header {
|
||||
left: 60px;
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.pear-mini .layui-body {
|
||||
left: 60px;
|
||||
}
|
||||
|
||||
.pear-mini .layui-logo {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.pear-mini .layui-nav-tree .layui-nav-item span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pear-mini .bottom-nav li {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.pear-mini .layui-side-scroll {
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.pear-admin .layui-header .layui-nav .layui-nav-bar {
|
||||
top: 0px !important;
|
||||
height: 2px !important;
|
||||
background-color: #5FB878;
|
||||
}
|
||||
|
||||
.pear-admin .layui-header .layui-nav .layui-this:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pear-admin .layui-header .layui-nav-more {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pear-collasped-pe {
|
||||
display: none;
|
||||
width: 50px;
|
||||
position: absolute;
|
||||
z-index: 400000;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
background-color: #5FB878 !important;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
border-radius: 50px;
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
||||
}
|
||||
|
||||
.pear-collasped-pe a {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.layui-hide-sm {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 769px) {
|
||||
.layui-hide-sm {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/** 新增兼容 */
|
||||
@media screen and (max-width:768px) {
|
||||
.collaspe {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.pear-collasped-pe {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.layui-layout-control {
|
||||
left: 45px !important;
|
||||
}
|
||||
|
||||
.layui-layout-left {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pear-mini .bottom-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pear-mini .layui-side-scroll {
|
||||
height: calc(100% - 62px);
|
||||
}
|
||||
|
||||
/** 隐 藏 布 局 样 式 */
|
||||
.pear-mini .layui-side {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.pear-mini .layui-header {
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pear-mini .layui-body {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.pear-mini .layui-logo {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-body {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.pear-admin .layui-header {
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pear-admin .pear-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #1E1E1E;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
margin-top: -60px;
|
||||
}
|
||||
|
||||
.pear-mini .pear-cover {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes am-horizontal-roll_show {
|
||||
0% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(2000px);
|
||||
transform: translateX(2000px)
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes am-horizontal-roll_show {
|
||||
0% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(800px);
|
||||
-ms-transform: translateX(800px);
|
||||
transform: translateX(800px)
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0)
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-right {
|
||||
-webkit-animation: am-horizontal-roll_show .6s ease-out;
|
||||
animation: am-horizontal-roll_show .6s ease-out;
|
||||
|
||||
}
|
||||
|
||||
/** 亮色侧边风格 */
|
||||
.light-theme .layui-logo {
|
||||
background-color: white !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.light-theme .layui-side-scroll {
|
||||
background-color: white !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.light-theme .layui-side {
|
||||
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, .05) !important;
|
||||
}
|
||||
|
||||
/** 主 题 选 择 界 面 样 式 */
|
||||
.pearone-color .color-title {
|
||||
padding: 15px 0 0px 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.pearone-color .color-content {
|
||||
padding: 15px 10px 0 20px;
|
||||
}
|
||||
|
||||
.pearone-color .color-content ul {
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.pearone-color .color-content ul li {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 70px;
|
||||
height: 50px;
|
||||
margin: 0 20px 20px 0;
|
||||
padding: 2px 2px 2px 2px;
|
||||
background-color: #f2f2f2;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.pearone-color .color-content li.layui-this:after,
|
||||
.pearone-color .color-content li:hover:after {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4px;
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
border: #5FB878 2px solid;
|
||||
opacity: 1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pearone-color .color-content li:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 1px;
|
||||
height: 0;
|
||||
border: 2px solid #F2F2F2;
|
||||
transition: all .3s;
|
||||
-webkit-transition: all .3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.select-color {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.select-color .select-color-title {
|
||||
padding: 15px 0 0px 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.select-color .select-color-content {
|
||||
padding: 20px 0 0px 0px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.select-color .select-color-content .select-color-item {
|
||||
background-color: gray;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 3px;
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .15);
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
/** 友情链接 */
|
||||
.more-setting {
|
||||
margin-top: 45px;
|
||||
}
|
||||
|
||||
.more-setting form {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.more-setting-title {
|
||||
padding: 15px 0 0px 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.more-setting .layui-form-label {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.more-menu-list {
|
||||
width: 100%;
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
.more-menu-item:first-child {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.more-menu-item .layui-icon {
|
||||
font-size: 18px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.more-menu-item {
|
||||
color: #595959;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 16px;
|
||||
padding: 0 25px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
font-style: normal;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.more-menu-item:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.more-menu-item:after {
|
||||
color: #8c8c8c;
|
||||
right: 16px;
|
||||
content: "\e602";
|
||||
position: absolute;
|
||||
font-family: layui-icon !important;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
.loader-main{
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: whitesmoke;
|
||||
z-index: 9999999;
|
||||
margin-top: -61px;
|
||||
}
|
||||
.loader {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 30px auto 40px;
|
||||
margin-top: 20%;
|
||||
position: relative;
|
||||
z-index: 999999;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
.loader:before {
|
||||
content: "";
|
||||
width: 50px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #000;
|
||||
opacity: 0.1;
|
||||
position: absolute;
|
||||
top: 59px;
|
||||
left: 0;
|
||||
animation: shadow .5s linear infinite;
|
||||
}
|
||||
.loader:after {
|
||||
content: "";
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 3px;
|
||||
background-color: #5FB878;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
animation: loading .5s linear infinite;
|
||||
}
|
||||
@-webkit-keyframes loading {
|
||||
17% {
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateY(9px) rotate(22.5deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(18px) scale(1, 0.9) rotate(45deg);
|
||||
border-bottom-right-radius: 40px;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateY(9px) rotate(67.5deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0) rotate(90deg);
|
||||
}
|
||||
}
|
||||
@keyframes loading {
|
||||
17% {
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateY(9px) rotate(22.5deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(18px) scale(1, 0.9) rotate(45deg);
|
||||
border-bottom-right-radius: 40px;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateY(9px) rotate(67.5deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0) rotate(90deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes shadow {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.2, 1);
|
||||
}
|
||||
}
|
||||
@keyframes shadow {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.2, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
.top-panel {
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top-panel>.layui-card-body {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.top-panel-number {
|
||||
line-height: 60px;
|
||||
font-size: 30px;
|
||||
border-right: 1px solid #eceff9;
|
||||
}
|
||||
|
||||
.top-panel-tips {
|
||||
padding-left: 8px;
|
||||
padding-top: 16px;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pear-container {
|
||||
background-color: whitesmoke;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 100%;
|
||||
height: 160px;
|
||||
background-color: whitesmoke;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.card .header .avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin: 20px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.card .header {
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
.card .body {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.card .body {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.card .footer {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
margin-top: 20px;
|
||||
font-size: 13px;
|
||||
color: gray;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.custom-tab .layui-tab-title {
|
||||
border-bottom-width: 0px;
|
||||
border-bottom-style: none;
|
||||
}
|
||||
|
||||
.custom-tab .layui-tab-title li {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.list .list-item {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
color: gray;
|
||||
padding: 5px;
|
||||
padding-left: 15px;
|
||||
border-radius: 4px;
|
||||
margin-top: 5.2px;
|
||||
}
|
||||
|
||||
.list .list-item:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.list .list-item .title {
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list .list-item .footer {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.top-panel-tips i {
|
||||
font-size: 33px;
|
||||
}
|
||||
|
||||
.layuiadmin-card-status {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.layuiadmin-card-status dd {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #EEE;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.layuiadmin-card-status dd div.layui-status-img,
|
||||
.layuiadmin-card-team .layui-team-img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.layuiadmin-card-status dd div.layui-status-img a {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.layuiadmin-card-status dd div span {
|
||||
color: #BBB;
|
||||
}
|
||||
|
||||
.layuiadmin-card-status dd div a {
|
||||
color: #01AAED;
|
||||
}
|
||||
|
||||
.top-panel-tips svg {
|
||||
margin-top: -12px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
.pear-container {background-color: whitesmoke;margin: 10px;}
|
||||
.pear-card {width: 100%;height: 66px;background-color: #F8F8F8;display: inline-block;border-radius: 5px;text-align: center;margin-bottom: 3px;}
|
||||
.pear-card:hover,.pear-card2:hover{box-shadow: 2px 0 8px 0 lightgray!important;}.pear-card2 {width: 100%;height: 90px;background-color: #F8F8F8;display: inline-block;border-radius: 5px;text-align: center;margin-bottom: 3px;}
|
||||
.pear-card2 i {font-size: 30px;height: 90px;line-height: 90px;}
|
||||
.pear-card i {font-size: 30px;height: 66px;line-height: 66px;}
|
||||
.layui-col-md3 {text-align: center;}
|
||||
.pear-card-title {margin-top: 3px;}
|
||||
.person img {width: 90px;height: 90px;border-radius: 4px;margin-top: 8px;margin-left: 8px;}
|
||||
.pear-card2 .count {color: #51A351;font-size: 30px;margin-top: 12px;}
|
||||
.pear-card2 .title {color: gray;font-size: 14px;margin-top: 14px;}
|
||||
.pear-card-status {padding: 0 10px 10px;}
|
||||
.pear-card-status li {position: relative;padding: 10px 0;border-bottom: 1px solid #EEE;}
|
||||
.pear-card-status li h3 {padding-bottom: 5px;font-weight: 700;}
|
||||
.pear-card-status li p {padding-bottom: 10px;padding-top: 3px ;}
|
||||
.pear-card-status li>span {color: #999;}
|
||||
.pear-reply {position: absolute;right: 20px;}
|
||||
.person .title{font-size: 17px;font-weight: 600;margin-left: 18px;margin-top: 16px;position: absolute;display: inline-block;}
|
||||
.person .desc{font-size: 16px;font-weight: 600;margin-left: 115px;margin-top: -30px;position: absolute;display: inline-block;}
|
||||
@@ -0,0 +1,6 @@
|
||||
.organizationTree {
|
||||
width: 100% !important;
|
||||
height: -webkit-calc(100vh - 130px);
|
||||
height: -moz-calc(100vh - 130px);
|
||||
height: calc(100vh - 130px);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.38rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
-webkit-tap-highlight-color: transparent
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 0.28rem;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content>img {
|
||||
height: 300px;
|
||||
max-width: 370px;
|
||||
margin-right: 180px;
|
||||
}
|
||||
|
||||
.content>* {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.content-r {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.content-r>h1 {
|
||||
font-size: 72px;
|
||||
color: #434e59;
|
||||
margin-bottom: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-r>p {
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
.layui-form {
|
||||
width: 320px !important;
|
||||
margin: auto !important;
|
||||
margin-top: 160px !important;
|
||||
}
|
||||
|
||||
.layui-form button {
|
||||
width: 100% !important;
|
||||
height: 44px !important;
|
||||
line-height: 44px !important;
|
||||
font-size: 16px !important;
|
||||
background-color: #5FB878 !important;
|
||||
font-weight: 550 !important;
|
||||
}
|
||||
|
||||
.layui-form-checked[lay-skin=primary] i {
|
||||
border-color: #5FB878 !important;
|
||||
background-color: #5FB878 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.layui-tab-content {
|
||||
margin-top: 15px !important;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.layui-form-item {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
.layui-input {
|
||||
height: 44px !important;
|
||||
line-height: 44px !important;
|
||||
padding-left: 15px !important;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.layui-input:focus {
|
||||
box-shadow: 0px 0px 3px 1px #5FB878 !important;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 60px !important;
|
||||
margin-top: 10px !important;
|
||||
margin-bottom: 10px !important;
|
||||
margin-left: 20px !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 30px !important;
|
||||
font-weight: 550 !important;
|
||||
margin-left: 20px !important;
|
||||
color: #5FB878 !important;
|
||||
display: inline-block !important;
|
||||
height: 60px !important;
|
||||
line-height: 60px !important;
|
||||
margin-top: 10px !important;
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.desc {
|
||||
width: 100% !important;
|
||||
text-align: center !important;
|
||||
color: gray !important;
|
||||
height: 60px !important;
|
||||
line-height: 60px !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background-repeat:no-repeat;
|
||||
background-color: whitesmoke;
|
||||
background-size: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.code {
|
||||
float: left;
|
||||
margin-right: 13px;
|
||||
margin: 0px !important;
|
||||
border: #e6e6e6 1px solid;
|
||||
display: inline-block!important;
|
||||
}
|
||||
|
||||
.codeImage {
|
||||
float: right;
|
||||
height: 42px;
|
||||
border: #e6e6e6 1px solid;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
.pear-container {
|
||||
background-color: whitesmoke;
|
||||
margin: 10px;
|
||||
}
|
||||
.layui-body {
|
||||
padding: 25px;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.user-info-head {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
line-height: 110px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.layui-line-dash {
|
||||
border-bottom: 1px dashed #ccc;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.comment {
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
color: dimgray;
|
||||
}
|
||||
.content {
|
||||
padding-left: 13px;
|
||||
font-size: 13px;
|
||||
color: dimgray;
|
||||
}
|
||||
.title {
|
||||
padding-left: 13.5px;
|
||||
}
|
||||
.layui-tab-title {
|
||||
border-bottom: none;
|
||||
}
|
||||
.fl-item {
|
||||
height: 30px;
|
||||
font-size: 13.5;
|
||||
}
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50px;
|
||||
background-color: gray;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.list .list-item {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
color: gray;
|
||||
padding: 5px;
|
||||
padding-left: 15px;
|
||||
border-radius: 4px;
|
||||
margin-top: 5.2px;
|
||||
}
|
||||
|
||||
.list .list-item:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.list .list-item .title {
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list .list-item .footer {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
.result {
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
.result .success svg {
|
||||
color: #32C682;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
|
||||
}
|
||||
.result .error svg {
|
||||
color: #f56c6c;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
|
||||
}
|
||||
.result .title {
|
||||
margin-top: 25px;
|
||||
|
||||
}
|
||||
.result .desc {
|
||||
margin-top: 25px;
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
}
|
||||
.result .content {
|
||||
margin-top: 20px;
|
||||
width: 80%;
|
||||
border-radius: 10px;
|
||||
background-color: whitesmoke;
|
||||
height: 200px;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.result .action {
|
||||
padding-top: 10px;
|
||||
border-top: 1px whitesmoke solid;
|
||||
margin-top: 25px;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "...",
|
||||
"count": 3,
|
||||
"data": [{
|
||||
"id": "1",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png",
|
||||
"title": "Alipay",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
},{
|
||||
"id": "2",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png",
|
||||
"title": "Layui",
|
||||
"remark": "生命就像一盒巧克力,结果往往出人意料",
|
||||
"time": "几秒前"
|
||||
},{
|
||||
"id": "1",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png",
|
||||
"title": "Angular",
|
||||
"remark": "希望是一个好东西,也许是最好的,好东西是不会消亡的",
|
||||
"time": "几秒前"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png",
|
||||
"title": "React",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
},{
|
||||
"id": "1",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png",
|
||||
"title": "Alipay",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
},{
|
||||
"id": "2",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png",
|
||||
"title": "Layui",
|
||||
"remark": "生命就像一盒巧克力,结果往往出人意料",
|
||||
"time": "几秒前"
|
||||
},{
|
||||
"id": "1",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png",
|
||||
"title": "Angular",
|
||||
"remark": "希望是一个好东西,也许是最好的,好东西是不会消亡的",
|
||||
"time": "几秒前"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png",
|
||||
"title": "React",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
[{
|
||||
"id": 1,
|
||||
"title": "工作空间",
|
||||
"type": 0,
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 10,
|
||||
"title": "控制后台",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/console/console1.html"
|
||||
}, {
|
||||
"id": 13,
|
||||
"title": "数据分析",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/console/console2.html"
|
||||
}, {
|
||||
"id": 14,
|
||||
"title": "百度一下",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "http://www.baidu.com"
|
||||
}, {
|
||||
"id": 15,
|
||||
"title": "主题预览",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/theme.html"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "component",
|
||||
"title": "常用组件",
|
||||
"icon": "layui-icon layui-icon-component",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 201,
|
||||
"title": "基础组件",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"children": [{
|
||||
"id": 2011,
|
||||
"title": "功能按钮",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/button.html"
|
||||
}, {
|
||||
"id": 2014,
|
||||
"title": "表单集合",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/form.html"
|
||||
}, {
|
||||
"id": 2010,
|
||||
"title": "字体图标",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/icon.html"
|
||||
}, {
|
||||
"id": 2012,
|
||||
"title": "多选下拉",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/select.html"
|
||||
}, {
|
||||
"id": 2013,
|
||||
"title": "动态标签",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/tag.html"
|
||||
}]
|
||||
}, {
|
||||
"id": 203,
|
||||
"title": "进阶组件",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"children": [{
|
||||
"id": 2031,
|
||||
"title": "数据表格",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/table.html"
|
||||
}, {
|
||||
"id": 2032,
|
||||
"title": "分布表单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/step.html"
|
||||
}, {
|
||||
"id": 2033,
|
||||
"title": "树形表格",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/treetable.html"
|
||||
}, {
|
||||
"id": 2034,
|
||||
"title": "树状结构",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/dtree.html"
|
||||
}, {
|
||||
"id": 2035,
|
||||
"title": "文本编辑",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/tinymce.html"
|
||||
}, {
|
||||
"id": 2036,
|
||||
"title": "卡片组件",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/card.html"
|
||||
}]
|
||||
}, {
|
||||
"id": 202,
|
||||
"title": "弹层组件",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"children": [{
|
||||
"id": 2021,
|
||||
"title": "抽屉组件",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/drawer.html"
|
||||
}, {
|
||||
"id": 2022,
|
||||
"title": "消息通知",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/notice.html"
|
||||
}, {
|
||||
"id": 2024,
|
||||
"title": "加载组件",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/loading.html"
|
||||
}, {
|
||||
"id": 2023,
|
||||
"title": "弹层组件",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/popup.html"
|
||||
}]
|
||||
},{
|
||||
"id": 60331,
|
||||
"title": "高级组件",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"children": [{
|
||||
"id": 60131,
|
||||
"title": "多选项卡",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/tab.html"
|
||||
},{
|
||||
"id": 60132,
|
||||
"title": "数据菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/menu.html"
|
||||
}]
|
||||
}, {
|
||||
"id": 204,
|
||||
"title": "其他组件",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"children": [{
|
||||
"id": 2041,
|
||||
"title": "哈希加密",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/hash.html"
|
||||
},
|
||||
{
|
||||
"id": 2042,
|
||||
"title": "图标选择",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/iconPicker.html"
|
||||
},
|
||||
{
|
||||
"id": 2043,
|
||||
"title": "省市级联",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/area.html"
|
||||
},
|
||||
{
|
||||
"id": 2044,
|
||||
"title": "数字滚动",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/count.html"
|
||||
},
|
||||
{
|
||||
"id": 2045,
|
||||
"title": "顶部返回",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/topBar.html"
|
||||
}
|
||||
]
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "result",
|
||||
"title": "结果页面",
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": "success",
|
||||
"title": "成功",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "failure",
|
||||
"title": "失败",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/result/error.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "error",
|
||||
"title": "错误页面",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 403,
|
||||
"title": "403",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/error/403.html"
|
||||
},
|
||||
{
|
||||
"id": 404,
|
||||
"title": "404",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/error/404.html"
|
||||
},
|
||||
|
||||
{
|
||||
"id": 500,
|
||||
"title": "500",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/error/500.html"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "system",
|
||||
"title": "系统管理",
|
||||
"icon": "layui-icon layui-icon-set-fill",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 601,
|
||||
"title": "用户管理",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/user.html"
|
||||
},
|
||||
{
|
||||
"id": 602,
|
||||
"title": "角色管理",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/role.html"
|
||||
},
|
||||
|
||||
{
|
||||
"id": 603,
|
||||
"title": "权限管理",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/power.html"
|
||||
},
|
||||
|
||||
{
|
||||
"id": 604,
|
||||
"title": "部门管理",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/deptment.html"
|
||||
},
|
||||
{
|
||||
"id": 605,
|
||||
"title": "行为日志",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/log.html"
|
||||
}, {
|
||||
"id": 606,
|
||||
"title": "数据字典",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/dict.html"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "common",
|
||||
"title": "常用页面",
|
||||
"icon": "layui-icon layui-icon-template-1",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 701,
|
||||
"title": "登录页面",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "login.html"
|
||||
}, {
|
||||
"id": 702,
|
||||
"title": "空白页面",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/space.html"
|
||||
}]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"status":{"code":200,"message":"操作成功"},
|
||||
"data": [
|
||||
{"id":"001","title": "湖南省","checkArr": "0","parentId": "0"},
|
||||
{"id":"002","title": "湖北省","checkArr": "0","parentId": "0"},
|
||||
{"id":"003","title": "广东省","checkArr": "0","parentId": "0"},
|
||||
{"id":"004","title": "浙江省","checkArr": "0","parentId": "0"},
|
||||
{"id":"005","title": "福建省","checkArr": "0","parentId": "0"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"code": 0,
|
||||
"msg": null,
|
||||
"count": 2,
|
||||
"data": [{
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": "男 : body",
|
||||
"params": null,
|
||||
"dataId": "1317401149287956480",
|
||||
"dataLabel": "男",
|
||||
"dataValue": "boy",
|
||||
"typeCode": "user_sex",
|
||||
"isDefault": null,
|
||||
"enable": "0"
|
||||
}, {
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": "女 : girl",
|
||||
"params": null,
|
||||
"dataId": "1317402976670711808",
|
||||
"dataLabel": "女",
|
||||
"dataValue": "girl",
|
||||
"typeCode": "user_sex",
|
||||
"isDefault": null,
|
||||
"enable": "0"
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"code": 0,
|
||||
"msg": null,
|
||||
"count": 6,
|
||||
"data": [{
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": null,
|
||||
"params": null,
|
||||
"id": "1304489072256876544",
|
||||
"typeName": "用户状态",
|
||||
"typeCode": "user_status",
|
||||
"description": "用户状态",
|
||||
"enable": "0"
|
||||
}, {
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": null,
|
||||
"params": null,
|
||||
"id": "1317360314219495424",
|
||||
"typeName": "登录类型",
|
||||
"typeCode": "login",
|
||||
"description": "登录类型",
|
||||
"enable": "0"
|
||||
}, {
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": null,
|
||||
"params": null,
|
||||
"id": "1317400519127334912",
|
||||
"typeName": "用户类型",
|
||||
"typeCode": "user_status",
|
||||
"description": "用户类型",
|
||||
"enable": "0"
|
||||
}, {
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": null,
|
||||
"params": null,
|
||||
"id": "1317400823096934400",
|
||||
"typeName": "配置类型",
|
||||
"typeCode": "config_type",
|
||||
"description": "配置类型",
|
||||
"enable": "0"
|
||||
}, {
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": null,
|
||||
"params": null,
|
||||
"id": "455184568505470976",
|
||||
"typeName": "用户性别",
|
||||
"typeCode": "user_sex",
|
||||
"description": "用户性别",
|
||||
"enable": "0"
|
||||
}, {
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"createName": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"updateName": null,
|
||||
"remark": null,
|
||||
"params": null,
|
||||
"id": "455184935989415936",
|
||||
"typeName": "全局状态",
|
||||
"typeCode": "sys_status",
|
||||
"description": "状态描述\n",
|
||||
"enable": "0"
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"status": {
|
||||
"code": 200,
|
||||
"message": "操作成功"
|
||||
},
|
||||
"data": [{
|
||||
"id": "001",
|
||||
"title": "湖南省",
|
||||
"parentId": "0",
|
||||
"children": [{
|
||||
"id": "001001",
|
||||
"title": "长沙市",
|
||||
"last": true,
|
||||
"parentId": "001"
|
||||
},
|
||||
{
|
||||
"id": "001002",
|
||||
"title": "株洲市",
|
||||
"last": true,
|
||||
"parentId": "001"
|
||||
},
|
||||
{
|
||||
"id": "001003",
|
||||
"title": "湘潭市",
|
||||
"last": true,
|
||||
"parentId": "001"
|
||||
},
|
||||
{
|
||||
"id": "001004",
|
||||
"title": "衡阳市",
|
||||
"last": true,
|
||||
"parentId": "001"
|
||||
},
|
||||
{
|
||||
"id": "001005",
|
||||
"title": "郴州市",
|
||||
"last": true,
|
||||
"parentId": "001"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "002",
|
||||
"title": "湖北省",
|
||||
"parentId": "0",
|
||||
"children": [{
|
||||
"id": "002001",
|
||||
"title": "武汉市",
|
||||
"last": true,
|
||||
"parentId": "002"
|
||||
},
|
||||
{
|
||||
"id": "002002",
|
||||
"title": "黄冈市",
|
||||
"last": true,
|
||||
"parentId": "002"
|
||||
},
|
||||
{
|
||||
"id": "002003",
|
||||
"title": "潜江市",
|
||||
"last": true,
|
||||
"parentId": "002"
|
||||
},
|
||||
{
|
||||
"id": "002004",
|
||||
"title": "荆州市",
|
||||
"last": true,
|
||||
"parentId": "002"
|
||||
},
|
||||
{
|
||||
"id": "002005",
|
||||
"title": "襄阳市",
|
||||
"last": true,
|
||||
"parentId": "002"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "003",
|
||||
"title": "广东省",
|
||||
"parentId": "0",
|
||||
"children": [{
|
||||
"id": "003001",
|
||||
"title": "广州市",
|
||||
"last": false,
|
||||
"parentId": "003",
|
||||
"children": [{
|
||||
"id": "003001001",
|
||||
"title": "天河区",
|
||||
"last": true,
|
||||
"parentId": "003001"
|
||||
},
|
||||
{
|
||||
"id": "003001002",
|
||||
"title": "花都区",
|
||||
"last": true,
|
||||
"parentId": "003001"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "003002",
|
||||
"title": "深圳市",
|
||||
"last": true,
|
||||
"parentId": "003"
|
||||
},
|
||||
{
|
||||
"id": "003003",
|
||||
"title": "中山市",
|
||||
"last": true,
|
||||
"parentId": "003"
|
||||
},
|
||||
{
|
||||
"id": "003004",
|
||||
"title": "东莞市",
|
||||
"last": true,
|
||||
"parentId": "003"
|
||||
},
|
||||
{
|
||||
"id": "003005",
|
||||
"title": "珠海市",
|
||||
"last": true,
|
||||
"parentId": "003"
|
||||
},
|
||||
{
|
||||
"id": "003006",
|
||||
"title": "韶关市",
|
||||
"last": true,
|
||||
"parentId": "003"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "004",
|
||||
"title": "浙江省",
|
||||
"parentId": "0",
|
||||
"children": [{
|
||||
"id": "004001",
|
||||
"title": "杭州市",
|
||||
"last": true,
|
||||
"parentId": "004"
|
||||
},
|
||||
{
|
||||
"id": "004002",
|
||||
"title": "温州市",
|
||||
"last": true,
|
||||
"parentId": "004"
|
||||
},
|
||||
{
|
||||
"id": "004003",
|
||||
"title": "绍兴市",
|
||||
"last": true,
|
||||
"parentId": "004"
|
||||
},
|
||||
{
|
||||
"id": "004004",
|
||||
"title": "金华市",
|
||||
"last": true,
|
||||
"parentId": "004"
|
||||
},
|
||||
{
|
||||
"id": "004005",
|
||||
"title": "义乌市",
|
||||
"last": true,
|
||||
"parentId": "004"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "005",
|
||||
"title": "福建省",
|
||||
"parentId": "0",
|
||||
"children": [{
|
||||
"id": "005001",
|
||||
"title": "厦门市",
|
||||
"last": true,
|
||||
"parentId": "005"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user