Compare commits
122
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad0f174996 | ||
|
|
45874b17ee | ||
|
|
d7d7e554ee | ||
|
|
8835721924 | ||
|
|
4748a286db | ||
|
|
7501ae1dc2 | ||
|
|
9fc2eb0ae3 | ||
|
|
12d4dfe410 | ||
|
|
d54eb4dc3e | ||
|
|
246ce3b3b7 | ||
|
|
36250fad43 | ||
|
|
0d25b479e5 | ||
|
|
7e6aecc99b | ||
|
|
82de7b0273 | ||
|
|
8760f21b03 | ||
|
|
1e34ef339a | ||
|
|
5333a68416 | ||
|
|
decde3a69a | ||
|
|
9dcc05deda | ||
|
|
c0d963628b | ||
|
|
6978002393 | ||
|
|
3453b01938 | ||
|
|
820ab9f5c6 | ||
|
|
9490ccb7c0 | ||
|
|
056c4b7023 | ||
|
|
d4d8d8b294 | ||
|
|
8bc5d6e0c2 | ||
|
|
3fa8ae0a8a | ||
|
|
316ebc2441 | ||
|
|
550f99e468 | ||
|
|
b9c449ad57 | ||
|
|
db5b4e89ed | ||
|
|
733286b090 | ||
|
|
7505e4552b | ||
|
|
e918727984 | ||
|
|
00053da117 | ||
|
|
27f5d78c35 | ||
|
|
97e0160aaf | ||
|
|
04794e583f | ||
|
|
0640255519 | ||
|
|
460984f209 | ||
|
|
635dcc1dac | ||
|
|
b26237e24b | ||
|
|
a35d443fb6 | ||
|
|
ab4bb1ca40 | ||
|
|
2b7304e615 | ||
|
|
64748e513d | ||
|
|
5b07fed518 | ||
|
|
eb85e30b79 | ||
|
|
b8e0173a9c | ||
|
|
0ca75c81d4 | ||
|
|
aa86bbe372 | ||
|
|
86f07817b1 | ||
|
|
3f74011c98 | ||
|
|
ce8adda79d | ||
|
|
5470837707 | ||
|
|
c26f15ae69 | ||
|
|
4d6d998ae2 | ||
|
|
d628432621 | ||
|
|
2bcaa8e071 | ||
|
|
ebf76055a4 | ||
|
|
b2b08769b0 | ||
|
|
aae15fd07a | ||
|
|
945f746d97 | ||
|
|
dce7908041 | ||
|
|
0ba2b3aa56 | ||
|
|
17792ccacf | ||
|
|
e3dd5458ad | ||
|
|
e9bd8a17a0 | ||
|
|
b8870d4c33 | ||
|
|
8fba71c464 | ||
|
|
0cb74c5eb7 | ||
|
|
6da6768f81 | ||
|
|
05dff28523 | ||
|
|
d7c7237cd4 | ||
|
|
9e8a5357be | ||
|
|
5faf88283f | ||
|
|
d9a5bcb0de | ||
|
|
48633729fb | ||
|
|
f274e060f4 | ||
|
|
9784f7816a | ||
|
|
f93a36c094 | ||
|
|
375d3a10b2 | ||
|
|
a1fbf5a0ae | ||
|
|
10035cc158 | ||
|
|
3b1150ad1f | ||
|
|
73222e854c | ||
|
|
3db99422b7 | ||
|
|
d876c8309f | ||
|
|
e0738aa5b2 | ||
|
|
f051693690 | ||
|
|
c6ffbbc062 | ||
|
|
2836fc1139 | ||
|
|
e111f4a763 | ||
|
|
3d5e4b9e9b | ||
|
|
8f4054e576 | ||
|
|
3ea4edd117 | ||
|
|
2f0df0a700 | ||
|
|
a709095dc2 | ||
|
|
4a8ac0ac83 | ||
|
|
b9301046da | ||
|
|
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_ENV=development
|
||||
FLASK_RUN_HOST=0.0.0.0
|
||||
FLASK_APP='applications:create_app'
|
||||
|
||||
+121
-2
@@ -1,5 +1,124 @@
|
||||
.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/
|
||||
.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/
|
||||
|
||||
# 数据库文件
|
||||
*.db
|
||||
#上传到本地的图片文件
|
||||
*upload/
|
||||
migrations/
|
||||
@@ -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,136 @@
|
||||
## 本地启动
|
||||
<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 开发者提供一个快速开发前后端半分离的后台管理系统的模板
|
||||
|
||||
在使用本项目之前最好掌握以下知识点
|
||||
|
||||
+ [html、css、JavaScript](https://developer.mozilla.org/zh-CN/docs/Learn/HTML)
|
||||
+ [jQuery](https://www.w3school.com.cn/jquery/index.asp)
|
||||
+ [layui](https://www.layui.com/)
|
||||
+ [flask](https://dormousehole.readthedocs.io/en/latest/)
|
||||
+ [flask-login](https://flask-login.readthedocs.io/en/latest/)
|
||||
+ [flask-sqlalchemy](http://www.pythondoc.com/flask-sqlalchemy/quickstart.html)
|
||||
|
||||
|
||||
## 预览
|
||||
Pear Admin Flask 有以下几个版本:
|
||||
|
||||
[master分支版本 ](https://gitee.com/pear-admin/pear-admin-flask/tree/master/)
|
||||
|
||||
>flask 2.x + flask-sqlalchemy + 权限验证 + Flask-APScheduler 定时任务 + marshmallow 序列化与数据验证
|
||||
|
||||
[Mini 分支版本 ](https://gitee.com/pear-admin/pear-admin-flask/tree/mini/)
|
||||
|
||||
>flask 2.x + flask-sqlalchemy + 基于角色的权限管理
|
||||
|
||||
| | |
|
||||
|---------------------|---------------------|
|
||||
|  |  |
|
||||
| |  |
|
||||
|  |  |
|
||||
|
||||
## 内置功能
|
||||
|
||||
- [x] 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
- [x] 权限管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||
- [x] 角色管理:角色菜单权限分配。
|
||||
- [x] 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
- [x] 登录日志:系统登录日志记录查询包含登录异常。
|
||||
- [x] 文件上传: 图片上传示例
|
||||
|
||||
|
||||
## 文档
|
||||
正在编写
|
||||
|
||||
|
||||
## 安装使用
|
||||
|
||||
+ 下载源码
|
||||
```bash
|
||||
git clone https://gitee.com/pear-admin/pear-admin-flask
|
||||
|
||||
# 切换分支
|
||||
git checkout mini
|
||||
```
|
||||
|
||||
初始化数据库
|
||||
+ 安装依赖
|
||||
```bash
|
||||
# 创建虚拟环境
|
||||
python -m venv venv
|
||||
|
||||
```shell
|
||||
flask init
|
||||
# 然后使虚拟环境生效(windows)
|
||||
venv\Scripts\activate
|
||||
# source venv/bin/activate # (Linux激活虚拟环境)
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirement.txt
|
||||
```
|
||||
|
||||
启动
|
||||
+ 数据迁移
|
||||
|
||||
```shell
|
||||
默认的使用 `sqlite3` 作为测试环境的数据库进行演示,不需要按照mysql即可查看演示。如果需要二次开发,建议改成 `mysql` 。
|
||||
|
||||
如果需要在开发环境使用 mysql 作为数据库,请查看 `applications/configs/config.py` 文件里面的相关配置文件, 注释掉 sqlite 的配置即可
|
||||
|
||||
如果需要修改数据的配置信息,请在 `.flaskenv` 里面调整即可
|
||||
|
||||
```bash
|
||||
flask db init
|
||||
flask db migrate -m '数据初始化'
|
||||
flask db upgrade
|
||||
|
||||
flask init-db
|
||||
```
|
||||
|
||||
+ 项目启动
|
||||
|
||||
```bash
|
||||
flask run
|
||||
```
|
||||
|
||||
## 贡献指南
|
||||
## Git 贡献提交规范
|
||||
|
||||
如果想参与项目的贡献,提交代码之前需要启用 pre-commit、commitizen 对代码进行校验,运行以下指令即可。
|
||||
|
||||
初始化 pre-commit
|
||||
|
||||
```shell
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
检查代码是否符合规范
|
||||
|
||||
```shell
|
||||
git add .
|
||||
```
|
||||
|
||||
```shell
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
初始化 commitizen
|
||||
|
||||
```shell
|
||||
pre-commit install -t commit-msg
|
||||
```
|
||||
|
||||
使用
|
||||
|
||||
```shell
|
||||
cz commit
|
||||
```
|
||||
|
||||
代替 `git commit` 进行提交
|
||||
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||
- `feat` 增加新功能
|
||||
- `fix` 修复问题/BUG
|
||||
- `style` 代码风格相关无影响运行结果的
|
||||
- `perf` 优化/性能提升
|
||||
- `refactor` 重构
|
||||
- `revert` 撤销修改
|
||||
- `test` 测试相关
|
||||
- `docs` 文档/注释
|
||||
- `chore` 依赖更新/脚手架配置修改等
|
||||
- `workflow` 工作流改进
|
||||
- `ci` 持续集成
|
||||
- `types` 类型定义文件更改
|
||||
- `wip` 开发中
|
||||
@@ -0,0 +1,60 @@
|
||||
import os
|
||||
|
||||
from flask import Flask, Blueprint
|
||||
|
||||
import config
|
||||
from applications.view import init_view
|
||||
from common import register_api
|
||||
from common.flask_uploads import configure_uploads
|
||||
from common.utils.upload import photos
|
||||
from extensions import init_plugs
|
||||
|
||||
api_bp: Blueprint = Blueprint('api', __name__, url_prefix='/api/v1')
|
||||
|
||||
from .rights import register_rights_api
|
||||
from .system import register_sys_api
|
||||
from .users import register_users_api
|
||||
|
||||
register_rights_api(api_bp)
|
||||
register_users_api(api_bp)
|
||||
register_sys_api(api_bp)
|
||||
|
||||
|
||||
def init_api(app: Flask) -> None:
|
||||
app.register_blueprint(api_bp)
|
||||
|
||||
|
||||
def create_app() -> Flask:
|
||||
app = Flask('pear-admin-flask')
|
||||
|
||||
# 引入数据库配置
|
||||
app.config.from_object(config)
|
||||
|
||||
# 注册各种插件
|
||||
init_plugs(app)
|
||||
|
||||
# 注册路由
|
||||
init_view(app)
|
||||
|
||||
# 注册接口(restful api)
|
||||
init_api(app)
|
||||
|
||||
# 文件上传
|
||||
configure_uploads(app, photos)
|
||||
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
|
||||
logo()
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def logo():
|
||||
print('''
|
||||
_____ _ _ ______ _ _
|
||||
| __ \ /\ | | (_) | ____| | | |
|
||||
| |__) |__ __ _ _ __ / \ __| |_ __ ___ _ _ __ | |__ | | __ _ ___| | __
|
||||
| ___/ _ \/ _` | '__| / /\ \ / _` | '_ ` _ \| | '_ \ | __| | |/ _` / __| |/ /
|
||||
| | | __/ (_| | | / ____ \ (_| | | | | | | | | | | | | | | (_| \__ \ <
|
||||
|_| \___|\__,_|_| /_/ \_\__,_|_| |_| |_|_|_| |_| |_| |_|\__,_|___/_|\_\\
|
||||
|
||||
''')
|
||||
@@ -0,0 +1,42 @@
|
||||
from common import register_api
|
||||
from .department import DepartmentsApi, DeptEnableAPI
|
||||
from .rights import (
|
||||
RightsApi, PowerApi, right_power_enable_resource,
|
||||
admin_configs_resource, admin_menu_resource
|
||||
)
|
||||
from .roles import RoleRoleApi, RolePowerApi, role_deletes, role_enable_resource
|
||||
|
||||
|
||||
def register_rights_api(api_bp):
|
||||
register_api(DepartmentsApi, 'rights_dept_api', '/dept/department/', pk='_id', app=api_bp)
|
||||
api_bp.add_url_rule('/dept/department/<int:_id>/status',
|
||||
view_func=DeptEnableAPI.as_view('dept_status_api'),
|
||||
methods=['PUT', ])
|
||||
api_bp.add_url_rule('/rights/rights',
|
||||
view_func=RightsApi.as_view('dept_rights_api'),
|
||||
methods=['GET', 'DELETE'])
|
||||
|
||||
register_api(PowerApi, 'rights_power_api', '/rights/power/', pk='_id', app=api_bp)
|
||||
api_bp.add_url_rule('/rights/configs',
|
||||
endpoint='rights_config_api',
|
||||
view_func=admin_configs_resource,
|
||||
methods=['GET'])
|
||||
api_bp.add_url_rule('/rights/menu',
|
||||
endpoint='rights_menu_api',
|
||||
view_func=admin_menu_resource,
|
||||
methods=['GET'])
|
||||
api_bp.add_url_rule('/rights/power/<int:_id>/<action>',
|
||||
endpoint='rights_power_action_api',
|
||||
view_func=right_power_enable_resource,
|
||||
methods=['PUT'])
|
||||
api_bp.add_url_rule('/roles/role',
|
||||
endpoint='rights_roles_role_api',
|
||||
view_func=role_deletes,
|
||||
methods=['DELETE'])
|
||||
|
||||
register_api(RoleRoleApi, 'rights_role_api', '/roles/role/', pk='_id', app=api_bp)
|
||||
register_api(RolePowerApi, 'rights_role_power_api', '/roles/role_power/', pk='_id', app=api_bp)
|
||||
api_bp.add_url_rule('/roles/role/<int:_id>/status',
|
||||
endpoint='rights_roles_enable_api',
|
||||
view_func=role_enable_resource,
|
||||
methods=['PUT'])
|
||||
@@ -0,0 +1,112 @@
|
||||
import typing as t
|
||||
|
||||
from flask.views import MethodView
|
||||
from flask_pydantic import validate
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from common.utils.http import success_api, fail_api, table_api
|
||||
from extensions import db
|
||||
from models import DepartmentModel, UserModel
|
||||
|
||||
|
||||
class DeptSchema(BaseModel):
|
||||
address: t.Optional[str]
|
||||
dept_name: t.Optional[str] = Field(alias='deptName')
|
||||
email: t.Optional[str]
|
||||
leader: t.Optional[str]
|
||||
parent_id: t.Optional[str] = Field(alias='parentId')
|
||||
phone: t.Optional[str]
|
||||
sort: t.Optional[int]
|
||||
status: t.Optional[int]
|
||||
|
||||
|
||||
class DepartmentsApi(MethodView):
|
||||
|
||||
def get(self, _id):
|
||||
if _id:
|
||||
dept = DepartmentModel.query.filter_by(id=_id).first()
|
||||
dept_data = {
|
||||
'id': dept.id,
|
||||
'dept_name': dept.dept_name,
|
||||
'leader': dept.leader,
|
||||
'email': dept.email,
|
||||
'phone': dept.phone,
|
||||
'status': dept.status,
|
||||
'sort': dept.sort,
|
||||
'address': dept.address,
|
||||
}
|
||||
return dict(success=True, message='ok', dept=dept_data)
|
||||
|
||||
dept_data = DepartmentModel.query.order_by(DepartmentModel.sort).all()
|
||||
return table_api(
|
||||
result={
|
||||
'items': [
|
||||
{
|
||||
'deptId': item.id,
|
||||
'parentId': item.parent_id,
|
||||
'deptName': item.dept_name,
|
||||
'sort': item.sort,
|
||||
'leader': item.leader,
|
||||
'phone': item.phone,
|
||||
'email': item.email,
|
||||
'status': item.status,
|
||||
'comment': item.comment,
|
||||
'address': item.address,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S')
|
||||
} for item in dept_data
|
||||
],
|
||||
'total': len(dept_data)}
|
||||
, code=0)
|
||||
|
||||
@validate()
|
||||
def post(self, body: DeptSchema):
|
||||
dept = DepartmentModel(
|
||||
parent_id=body.parent_id,
|
||||
dept_name=body.dept_name,
|
||||
sort=body.sort,
|
||||
leader=body.leader,
|
||||
phone=body.phone,
|
||||
email=body.email,
|
||||
status=body.status,
|
||||
address=body.address
|
||||
)
|
||||
db.session.add(dept)
|
||||
db.session.commit()
|
||||
|
||||
return success_api(message="成功")
|
||||
|
||||
@validate()
|
||||
def put(self, _id, body: DeptSchema):
|
||||
data = {
|
||||
"dept_name": body.dept_name,
|
||||
"sort": body.sort,
|
||||
"leader": body.leader,
|
||||
"phone": body.phone,
|
||||
"email": body.email,
|
||||
"status": body.status,
|
||||
"address": body.address
|
||||
}
|
||||
body = DepartmentModel.query.filter_by(id=_id).update(data)
|
||||
if not body:
|
||||
return {'success': False, 'message': "更新失败", 'code': 404}
|
||||
db.session.commit()
|
||||
return {'success': True, 'message': '更新成功', 'code': 200}
|
||||
|
||||
def delete(self, _id):
|
||||
ret = DepartmentModel.query.filter_by(id=_id).delete()
|
||||
UserModel.query.filter_by(dept_id=_id).update({"dept_id": None})
|
||||
db.session.commit()
|
||||
if ret:
|
||||
return success_api(message="删除成功")
|
||||
return fail_api(message="删除失败")
|
||||
|
||||
|
||||
class DeptEnableAPI(MethodView):
|
||||
def put(self, _id):
|
||||
d = DepartmentModel.query.get(_id)
|
||||
if d:
|
||||
d.status = not d.status
|
||||
db.session.commit()
|
||||
message = '修改成功'
|
||||
return success_api(message=message)
|
||||
return fail_api(message="出错啦")
|
||||
@@ -0,0 +1,278 @@
|
||||
import copy
|
||||
from collections import OrderedDict
|
||||
from typing import Optional
|
||||
|
||||
from flask import request, jsonify, current_app
|
||||
from flask.views import MethodView
|
||||
from flask_login import current_user
|
||||
from flask_pydantic import validate
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from common.utils.http import success_api, fail_api
|
||||
from extensions import db
|
||||
from models import RightModel, RoleModel
|
||||
|
||||
|
||||
def get_render_config():
|
||||
# 网站配置
|
||||
config = {
|
||||
'logo': {
|
||||
# 网站名称
|
||||
"title": current_app.config.get("SYSTEM_NAME"),
|
||||
# 网站图标
|
||||
"image": "/static/admin/admin/images/logo.png"
|
||||
# 菜单配置
|
||||
},
|
||||
'menu': {
|
||||
# 菜单数据来源
|
||||
"data": "/api/v1/rights/menu",
|
||||
"collaspe": True,
|
||||
# 是否同时只打开一个菜单目录
|
||||
"accordion": True,
|
||||
"method": "GET",
|
||||
# 是否开启多系统菜单模式
|
||||
"control": False,
|
||||
# 默认选中的菜单项
|
||||
"select": "0",
|
||||
# 是否开启异步菜单,false 时 data 属性设置为菜单数据,false 时为 json 文件或后端接口
|
||||
"async": True
|
||||
},
|
||||
'tab': {
|
||||
# 是否开启多选项卡
|
||||
"muiltTab": True,
|
||||
# 切换选项卡时,是否刷新页面状态
|
||||
"keepState": True,
|
||||
# 是否开启 Tab 记忆
|
||||
"session": True,
|
||||
# 最大可打开的选项卡数量
|
||||
"tabMax": 30,
|
||||
"index": {
|
||||
# 标识 ID , 建议与菜单项中的 ID 一致
|
||||
"id": "10",
|
||||
# 页面地址
|
||||
"href": "/admin/welcome",
|
||||
# 标题
|
||||
"title": "首页"
|
||||
}
|
||||
},
|
||||
'theme': {
|
||||
# 默认主题色,对应 colors 配置中的 ID 标识
|
||||
"defaultColor": "2",
|
||||
# 默认的菜单主题 dark-theme 黑 / light-theme 白
|
||||
"defaultMenu": "dark-theme",
|
||||
# 是否允许用户切换主题,false 时关闭自定义主题面板
|
||||
"allowCustom": True
|
||||
},
|
||||
'colors': [
|
||||
{
|
||||
"id": "1",
|
||||
"color": "#2d8cf0"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"color": "#5FB878"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"color": "#1E9FFF"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"color": "#FFB800"
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"color": "darkgray"
|
||||
},
|
||||
],
|
||||
'links': current_app.config.get("SYSTEM_PANEL_LINKS"),
|
||||
'other': {
|
||||
# 主页动画时长
|
||||
"keepLoad": 1200,
|
||||
# 布局顶部主题
|
||||
"autoHead": False
|
||||
},
|
||||
'header': False
|
||||
}
|
||||
return config
|
||||
|
||||
|
||||
# 生成菜单树
|
||||
def make_menu_tree():
|
||||
role = current_user.role
|
||||
powers = []
|
||||
for i in role:
|
||||
# 如果角色没有被启用就直接跳过
|
||||
if i.enable == 0:
|
||||
continue
|
||||
# 变量角色用户的权限
|
||||
for p in i.power:
|
||||
# 如果权限关闭了就直接跳过
|
||||
if p.enable == 0:
|
||||
continue
|
||||
# 一二级菜单
|
||||
if p.type == 0 or p.type == 1:
|
||||
powers.append(p)
|
||||
|
||||
power_dict = [
|
||||
{
|
||||
'id': item.id,
|
||||
'title': item.name,
|
||||
'type': item.type,
|
||||
'code': item.code,
|
||||
'href': item.url,
|
||||
'openType': item.open_type,
|
||||
'parent_id': item.parent_id,
|
||||
'icon': item.icon,
|
||||
'sort': item.sort,
|
||||
'enable': item.enable,
|
||||
} for item in powers
|
||||
]
|
||||
power_dict.sort(key=lambda x: x['id'], reverse=True)
|
||||
|
||||
menu_dict = OrderedDict()
|
||||
for _dict in power_dict:
|
||||
if _dict['id'] in menu_dict:
|
||||
# 当前节点添加子节点
|
||||
_dict['children'] = copy.deepcopy(menu_dict[_dict['id']])
|
||||
_dict['children'].sort(key=lambda item: item['sort'])
|
||||
# 删除子节点
|
||||
del menu_dict[_dict['id']]
|
||||
|
||||
if _dict['parent_id'] not in menu_dict:
|
||||
menu_dict[_dict['parent_id']] = [_dict]
|
||||
else:
|
||||
menu_dict[_dict['parent_id']].append(_dict)
|
||||
|
||||
return sorted(menu_dict.get(0), key=lambda item: item['sort'])
|
||||
|
||||
|
||||
class PowerSchema(BaseModel):
|
||||
icon: str
|
||||
open_type: Optional[str] = Field(alias='openType')
|
||||
parent_id: Optional[str] = Field(alias='parentId')
|
||||
power_code: Optional[str] = Field(alias='powerCode')
|
||||
power_name: Optional[str] = Field(alias='powerName')
|
||||
power_type: Optional[str] = Field(alias='powerType')
|
||||
power_url: Optional[str] = Field(alias='powerUrl')
|
||||
sort: Optional[int] = Field(alias='sort')
|
||||
|
||||
|
||||
class RightsApi(MethodView):
|
||||
def get(self):
|
||||
"""获取选择父节点"""
|
||||
|
||||
power = RightModel.query.all()
|
||||
# power_data = marshal(power, RightModel.fields())
|
||||
power_data = [
|
||||
{
|
||||
'powerId': item.id,
|
||||
'powerName': item.name,
|
||||
'powerType': item.type,
|
||||
'powerUrl': item.url,
|
||||
'openType': item.open_type,
|
||||
'parentId': item.parent_id,
|
||||
'icon': item.icon,
|
||||
'sort': item.sort,
|
||||
'enable': item.enable,
|
||||
} for item in power
|
||||
]
|
||||
power_data.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
|
||||
res = {
|
||||
"status": {"code": 200, "message": "默认"},
|
||||
"data": power_data
|
||||
}
|
||||
return res
|
||||
|
||||
def delete(self):
|
||||
ids = request.form.getlist('ids[]')
|
||||
for id in ids:
|
||||
role = RoleModel.query.filter_by(id=id).first()
|
||||
# 删除该角色的权限和用户
|
||||
role.power = []
|
||||
role.user = []
|
||||
|
||||
r = RoleModel.query.filter_by(id=id).delete()
|
||||
db.session.commit()
|
||||
return success_api(message="批量删除成功")
|
||||
|
||||
|
||||
class PowerApi(MethodView):
|
||||
@validate()
|
||||
def post(self, body: PowerSchema):
|
||||
power = RightModel(
|
||||
icon=body.icon,
|
||||
open_type=body.open_type,
|
||||
parent_id=body.parent_id,
|
||||
code=body.power_code,
|
||||
name=body.power_name,
|
||||
type=body.power_type,
|
||||
url=body.power_url,
|
||||
sort=body.sort,
|
||||
enable=1
|
||||
)
|
||||
try:
|
||||
db.session.add(power)
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return fail_api(message='数据提交失败')
|
||||
|
||||
return success_api(message="成功")
|
||||
|
||||
def delete(self, _id):
|
||||
# 删除权限(目前没有判断父节点自动删除子节点)
|
||||
power = RightModel.query.filter_by(id=_id).first()
|
||||
role_id_list = []
|
||||
roles = power.role
|
||||
for role in roles:
|
||||
role_id_list.append(role.id)
|
||||
roles = RoleModel.query.filter(RoleModel.id.in_(role_id_list)).all()
|
||||
for p in roles:
|
||||
power.role.remove(p)
|
||||
r = RightModel.query.filter_by(id=_id).delete()
|
||||
db.session.commit()
|
||||
|
||||
if r:
|
||||
return success_api(message="删除成功")
|
||||
else:
|
||||
return fail_api(message="删除失败")
|
||||
|
||||
@validate()
|
||||
def put(self, _id, body: PowerSchema):
|
||||
data = {
|
||||
"icon": body.icon,
|
||||
"open_type": body.open_type,
|
||||
"parent_id": body.parent_id,
|
||||
"code": body.power_code,
|
||||
"name": body.power_name,
|
||||
"type": body.power_type,
|
||||
"url": body.power_url,
|
||||
"sort": body.sort
|
||||
}
|
||||
power = RightModel.query.filter_by(id=_id).update(data)
|
||||
db.session.commit()
|
||||
|
||||
if not power:
|
||||
return fail_api(message="更新权限失败")
|
||||
return success_api(message="更新权限成功")
|
||||
|
||||
|
||||
def right_power_enable_resource(_id, action):
|
||||
power = RightModel.query.get(_id)
|
||||
if power:
|
||||
power.enable = not power.enable
|
||||
db.session.commit()
|
||||
message = "修改成功"
|
||||
return success_api(message=message)
|
||||
else:
|
||||
return fail_api(message="出错啦")
|
||||
|
||||
|
||||
def admin_configs_resource():
|
||||
return get_render_config()
|
||||
|
||||
|
||||
def admin_menu_resource():
|
||||
menu_tree = make_menu_tree()
|
||||
return jsonify(menu_tree)
|
||||
@@ -0,0 +1,162 @@
|
||||
from flask import request
|
||||
from flask.views import MethodView
|
||||
|
||||
from common.utils.http import table_api, success_api, fail_api
|
||||
from extensions import db
|
||||
from models import RightModel, RoleModel
|
||||
|
||||
|
||||
def role_deletes():
|
||||
ids = request.form.getlist('ids[]')
|
||||
for id in ids:
|
||||
role = RoleModel.query.filter_by(id=id).first()
|
||||
# 删除该角色的权限和用户
|
||||
role.power = []
|
||||
role.user = []
|
||||
|
||||
r = RoleModel.query.filter_by(id=id).delete()
|
||||
db.session.commit()
|
||||
return success_api(message="批量删除成功")
|
||||
|
||||
|
||||
class RoleRoleApi(MethodView):
|
||||
|
||||
def get(self, _id):
|
||||
if not _id:
|
||||
page = request.args.get('page', default=1, type=int)
|
||||
limit = request.args.get('limit', default=10, type=int)
|
||||
role_name = request.args.get('roleName', default="", type=str)
|
||||
role_code = request.args.get('roleCode', default="", type=str)
|
||||
|
||||
filters = []
|
||||
if role_name:
|
||||
filters.append(RoleModel.name.like('%' + role_name + '%'))
|
||||
if role_code:
|
||||
filters.append(RoleModel.code.like('%' + role_code + '%'))
|
||||
|
||||
paginate = RoleModel.query.filter(*filters).paginate(page=page, per_page=limit, error_out=False)
|
||||
|
||||
return table_api(
|
||||
result={
|
||||
'items': [{'id': item.id,
|
||||
'roleName': item.name,
|
||||
'roleCode': item.code,
|
||||
'enable': item.enable,
|
||||
'comment': item.comment,
|
||||
'details': item.details,
|
||||
'sort': item.sort,
|
||||
} for item in paginate.items],
|
||||
'total': paginate.total}
|
||||
, code=0)
|
||||
|
||||
def post(self):
|
||||
# TODO 添加校验
|
||||
details = request.json.get('details', '')
|
||||
enable = request.json.get('enable', 0)
|
||||
role_code = request.json.get('roleCode', '')
|
||||
role_name = request.json.get('roleName', '')
|
||||
sort = request.json.get('sort', 0)
|
||||
|
||||
role = RoleModel(
|
||||
details=details,
|
||||
enable=int(enable),
|
||||
code=role_code,
|
||||
name=role_name,
|
||||
sort=int(sort)
|
||||
)
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
return success_api(message="成功")
|
||||
|
||||
# 更新角色
|
||||
def put(self, _id):
|
||||
# TODO 添加校验
|
||||
role_code = request.json.get('roleCode', 0)
|
||||
role_name = request.json.get('roleName', '')
|
||||
sort = request.json.get('sort', 0) # int
|
||||
enable = request.json.get('enable', 0) # int
|
||||
details = request.json.get('details', '')
|
||||
|
||||
data = {
|
||||
"code": role_code,
|
||||
"name": role_name,
|
||||
"sort": sort,
|
||||
"enable": enable,
|
||||
"details": details
|
||||
}
|
||||
|
||||
role = RoleModel.query.filter_by(id=_id).update(data)
|
||||
db.session.commit()
|
||||
if not role:
|
||||
return fail_api(message="更新角色失败")
|
||||
return success_api(message="更新角色成功")
|
||||
|
||||
|
||||
def role_enable_resource(_id):
|
||||
"""启用用户"""
|
||||
ret = RoleModel.query.get(_id)
|
||||
ret.enable = not ret.enable
|
||||
db.session.commit()
|
||||
|
||||
message = "修改成功"
|
||||
if not ret:
|
||||
return fail_api(message="出错啦")
|
||||
return success_api(message=message)
|
||||
|
||||
|
||||
class RolePowerApi(MethodView):
|
||||
|
||||
def get(self, _id):
|
||||
# 获取角色权限
|
||||
role = RoleModel.query.filter_by(id=_id).first()
|
||||
# 获取权限列表的 id
|
||||
check_powers_list = [rp.id for rp in role.power]
|
||||
powers = RightModel.query.all() # 获取所有的权限
|
||||
powers = [
|
||||
{
|
||||
'powerId': item.id,
|
||||
'powerName': item.name,
|
||||
'powerType': item.type,
|
||||
'powerUrl': item.url,
|
||||
'openType': item.open_type,
|
||||
'parentId': item.parent_id,
|
||||
'icon': item.icon,
|
||||
'sort': item.sort,
|
||||
'enable': item.enable,
|
||||
} for item in powers]
|
||||
for i in powers:
|
||||
if int(i.get("powerId")) in check_powers_list:
|
||||
i["checkArr"] = "1"
|
||||
else:
|
||||
i["checkArr"] = "0"
|
||||
return {
|
||||
"data": powers,
|
||||
"status": {"code": 200, "message": "默认"}
|
||||
}
|
||||
|
||||
# 保存角色权限
|
||||
def put(self, _id):
|
||||
power_ids = request.json.get('powerIds', '')
|
||||
|
||||
power_list = power_ids.split(',')
|
||||
|
||||
""" 更新角色权限 """
|
||||
role = RoleModel.query.filter_by(id=_id).first()
|
||||
powers = RightModel.query.filter(RightModel.id.in_(power_list)).all()
|
||||
role.power = powers
|
||||
|
||||
db.session.commit()
|
||||
return success_api(message="授权成功")
|
||||
|
||||
# 角色删除
|
||||
def delete(self, _id):
|
||||
role = RoleModel.query.filter_by(id=_id).first()
|
||||
# 删除该角色的权限和用户
|
||||
role.power = []
|
||||
role.user = []
|
||||
|
||||
r = RoleModel.query.filter_by(id=_id).delete()
|
||||
db.session.commit()
|
||||
if not r:
|
||||
return fail_api(message="角色删除失败")
|
||||
return success_api(message="角色删除成功")
|
||||
@@ -0,0 +1,8 @@
|
||||
from common import register_api
|
||||
from .file import FilePhotoAPI
|
||||
from .passport import LoginAPI
|
||||
|
||||
|
||||
def register_sys_api(api_bp):
|
||||
register_api(LoginAPI, 'login_api', '/passport/login', pk='_id', app=api_bp)
|
||||
register_api(FilePhotoAPI, 'photo_api', '/file/photo', pk='photo_id', app=api_bp)
|
||||
@@ -0,0 +1,78 @@
|
||||
from flask import request, jsonify
|
||||
from flask.views import MethodView
|
||||
from sqlalchemy import desc
|
||||
|
||||
from common.utils.http import fail_api, success_api, table_api
|
||||
from common.utils.upload import upload_one, delete_photo_by_id
|
||||
from models import PhotoModel
|
||||
|
||||
|
||||
class FilePhotoAPI(MethodView):
|
||||
|
||||
def get(self, photo_id):
|
||||
if photo_id is None:
|
||||
page = request.args.get('page', type=int, default=1)
|
||||
limit = request.args.get('limit', type=int, default=10)
|
||||
photo_paginate = PhotoModel.query.order_by(
|
||||
desc(PhotoModel.create_at)).paginate(
|
||||
page=page, per_page=limit, error_out=False)
|
||||
data = [
|
||||
{
|
||||
'id': item.id,
|
||||
'name': item.name,
|
||||
'href': item.href,
|
||||
'mime': item.mime,
|
||||
'size': item.size,
|
||||
'ext': item.ext if hasattr(item, 'ext') else "",
|
||||
'create_at': str(item.create_at),
|
||||
} for item in photo_paginate.items
|
||||
]
|
||||
return table_api(
|
||||
result={
|
||||
'items': data,
|
||||
'total': photo_paginate.total,
|
||||
},
|
||||
code=0)
|
||||
else:
|
||||
# 显示一张图片
|
||||
item = PhotoModel.query.get(photo_id)
|
||||
return table_api(
|
||||
result={
|
||||
'items': {
|
||||
'id': item.id,
|
||||
'name': item.name,
|
||||
'href': item.href,
|
||||
'mime': item.mime,
|
||||
'size': item.size,
|
||||
'ext': item.ext if hasattr(item, 'ext') else "",
|
||||
'create_at': str(item.create_at),
|
||||
},
|
||||
'total': 1,
|
||||
},
|
||||
code=0
|
||||
)
|
||||
|
||||
def post(self):
|
||||
if 'file' in request.files:
|
||||
photo = request.files['file']
|
||||
mime = request.files['file'].content_type
|
||||
file_url = upload_one(photo=photo, mime=mime)
|
||||
|
||||
res = {
|
||||
"message": "上传成功",
|
||||
"code": 0,
|
||||
"success": True,
|
||||
"data": {"src": file_url},
|
||||
}
|
||||
return jsonify(res)
|
||||
return fail_api()
|
||||
|
||||
def delete(self, photo_id):
|
||||
res = delete_photo_by_id(photo_id)
|
||||
if res:
|
||||
return success_api(message="删除成功")
|
||||
else:
|
||||
return fail_api(message="删除失败")
|
||||
|
||||
def put(self, photo_id):
|
||||
pass
|
||||
@@ -0,0 +1,52 @@
|
||||
from flask import render_template, make_response
|
||||
from flask import session, redirect, url_for
|
||||
from flask.views import MethodView
|
||||
from flask_login import current_user, login_user
|
||||
from flask_pydantic import validate
|
||||
from pydantic import BaseModel
|
||||
|
||||
from common.gen_captcha import add_auth_session
|
||||
from common.utils.http import fail_api, success_api
|
||||
from common.utils.rights import record_logging
|
||||
from models import UserModel
|
||||
|
||||
|
||||
class LoginSchema(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
captcha: str
|
||||
|
||||
|
||||
class LoginAPI(MethodView):
|
||||
|
||||
def get(self):
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('admin.index'))
|
||||
return make_response(render_template('index/login.html'))
|
||||
|
||||
@validate()
|
||||
def post(self, body: LoginSchema):
|
||||
s_code = session.get("code", None)
|
||||
session["code"] = None
|
||||
|
||||
if body.captcha != s_code:
|
||||
return fail_api(message="验证码错误")
|
||||
user = UserModel.query.filter_by(username=body.username).first()
|
||||
|
||||
if user is None:
|
||||
return fail_api(message="不存在的用户")
|
||||
|
||||
if user.enable == 0:
|
||||
return fail_api(message="用户被暂停使用")
|
||||
|
||||
if user.validate_password(body.password):
|
||||
# 登录
|
||||
login_user(user)
|
||||
# 记录登录日志
|
||||
record_logging()
|
||||
|
||||
# 存入权限
|
||||
add_auth_session()
|
||||
return success_api(message="登录成功")
|
||||
record_logging()
|
||||
return fail_api(message="用户名或密码错误")
|
||||
@@ -0,0 +1,14 @@
|
||||
from common import register_api
|
||||
from .user import UserApi, user_role_resource, user_info
|
||||
|
||||
|
||||
def register_users_api(api_bp):
|
||||
api_bp.add_url_rule('/users/user/<int:_id>/<action>',
|
||||
view_func=user_info,
|
||||
methods=['PUT'])
|
||||
|
||||
register_api(UserApi, 'users_api', '/users/user/', pk='_id', app=api_bp)
|
||||
|
||||
api_bp.add_url_rule('/users/user/<int:_id>/role',
|
||||
view_func=user_role_resource,
|
||||
methods=['PUT'])
|
||||
@@ -0,0 +1,243 @@
|
||||
import typing as t
|
||||
|
||||
from flask import request, jsonify
|
||||
from flask.views import MethodView
|
||||
from flask_login import current_user
|
||||
from flask_pydantic import validate
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import desc
|
||||
|
||||
from common.utils.http import fail_api, success_api, table_api
|
||||
from extensions import db
|
||||
from models import LogModel
|
||||
from models import UserModel, RoleModel, DepartmentModel
|
||||
|
||||
|
||||
def get_current_user_logs():
|
||||
""" 获取当前用户日志 """
|
||||
log = LogModel.query.filter_by(url='/passport/login').filter_by(uid=current_user.id).order_by(
|
||||
desc(LogModel.create_at)).limit(10)
|
||||
return log
|
||||
|
||||
|
||||
def is_user_exists(username):
|
||||
""" 判断用户是否存在 """
|
||||
res = UserModel.query.filter_by(username=username).count()
|
||||
return bool(res)
|
||||
|
||||
|
||||
def delete_by_id(_id):
|
||||
""" 删除用户 """
|
||||
user = UserModel.query.filter_by(id=_id).first()
|
||||
roles_id = []
|
||||
for role in user.role:
|
||||
roles_id.append(role.id)
|
||||
roles = RoleModel.query.filter(RoleModel.id.in_(roles_id)).all()
|
||||
for r in roles:
|
||||
user.role.remove(r)
|
||||
res = UserModel.query.filter_by(id=_id).delete()
|
||||
db.session.commit()
|
||||
return res
|
||||
|
||||
|
||||
def batch_remove(ids):
|
||||
""" 批量删除 """
|
||||
for _id in ids:
|
||||
delete_by_id(_id)
|
||||
|
||||
|
||||
def update_user_role(_id, roles_list):
|
||||
user = UserModel.query.filter_by(id=_id).first()
|
||||
roles_id = []
|
||||
for role in user.role:
|
||||
roles_id.append(role.id)
|
||||
roles = RoleModel.query.filter(RoleModel.id.in_(roles_id)).all()
|
||||
for r in roles:
|
||||
user.role.remove(r)
|
||||
roles = RoleModel.query.filter(RoleModel.id.in_(roles_list)).all()
|
||||
for r in roles:
|
||||
user.role.append(r)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def users_delete():
|
||||
"""批量删除"""
|
||||
ids = request.form.getlist('ids[]')
|
||||
batch_remove(ids)
|
||||
return success_api(message="批量删除成功")
|
||||
|
||||
|
||||
class QuerySchema(BaseModel):
|
||||
page: int = 1
|
||||
limit: int = 10
|
||||
real_name: t.Optional[str] = Field(alias='realName')
|
||||
username: t.Optional[str]
|
||||
dept_id: t.Optional[str] = Field(alias='deptId', default=0)
|
||||
phone: t.Optional[str]
|
||||
sort: t.Optional[int]
|
||||
status: t.Optional[int]
|
||||
|
||||
|
||||
class PersonSchema(BaseModel):
|
||||
role_ids: str = Field(alias='roleIds')
|
||||
username: str
|
||||
real_name: str = Field(alias='realName')
|
||||
password: str
|
||||
|
||||
|
||||
class UserApi(MethodView):
|
||||
"""修改用户数据"""
|
||||
|
||||
@validate()
|
||||
def get(self, _id, query: QuerySchema):
|
||||
|
||||
filters = []
|
||||
|
||||
if query.real_name:
|
||||
filters.append(UserModel.realname.like('%' + query.real_name + '%'))
|
||||
if query.username:
|
||||
filters.append(UserModel.username.like('%' + query.username + '%'))
|
||||
if query.dept_id:
|
||||
filters.append(UserModel.dept_id == query.dept_id)
|
||||
|
||||
paginate = UserModel.query.filter(
|
||||
*filters).paginate(
|
||||
page=query.page, per_page=query.limit, error_out=False)
|
||||
|
||||
dept_name = lambda _id: DepartmentModel.query.filter_by(id=_id).first().dept_name if _id else ""
|
||||
user_data = [{
|
||||
'id': item.id,
|
||||
'username': item.username,
|
||||
'realname': item.realname,
|
||||
'enable': item.enable,
|
||||
'create_at': str(item.create_at),
|
||||
'update_at': str(item.update_at),
|
||||
'dept': dept_name(item.dept_id),
|
||||
} for item in paginate.items]
|
||||
return table_api(
|
||||
result={
|
||||
'items': user_data,
|
||||
'total': paginate.total,
|
||||
}
|
||||
, code=0
|
||||
)
|
||||
|
||||
@validate()
|
||||
def post(self, body: PersonSchema):
|
||||
"""新建单个用户"""
|
||||
|
||||
role_ids = body.role_ids.split(',')
|
||||
|
||||
if is_user_exists(body.username):
|
||||
return fail_api(message="用户已经存在")
|
||||
|
||||
user = UserModel()
|
||||
user.username = body.username
|
||||
user.realname = body.real_name
|
||||
user.set_password(body.password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
""" 增加用户角色 """
|
||||
user = UserModel.query.filter_by(id=user.id).first()
|
||||
roles = RoleModel.query.filter(RoleModel.id.in_(role_ids)).all()
|
||||
for r in roles:
|
||||
user.role.append(r)
|
||||
db.session.commit()
|
||||
|
||||
return success_api(message="增加成功", code=0)
|
||||
|
||||
def delete(self, _id):
|
||||
# 删除用户
|
||||
res = delete_by_id(_id)
|
||||
if not res:
|
||||
return fail_api(message="删除失败")
|
||||
return success_api(message="删除成功")
|
||||
|
||||
|
||||
class PersonModel2(BaseModel):
|
||||
role_ids: str = Field(alias='roleIds')
|
||||
user_id: str = Field(alias='userId')
|
||||
username: str
|
||||
real_name: str = Field(alias='realName')
|
||||
dept_id: str = Field(alias='deptId')
|
||||
|
||||
|
||||
@validate()
|
||||
def user_role_resource(_id, body: PersonModel2):
|
||||
role_ids = body.role_ids.split(',')
|
||||
|
||||
# 更新用户数据
|
||||
UserModel.query.filter_by(id=_id).update({'username': body.username,
|
||||
'realname': body.real_name,
|
||||
'dept_id': body.dept_id})
|
||||
db.session.commit()
|
||||
|
||||
update_user_role(_id, role_ids)
|
||||
|
||||
return success_api(message="更新成功")
|
||||
|
||||
|
||||
def user_info(_id, action):
|
||||
if action == 'info':
|
||||
real_name = request.json.get('realName', '')
|
||||
username = request.json.get('username', '')
|
||||
remark = request.json.get('remark', '')
|
||||
details = request.json.get('details', '')
|
||||
|
||||
ret = UserModel.query.get(_id)
|
||||
ret.username = username
|
||||
ret.realname = real_name
|
||||
ret.remark = details
|
||||
db.session.commit()
|
||||
if not ret:
|
||||
return fail_api(message="出错啦")
|
||||
return success_api(message="更新成功")
|
||||
elif action == 'status':
|
||||
user_id = int(request.json.get('userId', 0)) # int
|
||||
operate = int(request.json.get('operate', 0)) # int
|
||||
if operate not in [0, 1]:
|
||||
return {'status': 'error', 'message': '请求有误'}
|
||||
|
||||
if operate == 1:
|
||||
user = UserModel.query.get(_id)
|
||||
user.enable = operate
|
||||
message = success_api(message="启动成功")
|
||||
else:
|
||||
user = UserModel.query.filter_by(id=user_id).update({"enable": operate})
|
||||
message = success_api(message="禁用成功")
|
||||
if user:
|
||||
db.session.commit()
|
||||
else:
|
||||
return fail_api(message="出错啦")
|
||||
return message
|
||||
elif action == 'avatar':
|
||||
url = request.json.get("avatar").get("src")
|
||||
ret = UserModel.query.get(_id)
|
||||
ret.avatar = url
|
||||
db.session.commit()
|
||||
if not ret:
|
||||
return fail_api(message="出错啦")
|
||||
return success_api(message="修改成功")
|
||||
elif action == 'password':
|
||||
oldPassword = request.json.get('oldPassword', '')
|
||||
newPassword = request.json.get('newPassword', '')
|
||||
confirmPassword = request.json.get('confirmPassword', '')
|
||||
if not all([oldPassword, newPassword, confirmPassword]):
|
||||
return {'status': 'error', 'message': '密码不能为空'}
|
||||
|
||||
if newPassword != confirmPassword:
|
||||
return fail_api(message='确认密码不一致')
|
||||
|
||||
""" 修改当前用户密码 """
|
||||
user = UserModel.query.get(_id)
|
||||
is_right = user.validate_password(oldPassword)
|
||||
if not is_right:
|
||||
return jsonify(success=False, message="旧密码错误")
|
||||
user.set_password(newPassword)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify(success=True, message="更改成功")
|
||||
else:
|
||||
return jsonify(success=False, message="操作有误")
|
||||
@@ -0,0 +1,20 @@
|
||||
from flask import Flask
|
||||
|
||||
from .index import index_bp
|
||||
from .logs_view import logs_bp
|
||||
from .roles import role_bp
|
||||
from .system_view import system_bp
|
||||
from .document_view import document_bp
|
||||
|
||||
from . import department
|
||||
from . import file
|
||||
from . import rights
|
||||
from . import users
|
||||
|
||||
|
||||
def init_view(app: Flask):
|
||||
app.register_blueprint(index_bp)
|
||||
app.register_blueprint(logs_bp)
|
||||
app.register_blueprint(role_bp)
|
||||
app.register_blueprint(system_bp)
|
||||
app.register_blueprint(document_bp)
|
||||
@@ -0,0 +1,21 @@
|
||||
from flask import render_template, request
|
||||
|
||||
from models import DepartmentModel
|
||||
from common.utils.rights import permission_required, view_logging_required
|
||||
from applications.view import index_bp
|
||||
|
||||
|
||||
@index_bp.get('/dept/add')
|
||||
@view_logging_required
|
||||
@permission_required("admin:dept:add")
|
||||
def add():
|
||||
return render_template('view/system/department_add.html')
|
||||
|
||||
|
||||
@index_bp.get('/dept/edit')
|
||||
@view_logging_required
|
||||
@permission_required("admin:dept:edit")
|
||||
def edit():
|
||||
dept_id = request.args.get("deptId", type=int)
|
||||
dept = DepartmentModel.query.get(dept_id)
|
||||
return render_template('view/system/department_edit.html', dept=dept)
|
||||
@@ -0,0 +1,18 @@
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
document_bp = Blueprint('document', __name__)
|
||||
|
||||
|
||||
@document_bp.route('/component/code/index.html')
|
||||
def component_code():
|
||||
return render_template('component/code/index.html')
|
||||
|
||||
|
||||
@document_bp.route('/view/<document>/<file>')
|
||||
def document_view(document, file):
|
||||
return render_template(f'view/{document}/{file}')
|
||||
|
||||
|
||||
@document_bp.get('/login.html')
|
||||
def login_html():
|
||||
return render_template('login.html')
|
||||
@@ -0,0 +1,18 @@
|
||||
from flask import render_template
|
||||
|
||||
from applications.view import index_bp
|
||||
from common.utils.rights import view_logging_required, permission_required
|
||||
|
||||
|
||||
@index_bp.get('/file')
|
||||
@view_logging_required
|
||||
@permission_required("admin:file:main")
|
||||
def file_index():
|
||||
return render_template('view/system/photo.html')
|
||||
|
||||
|
||||
@index_bp.get('/file/photo/add')
|
||||
@view_logging_required
|
||||
@permission_required("admin:file:main")
|
||||
def file_photo_add():
|
||||
return render_template('view/system/photo_add.html')
|
||||
@@ -0,0 +1,78 @@
|
||||
from flask import Blueprint, redirect, url_for
|
||||
from flask import send_file
|
||||
from flask import session, render_template
|
||||
from flask_login import current_user, logout_user, login_required
|
||||
|
||||
from common.gen_captcha import get_captcha_image
|
||||
|
||||
index_bp = Blueprint('index', __name__)
|
||||
|
||||
|
||||
@index_bp.route('/')
|
||||
@index_bp.route('/admin')
|
||||
@login_required
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@index_bp.route('/config/pear.config.json')
|
||||
def pear_config():
|
||||
return send_file('static/config/pear.config.json')
|
||||
|
||||
|
||||
@index_bp.route('/admin/data/menu.json')
|
||||
def menu():
|
||||
return send_file('static/admin/data/menu.json')
|
||||
|
||||
|
||||
@index_bp.route('/admin/data/message.json')
|
||||
def message():
|
||||
return send_file('static/admin/data/message.json')
|
||||
|
||||
|
||||
@index_bp.route('/view/console/console1.html')
|
||||
def console1():
|
||||
# 控制后台
|
||||
return render_template('view/console/console1.html')
|
||||
|
||||
|
||||
@index_bp.route('/view/console/console2.html')
|
||||
def console2():
|
||||
# 数据分析
|
||||
return render_template('view/console/console2.html')
|
||||
|
||||
|
||||
@index_bp.route('/view/system/theme.html')
|
||||
def theme():
|
||||
# 酸爽翻倍
|
||||
return render_template('view/system/theme.html')
|
||||
|
||||
|
||||
@index_bp.route('/view/document/core.html')
|
||||
def core():
|
||||
# 酸爽翻倍
|
||||
return render_template('view/document/core.html')
|
||||
|
||||
|
||||
@index_bp.get('/passport/getCaptcha')
|
||||
def get_captcha():
|
||||
resp, code = get_captcha_image()
|
||||
session["code"] = code
|
||||
return resp
|
||||
|
||||
|
||||
# 退出登录
|
||||
@index_bp.post('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
session.pop('permissions')
|
||||
print({"message": "注销成功", "success": True})
|
||||
return {"message": "注销成功", "success": True}
|
||||
|
||||
|
||||
@index_bp.get('/login')
|
||||
def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('admin.index'))
|
||||
return render_template('login.html')
|
||||
@@ -0,0 +1,63 @@
|
||||
from flask import Blueprint, request, render_template
|
||||
from sqlalchemy import desc
|
||||
|
||||
from common.utils.http import table_api
|
||||
from common.utils.rights import permission_required
|
||||
from models import LogModel
|
||||
|
||||
logs_bp = Blueprint('logs', __name__, url_prefix='/logs')
|
||||
|
||||
|
||||
@logs_bp.get('/login_log')
|
||||
@permission_required("admin:log:main")
|
||||
def login_log():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
log_paginate = LogModel.query.filter_by(
|
||||
url='/api/v1/passport/login').order_by(
|
||||
desc(LogModel.create_at)).paginate(
|
||||
page=page, per_page=limit, error_out=False)
|
||||
data = [
|
||||
{
|
||||
'id': item.id,
|
||||
'method': item.method,
|
||||
'uid': item.uid,
|
||||
'url': item.url,
|
||||
'desc': item.desc,
|
||||
'ip': item.ip,
|
||||
'success': item.success,
|
||||
'user_agent': item.user_agent,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
} for item in log_paginate.items
|
||||
]
|
||||
|
||||
return table_api(result={'items': data,
|
||||
'total': log_paginate.total, },
|
||||
code=0)
|
||||
|
||||
|
||||
@logs_bp.get('/access_log')
|
||||
@permission_required("admin:log:main")
|
||||
def operate_log():
|
||||
page = request.args.get('page', type=int)
|
||||
limit = request.args.get('limit', type=int)
|
||||
log_paginate = LogModel.query.filter(
|
||||
LogModel.url != '/api/v1/passport/login').order_by(
|
||||
desc(LogModel.create_at)).paginate(
|
||||
page=page, per_page=limit, error_out=False)
|
||||
data = [
|
||||
{
|
||||
'id': item.id,
|
||||
'method': item.method,
|
||||
'uid': item.uid,
|
||||
'url': item.url,
|
||||
'desc': item.desc,
|
||||
'ip': item.ip,
|
||||
'success': item.success,
|
||||
'user_agent': item.user_agent,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
} for item in log_paginate.items
|
||||
]
|
||||
return table_api(result={'items': data,
|
||||
'total': log_paginate.total, },
|
||||
code=0)
|
||||
@@ -0,0 +1,25 @@
|
||||
from flask import render_template
|
||||
|
||||
from common.utils.rights import permission_required, view_logging_required
|
||||
from models import RightModel
|
||||
from applications.view import index_bp
|
||||
|
||||
|
||||
@index_bp.get('/rights/power/<int:power_id>')
|
||||
@view_logging_required
|
||||
@permission_required("admin:power:edit")
|
||||
def rights_edit(power_id):
|
||||
power = RightModel.query.filter_by(id=power_id).first()
|
||||
icon = str(power.icon).split()
|
||||
if len(icon) == 2:
|
||||
icon = icon[1]
|
||||
else:
|
||||
icon = None
|
||||
return render_template('view/system/power_edit.html', power=power, icon=icon)
|
||||
|
||||
|
||||
@index_bp.get('/rights/add')
|
||||
@view_logging_required
|
||||
@permission_required("admin:power:main")
|
||||
def rights_add():
|
||||
return render_template('view/system/power_add.html')
|
||||
@@ -0,0 +1,31 @@
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
from common.utils.rights import permission_required, view_logging_required
|
||||
|
||||
from models import RoleModel
|
||||
|
||||
role_bp = Blueprint('role', __name__, url_prefix='/admin/role')
|
||||
|
||||
|
||||
# 角色授权操作
|
||||
@role_bp.get('/power/<int:role_id>')
|
||||
@view_logging_required
|
||||
@permission_required("admin:role:power")
|
||||
def power(role_id):
|
||||
return render_template('view/system/roles_power.html', role_id=role_id)
|
||||
|
||||
|
||||
# 角色编辑
|
||||
@role_bp.get('/edit/<int:role_id>')
|
||||
@view_logging_required
|
||||
@permission_required("admin:role:edit")
|
||||
def role_editor(role_id):
|
||||
role = RoleModel.query.filter_by(id=role_id).first()
|
||||
return render_template('view/system/roles_edit.html', role=role)
|
||||
|
||||
|
||||
@role_bp.get('/add')
|
||||
@view_logging_required
|
||||
@permission_required("admin:role:edit")
|
||||
def role_add():
|
||||
return render_template('view/system/roles_add.html')
|
||||
@@ -0,0 +1,48 @@
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
system_bp = Blueprint('system', __name__)
|
||||
|
||||
|
||||
@system_bp.route('/view/system/user.html')
|
||||
def system_user():
|
||||
return render_template('view/system/user.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/role.html')
|
||||
def system_role():
|
||||
return render_template('view/system/role.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/power.html')
|
||||
def system_power():
|
||||
return render_template('view/system/power.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/department.html')
|
||||
def system_department():
|
||||
return render_template('view/system/department.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/log.html')
|
||||
def system_log():
|
||||
return render_template('view/system/log.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/dict.html')
|
||||
def system_dict():
|
||||
return render_template('view/system/dict.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/operate/add.html')
|
||||
def system_operate_add():
|
||||
return render_template('view/system/operate/add.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/operate/edit.html')
|
||||
def system_operate_edit():
|
||||
return render_template('view/system/operate/edit.html')
|
||||
|
||||
|
||||
@system_bp.route('/view/system/operate/profile.html')
|
||||
def system_operate_profile():
|
||||
return render_template('view/system/operate/profile.html')
|
||||
@@ -0,0 +1,26 @@
|
||||
from flask import render_template
|
||||
|
||||
from common.utils.rights import permission_required, view_logging_required
|
||||
from models import RoleModel, UserModel
|
||||
from . import index_bp
|
||||
|
||||
|
||||
@index_bp.get('/users/add')
|
||||
@view_logging_required
|
||||
@permission_required("admin:user:add")
|
||||
def users_add_view():
|
||||
roles = RoleModel.query.all()
|
||||
return render_template('view/system/user_add.html', roles=roles)
|
||||
|
||||
|
||||
@index_bp.get('/users/<user_id>')
|
||||
@view_logging_required
|
||||
@permission_required("admin:user:edit")
|
||||
def users_user_id_view(user_id):
|
||||
# 获取编辑用户信息
|
||||
user = UserModel.query.filter_by(id=user_id).first()
|
||||
roles = RoleModel.query.all()
|
||||
checked_roles = []
|
||||
for r in user.role:
|
||||
checked_roles.append(r.id)
|
||||
return render_template('view/system/user_edit.html', user=user, roles=roles, checked_roles=checked_roles)
|
||||
@@ -0,0 +1,5 @@
|
||||
def register_api(view, endpoint, url, pk='id', pk_type='int', app=None):
|
||||
view_func = view.as_view(endpoint)
|
||||
app.add_url_rule(url, defaults={pk: None}, view_func=view_func, methods=['GET', ])
|
||||
app.add_url_rule(url, view_func=view_func, methods=['POST', ])
|
||||
app.add_url_rule(f'{url}<{pk_type}:{pk}>', view_func=view_func, methods=['GET', 'PUT', 'DELETE'])
|
||||
@@ -0,0 +1,504 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
为了解决 flask_uploads 与 Werkzeug 2.0.1 冲突而直接复制源码进行适配
|
||||
原始地址: https://github.com/maxcountryman/flask-uploads/blob/master/flask_uploads.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
else:
|
||||
string_types = basestring,
|
||||
|
||||
import os.path
|
||||
import posixpath
|
||||
|
||||
from flask import current_app, send_from_directory, abort, url_for
|
||||
from itertools import chain
|
||||
from werkzeug.datastructures import FileStorage
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
# Extension presets
|
||||
|
||||
#: This just contains plain text files (.txt).
|
||||
TEXT = ('txt',)
|
||||
|
||||
#: This contains various office document formats (.rtf, .odf, .ods, .gnumeric,
|
||||
#: .abw, .doc, .docx, .xls, and .xlsx). Note that the macro-enabled versions
|
||||
#: of Microsoft Office 2007 files are not included.
|
||||
DOCUMENTS = tuple('rtf odf ods gnumeric abw doc docx xls xlsx'.split())
|
||||
|
||||
#: This contains basic image types that are viewable from most browsers (.jpg,
|
||||
#: .jpe, .jpeg, .png, .gif, .svg, and .bmp).
|
||||
IMAGES = tuple('jpg jpe jpeg png gif svg bmp'.split())
|
||||
|
||||
#: This contains audio file types (.wav, .mp3, .aac, .ogg, .oga, and .flac).
|
||||
AUDIO = tuple('wav mp3 aac ogg oga flac'.split())
|
||||
|
||||
#: This is for structured data files (.csv, .ini, .json, .plist, .xml, .yaml,
|
||||
#: and .yml).
|
||||
DATA = tuple('csv ini json plist xml yaml yml'.split())
|
||||
|
||||
#: This contains various types of scripts (.js, .php, .pl, .py .rb, and .sh).
|
||||
#: If your Web server has PHP installed and set to auto-run, you might want to
|
||||
#: add ``php`` to the DENY setting.
|
||||
SCRIPTS = tuple('js php pl py rb sh'.split())
|
||||
|
||||
#: This contains archive and compression formats (.gz, .bz2, .zip, .tar,
|
||||
#: .tgz, .txz, and .7z).
|
||||
ARCHIVES = tuple('gz bz2 zip tar tgz txz 7z'.split())
|
||||
|
||||
#: This contains shared libraries and executable files (.so, .exe and .dll).
|
||||
#: Most of the time, you will not want to allow this - it's better suited for
|
||||
#: use with `AllExcept`.
|
||||
EXECUTABLES = tuple('so exe dll'.split())
|
||||
|
||||
#: The default allowed extensions - `TEXT`, `DOCUMENTS`, `DATA`, and `IMAGES`.
|
||||
DEFAULTS = TEXT + DOCUMENTS + IMAGES + DATA
|
||||
|
||||
|
||||
class UploadNotAllowed(Exception):
|
||||
"""
|
||||
This exception is raised if the upload was not allowed. You should catch
|
||||
it in your view code and display an appropriate message to the user.
|
||||
"""
|
||||
|
||||
|
||||
def tuple_from(*iters):
|
||||
return tuple(itertools.chain(*iters))
|
||||
|
||||
|
||||
def extension(filename):
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.startswith('.'):
|
||||
# os.path.splitext retains . separator
|
||||
ext = ext[1:]
|
||||
return ext
|
||||
|
||||
|
||||
def lowercase_ext(filename):
|
||||
"""
|
||||
This is a helper used by UploadSet.save to provide lowercase extensions for
|
||||
all processed files, to compare with configured extensions in the same
|
||||
case.
|
||||
|
||||
.. versionchanged:: 0.1.4
|
||||
Filenames without extensions are no longer lowercased, only the
|
||||
extension is returned in lowercase, if an extension exists.
|
||||
|
||||
:param filename: The filename to ensure has a lowercase extension.
|
||||
"""
|
||||
if '.' in filename:
|
||||
main, ext = os.path.splitext(filename)
|
||||
return main + ext.lower()
|
||||
# For consistency with os.path.splitext,
|
||||
# do not treat a filename without an extension as an extension.
|
||||
# That is, do not return filename.lower().
|
||||
return filename
|
||||
|
||||
|
||||
def addslash(url):
|
||||
if url.endswith('/'):
|
||||
return url
|
||||
return url + '/'
|
||||
|
||||
|
||||
def patch_request_class(app, size=64 * 1024 * 1024):
|
||||
"""
|
||||
By default, Flask will accept uploads to an arbitrary size. While Werkzeug
|
||||
switches uploads from memory to a temporary file when they hit 500 KiB,
|
||||
it's still possible for someone to overload your disk space with a
|
||||
gigantic file.
|
||||
|
||||
This patches the app's request class's
|
||||
`~werkzeug.BaseRequest.max_content_length` attribute so that any upload
|
||||
larger than the given size is rejected with an HTTP error.
|
||||
|
||||
.. note::
|
||||
|
||||
In Flask 0.6, you can do this by setting the `MAX_CONTENT_LENGTH`
|
||||
setting, without patching the request class. To emulate this behavior,
|
||||
you can pass `None` as the size (you must pass it explicitly). That is
|
||||
the best way to call this function, as it won't break the Flask 0.6
|
||||
functionality if it exists.
|
||||
|
||||
.. versionchanged:: 0.1.1
|
||||
|
||||
:param app: The app to patch the request class of.
|
||||
:param size: The maximum size to accept, in bytes. The default is 64 MiB.
|
||||
If it is `None`, the app's `MAX_CONTENT_LENGTH` configuration
|
||||
setting will be used to patch.
|
||||
"""
|
||||
if size is None:
|
||||
if isinstance(app.request_class.__dict__['max_content_length'],
|
||||
property):
|
||||
return
|
||||
size = app.config.get('MAX_CONTENT_LENGTH')
|
||||
reqclass = app.request_class
|
||||
patched = type(reqclass.__name__, (reqclass,),
|
||||
{'max_content_length': size})
|
||||
app.request_class = patched
|
||||
|
||||
|
||||
def config_for_set(uset, app, defaults=None):
|
||||
"""
|
||||
This is a helper function for `configure_uploads` that extracts the
|
||||
configuration for a single set.
|
||||
|
||||
:param uset: The upload set.
|
||||
:param app: The app to load the configuration from.
|
||||
:param defaults: A dict with keys `url` and `dest` from the
|
||||
`UPLOADS_DEFAULT_DEST` and `DEFAULT_UPLOADS_URL`
|
||||
settings.
|
||||
"""
|
||||
config = app.config
|
||||
prefix = 'UPLOADED_%s_' % uset.name.upper()
|
||||
using_defaults = False
|
||||
if defaults is None:
|
||||
defaults = dict(dest=None, url=None)
|
||||
|
||||
allow_extns = tuple(config.get(prefix + 'ALLOW', ()))
|
||||
deny_extns = tuple(config.get(prefix + 'DENY', ()))
|
||||
destination = config.get(prefix + 'DEST')
|
||||
base_url = config.get(prefix + 'URL')
|
||||
|
||||
if destination is None:
|
||||
# the upload set's destination wasn't given
|
||||
if uset.default_dest:
|
||||
# use the "default_dest" callable
|
||||
destination = uset.default_dest(app)
|
||||
if destination is None: # still
|
||||
# use the default dest from the config
|
||||
if defaults['dest'] is not None:
|
||||
using_defaults = True
|
||||
destination = os.path.join(defaults['dest'], uset.name)
|
||||
else:
|
||||
raise RuntimeError("no destination for set %s" % uset.name)
|
||||
|
||||
if base_url is None and using_defaults and defaults['url']:
|
||||
base_url = addslash(defaults['url']) + uset.name + '/'
|
||||
|
||||
return UploadConfiguration(destination, base_url, allow_extns, deny_extns)
|
||||
|
||||
|
||||
def configure_uploads(app, upload_sets):
|
||||
"""
|
||||
Call this after the app has been configured. It will go through all the
|
||||
upload sets, get their configuration, and store the configuration on the
|
||||
app. It will also register the uploads module if it hasn't been set. This
|
||||
can be called multiple times with different upload sets.
|
||||
|
||||
.. versionchanged:: 0.1.3
|
||||
The uploads module/blueprint will only be registered if it is needed
|
||||
to serve the upload sets.
|
||||
|
||||
:param app: The `~flask.Flask` instance to get the configuration from.
|
||||
:param upload_sets: The `UploadSet` instances to configure.
|
||||
"""
|
||||
if isinstance(upload_sets, UploadSet):
|
||||
upload_sets = (upload_sets,)
|
||||
|
||||
if not hasattr(app, 'upload_set_config'):
|
||||
app.upload_set_config = {}
|
||||
set_config = app.upload_set_config
|
||||
defaults = dict(dest=app.config.get('UPLOADS_DEFAULT_DEST'),
|
||||
url=app.config.get('UPLOADS_DEFAULT_URL'))
|
||||
|
||||
for uset in upload_sets:
|
||||
config = config_for_set(uset, app, defaults)
|
||||
set_config[uset.name] = config
|
||||
|
||||
should_serve = any(s.base_url is None for s in set_config.values())
|
||||
if '_uploads' not in app.blueprints and should_serve:
|
||||
app.register_blueprint(uploads_mod)
|
||||
|
||||
|
||||
class All(object):
|
||||
"""
|
||||
This type can be used to allow all extensions. There is a predefined
|
||||
instance named `ALL`.
|
||||
"""
|
||||
|
||||
def __contains__(self, item):
|
||||
return True
|
||||
|
||||
|
||||
#: This "contains" all items. You can use it to allow all extensions to be
|
||||
#: uploaded.
|
||||
ALL = All()
|
||||
|
||||
|
||||
class AllExcept(object):
|
||||
"""
|
||||
This can be used to allow all file types except certain ones. For example,
|
||||
to ban .exe and .iso files, pass::
|
||||
|
||||
AllExcept(('exe', 'iso'))
|
||||
|
||||
to the `UploadSet` constructor as `extensions`. You can use any container,
|
||||
for example::
|
||||
|
||||
AllExcept(SCRIPTS + EXECUTABLES)
|
||||
"""
|
||||
|
||||
def __init__(self, items):
|
||||
self.items = items
|
||||
|
||||
def __contains__(self, item):
|
||||
return item not in self.items
|
||||
|
||||
|
||||
class UploadConfiguration(object):
|
||||
"""
|
||||
This holds the configuration for a single `UploadSet`. The constructor's
|
||||
arguments are also the attributes.
|
||||
|
||||
:param destination: The directory to save files to.
|
||||
:param base_url: The URL (ending with a /) that files can be downloaded
|
||||
from. If this is `None`, Flask-Uploads will serve the
|
||||
files itself.
|
||||
:param allow: A list of extensions to allow, even if they're not in the
|
||||
`UploadSet` extensions list.
|
||||
:param deny: A list of extensions to deny, even if they are in the
|
||||
`UploadSet` extensions list.
|
||||
"""
|
||||
|
||||
def __init__(self, destination, base_url=None, allow=(), deny=()):
|
||||
self.destination = destination
|
||||
self.base_url = base_url
|
||||
self.allow = allow
|
||||
self.deny = deny
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
return (self.destination, self.base_url, self.allow, self.deny)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.tuple == other.tuple
|
||||
|
||||
|
||||
class UploadSet(object):
|
||||
"""
|
||||
This represents a single set of uploaded files. Each upload set is
|
||||
independent of the others. This can be reused across multiple application
|
||||
instances, as all configuration is stored on the application object itself
|
||||
and found with `flask.current_app`.
|
||||
|
||||
:param name: The name of this upload set. It defaults to ``files``, but
|
||||
you can pick any alphanumeric name you want. (For simplicity,
|
||||
it's best to use a plural noun.)
|
||||
:param extensions: The extensions to allow uploading in this set. The
|
||||
easiest way to do this is to add together the extension
|
||||
presets (for example, ``TEXT + DOCUMENTS + IMAGES``).
|
||||
It can be overridden by the configuration with the
|
||||
`UPLOADED_X_ALLOW` and `UPLOADED_X_DENY` configuration
|
||||
parameters. The default is `DEFAULTS`.
|
||||
:param default_dest: If given, this should be a callable. If you call it
|
||||
with the app, it should return the default upload
|
||||
destination path for that app.
|
||||
"""
|
||||
|
||||
def __init__(self, name='files', extensions=DEFAULTS, default_dest=None):
|
||||
if not name.isalnum():
|
||||
raise ValueError("Name must be alphanumeric (no underscores)")
|
||||
self.name = name
|
||||
self.extensions = extensions
|
||||
self._config = None
|
||||
self.default_dest = default_dest
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
"""
|
||||
This gets the current configuration. By default, it looks up the
|
||||
current application and gets the configuration from there. But if you
|
||||
don't want to go to the full effort of setting an application, or it's
|
||||
otherwise outside of a request context, set the `_config` attribute to
|
||||
an `UploadConfiguration` instance, then set it back to `None` when
|
||||
you're done.
|
||||
"""
|
||||
if self._config is not None:
|
||||
return self._config
|
||||
try:
|
||||
return current_app.upload_set_config[self.name]
|
||||
except AttributeError:
|
||||
raise RuntimeError("cannot access configuration outside request")
|
||||
|
||||
def url(self, filename):
|
||||
"""
|
||||
This function gets the URL a file uploaded to this set would be
|
||||
accessed at. It doesn't check whether said file exists.
|
||||
|
||||
:param filename: The filename to return the URL for.
|
||||
"""
|
||||
base = self.config.base_url
|
||||
if base is None:
|
||||
return url_for('_uploads.uploaded_file', setname=self.name,
|
||||
filename=filename, _external=True)
|
||||
else:
|
||||
return base + filename
|
||||
|
||||
def path(self, filename, folder=None):
|
||||
"""
|
||||
This returns the absolute path of a file uploaded to this set. It
|
||||
doesn't actually check whether said file exists.
|
||||
|
||||
:param filename: The filename to return the path for.
|
||||
:param folder: The subfolder within the upload set previously used
|
||||
to save to.
|
||||
"""
|
||||
if folder is not None:
|
||||
target_folder = os.path.join(self.config.destination, folder)
|
||||
else:
|
||||
target_folder = self.config.destination
|
||||
return os.path.join(target_folder, filename)
|
||||
|
||||
def file_allowed(self, storage, basename):
|
||||
"""
|
||||
This tells whether a file is allowed. It should return `True` if the
|
||||
given `werkzeug.FileStorage` object can be saved with the given
|
||||
basename, and `False` if it can't. The default implementation just
|
||||
checks the extension, so you can override this if you want.
|
||||
|
||||
:param storage: The `werkzeug.FileStorage` to check.
|
||||
:param basename: The basename it will be saved under.
|
||||
"""
|
||||
return self.extension_allowed(extension(basename))
|
||||
|
||||
def extension_allowed(self, ext):
|
||||
"""
|
||||
This determines whether a specific extension is allowed. It is called
|
||||
by `file_allowed`, so if you override that but still want to check
|
||||
extensions, call back into this.
|
||||
|
||||
:param ext: The extension to check, without the dot.
|
||||
"""
|
||||
return ((ext in self.config.allow) or
|
||||
(ext in self.extensions and ext not in self.config.deny))
|
||||
|
||||
def get_basename(self, filename):
|
||||
return lowercase_ext(secure_filename(filename))
|
||||
|
||||
def save(self, storage, folder=None, name=None):
|
||||
"""
|
||||
This saves a `werkzeug.FileStorage` into this upload set. If the
|
||||
upload is not allowed, an `UploadNotAllowed` error will be raised.
|
||||
Otherwise, the file will be saved and its name (including the folder)
|
||||
will be returned.
|
||||
|
||||
:param storage: The uploaded file to save.
|
||||
:param folder: The subfolder within the upload set to save to.
|
||||
:param name: The name to save the file as. If it ends with a dot, the
|
||||
file's extension will be appended to the end. (If you
|
||||
are using `name`, you can include the folder in the
|
||||
`name` instead of explicitly using `folder`, i.e.
|
||||
``uset.save(file, name="someguy/photo_123.")``
|
||||
"""
|
||||
if not isinstance(storage, FileStorage):
|
||||
raise TypeError("storage must be a werkzeug.FileStorage")
|
||||
|
||||
if folder is None and name is not None and "/" in name:
|
||||
folder, name = os.path.split(name)
|
||||
|
||||
basename = self.get_basename(storage.filename)
|
||||
if name:
|
||||
if name.endswith('.'):
|
||||
basename = name + extension(basename)
|
||||
else:
|
||||
basename = name
|
||||
|
||||
if not self.file_allowed(storage, basename):
|
||||
raise UploadNotAllowed()
|
||||
|
||||
if folder:
|
||||
target_folder = os.path.join(self.config.destination, folder)
|
||||
else:
|
||||
target_folder = self.config.destination
|
||||
if not os.path.exists(target_folder):
|
||||
os.makedirs(target_folder)
|
||||
if os.path.exists(os.path.join(target_folder, basename)):
|
||||
basename = self.resolve_conflict(target_folder, basename)
|
||||
|
||||
target = os.path.join(target_folder, basename)
|
||||
storage.save(target)
|
||||
if folder:
|
||||
return posixpath.join(folder, basename)
|
||||
else:
|
||||
return basename
|
||||
|
||||
def resolve_conflict(self, target_folder, basename):
|
||||
"""
|
||||
If a file with the selected name already exists in the target folder,
|
||||
this method is called to resolve the conflict. It should return a new
|
||||
basename for the file.
|
||||
|
||||
The default implementation splits the name and extension and adds a
|
||||
suffix to the name consisting of an underscore and a number, and tries
|
||||
that until it finds one that doesn't exist.
|
||||
|
||||
:param target_folder: The absolute path to the target.
|
||||
:param basename: The file's original basename.
|
||||
"""
|
||||
name, ext = os.path.splitext(basename)
|
||||
count = 0
|
||||
while True:
|
||||
count = count + 1
|
||||
newname = '%s_%d%s' % (name, count, ext)
|
||||
if not os.path.exists(os.path.join(target_folder, newname)):
|
||||
return newname
|
||||
|
||||
|
||||
uploads_mod = Blueprint('_uploads', __name__, url_prefix='/_uploads')
|
||||
|
||||
|
||||
@uploads_mod.route('/<setname>/<path:filename>')
|
||||
def uploaded_file(setname, filename):
|
||||
config = current_app.upload_set_config.get(setname)
|
||||
if config is None:
|
||||
abort(404)
|
||||
return send_from_directory(config.destination, filename)
|
||||
|
||||
|
||||
class TestingFileStorage(FileStorage):
|
||||
"""
|
||||
This is a helper for testing upload behavior in your application. You
|
||||
can manually create it, and its save method is overloaded to set `saved`
|
||||
to the name of the file it was saved to. All of these parameters are
|
||||
optional, so only bother setting the ones relevant to your application.
|
||||
|
||||
:param stream: A stream. The default is an empty stream.
|
||||
:param filename: The filename uploaded from the client. The default is the
|
||||
stream's name.
|
||||
:param name: The name of the form field it was loaded from. The default is
|
||||
`None`.
|
||||
:param content_type: The content type it was uploaded as. The default is
|
||||
``application/octet-stream``.
|
||||
:param content_length: How long it is. The default is -1.
|
||||
:param headers: Multipart headers as a `werkzeug.Headers`. The default is
|
||||
`None`.
|
||||
"""
|
||||
|
||||
def __init__(self, stream=None, filename=None, name=None,
|
||||
content_type='application/octet-stream', content_length=-1,
|
||||
headers=None):
|
||||
FileStorage.__init__(self, stream, filename, name=name,
|
||||
content_type=content_type, content_length=content_length,
|
||||
headers=None)
|
||||
self.saved = None
|
||||
|
||||
def save(self, dst, buffer_size=16384):
|
||||
"""
|
||||
This marks the file as saved by setting the `saved` attribute to the
|
||||
name of the file it was saved to.
|
||||
|
||||
:param dst: The file to save to.
|
||||
:param buffer_size: Ignored.
|
||||
"""
|
||||
if isinstance(dst, string_types):
|
||||
self.saved = dst
|
||||
else:
|
||||
self.saved = dst.name
|
||||
@@ -0,0 +1,43 @@
|
||||
from captcha.image import ImageCaptcha
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
from random import choices
|
||||
|
||||
from flask import session, make_response
|
||||
from flask_login import current_user
|
||||
|
||||
|
||||
def gen_captcha(content='0123456789'):
|
||||
""" 生成验证码 """
|
||||
image = ImageCaptcha()
|
||||
# 获取字符串
|
||||
captcha_text = "".join(choices(content, k=4))
|
||||
# 生成图像
|
||||
captcha_image = Image.open(image.generate(captcha_text))
|
||||
return captcha_text, captcha_image
|
||||
|
||||
|
||||
# 生成验证码
|
||||
def get_captcha_image():
|
||||
code, image = gen_captcha()
|
||||
out = BytesIO()
|
||||
session["code"] = code
|
||||
image.save(out, 'png')
|
||||
out.seek(0)
|
||||
resp = make_response(out.read())
|
||||
resp.content_type = 'image/png'
|
||||
return resp, code
|
||||
|
||||
|
||||
# 授权路由存入session
|
||||
def add_auth_session():
|
||||
role = current_user.role
|
||||
user_power = []
|
||||
for i in role:
|
||||
if i.enable == 0:
|
||||
continue
|
||||
for p in i.power:
|
||||
if p.enable == 0:
|
||||
continue
|
||||
user_power.append(p.code)
|
||||
session['permissions'] = user_power
|
||||
@@ -0,0 +1,35 @@
|
||||
import typing as t
|
||||
|
||||
|
||||
def success_api(message: str = "成功", code: int = 200) -> t.Dict:
|
||||
""" 成功响应 默认值”成功“ """
|
||||
return {'success': True, 'message': message, 'code': code}
|
||||
|
||||
|
||||
def fail_api(message: str = "失败", code: int = 404) -> t.Dict:
|
||||
""" 失败响应 默认值“失败” """
|
||||
return {'success': False, 'message': message, 'code': code}
|
||||
|
||||
|
||||
def table_api(success: bool = True,
|
||||
message: str = "",
|
||||
result: t.Union[dict, list] = None,
|
||||
code: int = 0) -> t.Dict:
|
||||
"""
|
||||
动态表格渲染响应
|
||||
此方法返回数据给前端
|
||||
{
|
||||
'success': True,
|
||||
'code': 10002,
|
||||
'message': '提示消息',
|
||||
'result':{'items':[],'total': 100}
|
||||
}
|
||||
|
||||
注:lay_ui 表格数据需要指定 code=0
|
||||
"""
|
||||
return {
|
||||
'success': success,
|
||||
'message': message,
|
||||
'code': code,
|
||||
'result': result,
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import typing as t
|
||||
from functools import wraps
|
||||
|
||||
from flask import abort, request, jsonify, session
|
||||
from flask_login import login_required
|
||||
from flask_login import current_user
|
||||
|
||||
from extensions import db
|
||||
from models import LogModel
|
||||
|
||||
|
||||
def record_logging(success: bool = True) -> None:
|
||||
"""
|
||||
记录用户日志数据
|
||||
"""
|
||||
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': success
|
||||
}
|
||||
log = LogModel()
|
||||
for key, value in info.items():
|
||||
setattr(log, key, value)
|
||||
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def view_logging_required(func: t.Callable) -> t.Callable:
|
||||
"""
|
||||
日志装饰器,用于记录请求
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs) -> t.Callable:
|
||||
record_logging()
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def permission_required(permission: str) -> t.Callable:
|
||||
"""
|
||||
权限装饰器,用于过滤需要的权限
|
||||
"""
|
||||
|
||||
def decorator(func: t.Callable):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs) -> t.Callable:
|
||||
if permission not in session.get('permissions'):
|
||||
record_logging(success=False)
|
||||
abort(403)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,30 @@
|
||||
import os
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from common.flask_uploads import UploadSet, IMAGES
|
||||
from extensions import db
|
||||
from models import PhotoModel
|
||||
|
||||
photos = UploadSet('photos', IMAGES)
|
||||
|
||||
|
||||
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 = PhotoModel(name=filename, href=file_url, mime=mime, size=size)
|
||||
db.session.add(photo)
|
||||
db.session.commit()
|
||||
return file_url
|
||||
|
||||
|
||||
def delete_photo_by_id(_id):
|
||||
photo_name = PhotoModel.query.filter_by(id=_id).first().name
|
||||
photo = PhotoModel.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,50 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
# 主题面板的链接列表配置
|
||||
SYSTEM_NAME = "Pear Admin"
|
||||
SYSTEM_PANEL_LINKS = [
|
||||
{
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"title": "官方网站",
|
||||
"href": "http://www.pearadmin.com"
|
||||
},
|
||||
{
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"title": "开发文档",
|
||||
"href": "http://www.pearadmin.com"
|
||||
},
|
||||
{
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"title": "开源地址",
|
||||
"href": "https://gitee.com/Jmysy/Pear-Admin-Layui"
|
||||
}
|
||||
]
|
||||
|
||||
SECRET_KEY = os.getenv('SECRET_KEY', 'dev key')
|
||||
|
||||
# mysql 配置
|
||||
MYSQL_USERNAME = "windows"
|
||||
MYSQL_PASSWORD = "123456"
|
||||
MYSQL_HOST = "127.0.0.1"
|
||||
MYSQL_PORT = 3306
|
||||
MYSQL_DATABASE = "PearAdminFlask"
|
||||
|
||||
# redis 配置
|
||||
REDIS_HOST = "127.0.0.1"
|
||||
REDIS_PORT = 6379
|
||||
|
||||
""" Sqlalchemy 配置 """
|
||||
SQLALCHEMY_DATABASE_URI = r'sqlite:///pear_admin.db'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
SQLALCHEMY_ECHO = False
|
||||
SQLALCHEMY_POOL_RECYCLE = 8
|
||||
# SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}"
|
||||
|
||||
LOG_LEVEL = logging.ERROR
|
||||
|
||||
# 图片文件存放位置
|
||||
UPLOADED_PHOTOS_DEST = os.path.join(os.path.dirname(os.path.abspath(__name__)), 'static', 'upload')
|
||||
UPLOADED_FILES_ALLOW = ['gif', 'jpg']
|
||||
# JSON配置
|
||||
JSON_AS_ASCII = False
|
||||
-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}
|
||||
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,17 @@
|
||||
from flask import Flask
|
||||
|
||||
from .init_databases import register_script
|
||||
from .init_sqlalchemy import db, init_databases
|
||||
from .init_login import init_login_manager
|
||||
from .init_template_directives import init_template_directives
|
||||
from .init_error_views import init_error_views
|
||||
|
||||
|
||||
def init_plugs(app: Flask) -> None:
|
||||
init_login_manager(app)
|
||||
init_databases(app)
|
||||
init_template_directives(app)
|
||||
init_error_views(app)
|
||||
|
||||
# 生成测试数据的命令,生成环境可以注释
|
||||
register_script(app)
|
||||
@@ -0,0 +1,82 @@
|
||||
from flask import Flask
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
date_str = re.compile('\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$')
|
||||
|
||||
|
||||
def add_data(data_list, obj):
|
||||
from extensions import db
|
||||
|
||||
for _data in data_list:
|
||||
dept = obj()
|
||||
for key, value in _data.items():
|
||||
|
||||
if isinstance(value, str) and date_str.match(value):
|
||||
value = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
setattr(dept, key, value)
|
||||
db.session.add(dept)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def register_script(app: Flask):
|
||||
@app.cli.command()
|
||||
def init_db():
|
||||
import json
|
||||
import os
|
||||
|
||||
path = os.path.dirname(os.path.abspath(__name__))
|
||||
path = os.path.join(path, 'static', 'data')
|
||||
cp_dept_data_list = json.loads(open(os.path.join(path, 'cp_dept.json'), encoding='utf-8').read())
|
||||
cp_user_data_list = json.loads(open(os.path.join(path, 'cp_user.json'), encoding='utf-8').read())
|
||||
file_photo_data_list = json.loads(open(os.path.join(path, 'file_photo.json'), encoding='utf-8').read())
|
||||
rt_power_data_list = json.loads(open(os.path.join(path, 'rt_power.json'), encoding='utf-8').read())
|
||||
rt_role_data_list = json.loads(open(os.path.join(path, 'rt_role.json'), encoding='utf-8').read())
|
||||
rt_role_power_data_list = json.loads(open(os.path.join(path, 'rt_role_power.json'), encoding='utf-8').read())
|
||||
rt_user_role_data_list = json.loads(open(os.path.join(path, 'rt_user_role.json'), encoding='utf-8').read())
|
||||
|
||||
"""数据库初始化"""
|
||||
|
||||
# 创建化部门数据
|
||||
from models import DepartmentModel
|
||||
add_data(cp_dept_data_list, DepartmentModel)
|
||||
|
||||
# 图片数据
|
||||
from models import PhotoModel
|
||||
|
||||
add_data(file_photo_data_list, PhotoModel)
|
||||
|
||||
# 初始化权限表数据
|
||||
from models import RightModel
|
||||
|
||||
add_data(rt_power_data_list, RightModel)
|
||||
# 初始化角色表
|
||||
from models import RoleModel
|
||||
|
||||
# 角色权限关系表
|
||||
from extensions import db
|
||||
for data in rt_role_power_data_list:
|
||||
db.session.execute('insert into rt_role_power VALUES (%s, %s, %s);' % tuple(data))
|
||||
db.session.commit()
|
||||
|
||||
add_data(rt_role_data_list, RoleModel)
|
||||
|
||||
# 管理员用户
|
||||
from models import UserModel
|
||||
|
||||
add_data(cp_user_data_list, UserModel)
|
||||
|
||||
# 用户角色表
|
||||
from extensions import db
|
||||
|
||||
for data in rt_user_role_data_list:
|
||||
db.session.execute('insert into rt_user_role VALUES (%s, %s, %s);' % tuple(data))
|
||||
db.session.commit()
|
||||
|
||||
@app.cli.command()
|
||||
def turn():
|
||||
"""清空数据库"""
|
||||
from extensions import db
|
||||
db.drop_all()
|
||||
db.create_all()
|
||||
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
root_path = os.path.abspath(os.path.dirname(__file__)).split('applications')[0]
|
||||
dot_env_path = os.path.join(root_path, '.env')
|
||||
flask_env_path = os.path.join(root_path, '.flaskenv')
|
||||
|
||||
if os.path.exists(dot_env_path):
|
||||
load_dotenv(dot_env_path)
|
||||
|
||||
if os.path.exists(flask_env_path):
|
||||
load_dotenv(flask_env_path)
|
||||
@@ -0,0 +1,15 @@
|
||||
from flask import render_template
|
||||
|
||||
|
||||
def init_error_views(app):
|
||||
@app.errorhandler(403)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/403.html'), 403
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return render_template('errors/500.html'), 500
|
||||
@@ -0,0 +1,15 @@
|
||||
from flask_login import LoginManager
|
||||
|
||||
|
||||
def init_login_manager(app):
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
login_manager.login_view = 'index.login'
|
||||
login_manager.login_message = u'请登录以访问此页面'
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
from models import UserModel
|
||||
user = UserModel.query.get(int(user_id))
|
||||
return user
|
||||
@@ -1,5 +1,11 @@
|
||||
from flask_migrate import Migrate
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
|
||||
db = SQLAlchemy()
|
||||
migrate = Migrate()
|
||||
|
||||
|
||||
def init_databases(app: Flask):
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db)
|
||||
@@ -0,0 +1,7 @@
|
||||
from flask import session
|
||||
|
||||
|
||||
def init_template_directives(app):
|
||||
@app.template_global()
|
||||
def authorize(power):
|
||||
return bool(power in session.get('permissions'))
|
||||
@@ -0,0 +1,4 @@
|
||||
from .file import PhotoModel
|
||||
from .rights import RightModel, RoleModel
|
||||
from .system import LogModel
|
||||
from .users import UserModel, DepartmentModel
|
||||
@@ -0,0 +1,15 @@
|
||||
from datetime import datetime
|
||||
|
||||
from extensions import db
|
||||
|
||||
|
||||
class PhotoModel(db.Model):
|
||||
__tablename__ = 'file_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_at = db.Column(db.DateTime, default=datetime.now, comment='创建时间')
|
||||
update_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
||||
@@ -0,0 +1,62 @@
|
||||
from extensions import db
|
||||
|
||||
# 创建中间表
|
||||
user_role = db.Table(
|
||||
"rt_user_role", # 中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
|
||||
db.Column("user_id", db.Integer, db.ForeignKey("cp_user.id"), comment='用户编号'), # 属性 外键
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("rt_role.id"), comment='角色编号'), # 属性 外键
|
||||
)
|
||||
|
||||
# 创建中间表
|
||||
role_power = db.Table(
|
||||
"rt_role_power", # 中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
|
||||
db.Column("power_id", db.Integer, db.ForeignKey("rt_power.id"), comment='用户编号'), # 属性 外键
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("rt_role.id"), comment='角色编号'), # 属性 外键
|
||||
)
|
||||
|
||||
|
||||
class RightModel(db.Model):
|
||||
__tablename__ = 'rt_power'
|
||||
id = db.Column(db.Integer, primary_key=True, comment='权限编号')
|
||||
name = db.Column(db.String(255), comment='权限名称')
|
||||
type = db.Column(db.SMALLINT, comment='权限类型')
|
||||
code = db.Column(db.String(30), comment='权限标识')
|
||||
url = db.Column(db.String(255), comment='权限路径')
|
||||
open_type = db.Column(db.String(10), comment='打开方式')
|
||||
parent_id = db.Column(db.Integer, db.ForeignKey("rt_power.id"), comment='父类编号')
|
||||
icon = db.Column(db.String(128), comment='图标')
|
||||
sort = db.Column(db.Integer, comment='排序')
|
||||
enable = db.Column(db.Boolean, comment='是否开启')
|
||||
|
||||
parent = db.relationship("RightModel", remote_side=[id]) # 自关联
|
||||
|
||||
"""
|
||||
id = db.Column(db.Integer, primary_key=True, comment='权限编号')
|
||||
powerName = db.Column(db.String(255), comment='权限名称')
|
||||
powerType = db.Column(db.SMALLINT, comment='权限类型')
|
||||
powerCode = db.Column(db.String(30), comment='权限标识')
|
||||
powerUrl = db.Column(db.String(255), comment='权限路径')
|
||||
openType = db.Column(db.String(10), comment='打开方式')
|
||||
parentId = db.Column(db.Integer, db.ForeignKey("rt_power.id"), comment='父类编号')
|
||||
icon = db.Column(db.String(128), comment='图标')
|
||||
sort = db.Column(db.Integer, comment='排序')
|
||||
enable = db.Column(db.Boolean, comment='是否开启')
|
||||
checkArr = db.Column(db.String(128), comment='')
|
||||
|
||||
parent = db.relationship("RightModel", remote_side=[id]) # 自关联
|
||||
"""
|
||||
|
||||
|
||||
class RoleModel(db.Model):
|
||||
__tablename__ = 'rt_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.Boolean, comment='是否启用')
|
||||
comment = db.Column(db.String(255), comment='备注')
|
||||
details = db.Column(db.String(255), comment='详情')
|
||||
sort = db.Column(db.Integer, comment='排序')
|
||||
|
||||
power = db.relationship('RightModel', secondary="rt_role_power", backref=db.backref('role'))
|
||||
@@ -0,0 +1,18 @@
|
||||
from datetime import datetime
|
||||
|
||||
from extensions import db
|
||||
|
||||
|
||||
class LogModel(db.Model):
|
||||
__tablename__ = 'lg_logging'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
method = db.Column(db.String(10))
|
||||
uid = db.Column(db.Integer, default=None)
|
||||
url = db.Column(db.String(255))
|
||||
desc = db.Column(db.Text)
|
||||
ip = db.Column(db.String(255))
|
||||
success = db.Column(db.Boolean, default=True)
|
||||
user_agent = db.Column(db.Text)
|
||||
|
||||
create_at = db.Column(db.DateTime, default=datetime.now, comment='创建时间')
|
||||
update_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
||||
@@ -0,0 +1,48 @@
|
||||
from datetime import datetime
|
||||
|
||||
from flask_login import UserMixin
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
from extensions import db
|
||||
|
||||
|
||||
class UserModel(db.Model, UserMixin):
|
||||
__tablename__ = 'cp_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='真实名字')
|
||||
mobile = db.Column(db.String(11), comment='电话号码')
|
||||
avatar = db.Column(db.String(255), comment='头像', default="/static/admin/admin/images/avatar.jpg")
|
||||
comment = db.Column(db.String(255), comment='备注')
|
||||
password_hash = db.Column(db.String(128), comment='哈希密码')
|
||||
enable = db.Column(db.Integer, default=0, comment='启用')
|
||||
dept_id = db.Column(db.Integer, comment='部门id')
|
||||
|
||||
role = db.relationship('RoleModel', secondary="rt_user_role", backref=db.backref('user'), lazy='dynamic')
|
||||
|
||||
def set_password(self, password):
|
||||
"""设置密码,对密码进行加密存储"""
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def validate_password(self, password):
|
||||
"""校验密码方法"""
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
create_at = db.Column(db.DateTime, default=datetime.now, comment='创建时间')
|
||||
update_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
||||
|
||||
|
||||
class DepartmentModel(db.Model):
|
||||
__tablename__ = 'cp_dept'
|
||||
id = db.Column(db.Integer, primary_key=True, comment="部门ID")
|
||||
parent_id = db.Column(db.Integer, comment="父级编号")
|
||||
dept_name = db.Column(db.String(50), comment="部门名称")
|
||||
leader = db.Column(db.String(50), comment="负责人")
|
||||
phone = db.Column(db.String(20), comment="联系方式")
|
||||
email = db.Column(db.String(50), comment="邮箱")
|
||||
status = db.Column(db.Boolean, comment='状态(1开启,0关闭)')
|
||||
comment = db.Column(db.Text, comment="备注")
|
||||
address = db.Column(db.String(255), comment="详细地址")
|
||||
sort = db.Column(db.Integer, comment="排序")
|
||||
|
||||
create_at = db.Column(db.DateTime, default=datetime.now, comment='创建时间')
|
||||
@@ -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,68 +0,0 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
|
||||
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 {"code": 0, "message": "请求权限数据成功", "data": ret}
|
||||
q = db.select(DepartmentORM)
|
||||
|
||||
pages: Pagination = db.paginate(q, page=page, per_page=per_page)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"msg": "获取部门数据成功",
|
||||
"data": [item.json() for item in pages.items],
|
||||
"count": pages.total,
|
||||
}
|
||||
|
||||
|
||||
@department_api.post("/department")
|
||||
def create_department():
|
||||
data = request.get_json()
|
||||
if data["id"]:
|
||||
del data["id"]
|
||||
department = DepartmentORM(**data)
|
||||
department.save()
|
||||
return {"code": 0, "msg": "新增部门成功"}
|
||||
|
||||
|
||||
@department_api.put("/department/<int:rid>")
|
||||
def change_department(rid):
|
||||
data = request.get_json()
|
||||
del data["id"]
|
||||
|
||||
department_obj = DepartmentORM.query.get(rid)
|
||||
for key, value in data.items():
|
||||
setattr(department_obj, key, value)
|
||||
department_obj.save()
|
||||
return {"code": 0, "msg": "修改部门成功"}
|
||||
|
||||
|
||||
@department_api.delete("/department/<int:rid>")
|
||||
def del_department(rid):
|
||||
department_obj = DepartmentORM.query.get(rid)
|
||||
department_obj.delete()
|
||||
return {"code": 0, "msg": "删除删除成功"}
|
||||
@@ -1,79 +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.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 {"message": "用户不存在", "code": -1}, 401
|
||||
if not user.check_password(data["password"]):
|
||||
return {"message": "用户密码错误", "code": -1}, 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 {"msg": "退出登录成功", "code": 0}
|
||||
|
||||
|
||||
@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,101 +0,0 @@
|
||||
from copy import deepcopy
|
||||
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import jwt_required
|
||||
|
||||
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 {"code": 0, "data": tree_dict.get(0)}
|
||||
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 {"code": 0, "msg": "请求权限数据成功", "count": pages.total, "data": ret}
|
||||
return {"code": 0, "msg": "请求权限数据成功,"}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "新增权限数据成功"}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "修改权限数据成功"}
|
||||
|
||||
|
||||
@rights_api.delete("/rights/<int:rid>")
|
||||
@jwt_required()
|
||||
def delete_rights(rid):
|
||||
rights_obj = RightsORM.query.get(rid)
|
||||
rights_obj.delete()
|
||||
return {"code": 0, "msg": "删除权限数据成功"}
|
||||
@@ -1,82 +0,0 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
|
||||
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 {
|
||||
"code": 0,
|
||||
"msg": "获取角色数据成功",
|
||||
"data": [item.json() for item in pages.items],
|
||||
"count": pages.total,
|
||||
}
|
||||
|
||||
|
||||
@role_api.post("/role")
|
||||
def create_role():
|
||||
data = request.get_json()
|
||||
if data["id"]:
|
||||
del data["id"]
|
||||
role = RoleORM(**data)
|
||||
role.save()
|
||||
return {"code": 0, "msg": "新增角色成功"}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "修改角色权限成功"}
|
||||
|
||||
|
||||
@role_api.delete("/role/<int:rid>")
|
||||
def del_role(rid):
|
||||
role_obj = RoleORM.query.get(rid)
|
||||
role_obj.delete()
|
||||
return {"code": 0, "msg": "删除角色成功"}
|
||||
|
||||
|
||||
@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 {
|
||||
"code": 0,
|
||||
"msg": "返回角色权限数据成功",
|
||||
"data": own_rights_list,
|
||||
}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "授权成功"}
|
||||
@@ -1,102 +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.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 {
|
||||
"code": 0,
|
||||
"msg": "获取用户数据成功",
|
||||
"data": [item.json() for item in pages.items],
|
||||
"count": pages.total,
|
||||
}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "新增用户成功"}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "修改用户信息成功"}
|
||||
|
||||
|
||||
@user_api.delete("/user/<int:rid>")
|
||||
def del_user(rid):
|
||||
user_obj = UserORM.query.get(rid)
|
||||
user_obj.delete()
|
||||
return {"code": 0, "msg": "删除用户成功"}
|
||||
|
||||
|
||||
@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 {
|
||||
"code": 0,
|
||||
"msg": "返回角色权限数据成功",
|
||||
"data": wn_role_list,
|
||||
}
|
||||
|
||||
|
||||
@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 {"code": 0, "msg": "授权成功"}
|
||||
|
||||
|
||||
@user_api.get("/user/profile")
|
||||
@jwt_required()
|
||||
def user_profile():
|
||||
return {
|
||||
"code": 0,
|
||||
"msg": "获取个人数据成功",
|
||||
"data": current_user.json(),
|
||||
}
|
||||
@@ -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,13 +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()
|
||||
@@ -1,28 +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")
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"pid": self.pid,
|
||||
"name": self.name,
|
||||
"leader": self.leader,
|
||||
"enable": self.enable,
|
||||
}
|
||||
@@ -1,59 +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 json(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"code": self.code,
|
||||
"type": self.type,
|
||||
"url": self.url,
|
||||
"icon_sign": self.icon_sign,
|
||||
"status": self.status,
|
||||
"sort": self.sort,
|
||||
"open_type": self.open_type,
|
||||
"pid": self.pid,
|
||||
}
|
||||
|
||||
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,30 +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")
|
||||
)
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"code": self.code,
|
||||
"desc": self.desc,
|
||||
"rights_ids": self.rights_ids,
|
||||
}
|
||||
@@ -1,56 +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):
|
||||
return {
|
||||
"id": self.id,
|
||||
"username": self.username,
|
||||
"nickname": self.nickname,
|
||||
"mobile": self.mobile,
|
||||
"email": self.email,
|
||||
"create_at": self.create_at.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
|
||||
@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"
|
||||
@@ -0,0 +1,28 @@
|
||||
alembic==1.6.5
|
||||
aniso8601==9.0.1
|
||||
captcha==0.3
|
||||
click==8.0.1
|
||||
colorama==0.4.4
|
||||
Flask==2.0.1
|
||||
Flask-Login==0.5.0
|
||||
Flask-Migrate==3.0.1
|
||||
Flask-Pydantic==0.9.0
|
||||
Flask-SQLAlchemy==2.5.1
|
||||
greenlet==1.1.0
|
||||
gunicorn==20.1.0
|
||||
itsdangerous==2.0.1
|
||||
Jinja2==3.0.1
|
||||
Mako==1.1.4
|
||||
MarkupSafe==2.0.1
|
||||
Pillow==8.2.0
|
||||
psutil==5.8.0
|
||||
pydantic==1.9.1
|
||||
PyMySQL==1.0.2
|
||||
python-dateutil==2.8.1
|
||||
python-dotenv==0.18.0
|
||||
python-editor==1.0.4
|
||||
pytz==2021.1
|
||||
six==1.16.0
|
||||
SQLAlchemy==1.4.18
|
||||
typing_extensions==4.2.0
|
||||
Werkzeug==2.0.1
|
||||
+357
-440
File diff suppressed because it is too large
Load Diff
@@ -1,356 +0,0 @@
|
||||
/** loader */
|
||||
.pear-admin-dark .loader-wrapper,
|
||||
.pear-admin-dark .loader-wrapper .loader {
|
||||
background-color: #141414;
|
||||
}
|
||||
|
||||
/** layout */
|
||||
.pear-admin-dark .layui-layout {
|
||||
background-color: #141414;
|
||||
}
|
||||
|
||||
/** header */
|
||||
.pear-admin-dark .layui-header,
|
||||
.pear-admin-dark .layui-header .layui-logo {
|
||||
background-color: #141414 !important;
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.pear-admin-dark .layui-header.auto-theme,
|
||||
.pear-admin-dark .layui-header.auto-theme .layui-logo {
|
||||
background-color: var(--global-primary-color) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-header.auto-theme .layui-logo .title {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-header {
|
||||
border: 1px solid rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-header .layui-nav * {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-header .layui-nav .layui-nav-child {
|
||||
box-shadow: 0 3px 4px rgba(0, 0, 0, 0.6) !important;
|
||||
background-color: #141414;
|
||||
border-color: #141414;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-header .layui-nav .layui-nav-child dd > a:hover {
|
||||
background-color: #141414 !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-header .pear-nav-control .layui-this a {
|
||||
background-color: #0c0c0c !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .auto-theme .layui-logo .title {
|
||||
color: var(--global-primary-color) !important;
|
||||
}
|
||||
|
||||
/** side */
|
||||
.pear-admin-dark .layui-side {
|
||||
box-shadow: 0 3px 4px rgba(0, 0, 0, 0.6) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-logo {
|
||||
border-color: rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-side .layui-logo,
|
||||
.pear-admin-dark .layui-side .layui-side-scroll,
|
||||
.pear-admin-dark .layui-side .layui-side-scroll .layui-nav-tree {
|
||||
background-color: #141414 !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark
|
||||
.layui-side
|
||||
.layui-side-scroll
|
||||
.layui-nav-tree
|
||||
.layui-nav-child {
|
||||
background-color: rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-side .layui-side-scroll .layui-nav .layui-nav-item a,
|
||||
.pear-admin-dark
|
||||
.layui-side
|
||||
.layui-side-scroll
|
||||
.layui-nav
|
||||
.layui-nav-item
|
||||
a
|
||||
> .layui-nav-more {
|
||||
color: rgba(255, 255, 255, 0.7) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark
|
||||
.layui-side
|
||||
.layui-side-scroll
|
||||
.layui-nav
|
||||
.layui-nav-child
|
||||
dd.layui-this
|
||||
a,
|
||||
.pear-admin-dark
|
||||
.layui-side
|
||||
.layui-side-scroll
|
||||
.layui-nav
|
||||
.layui-nav-itemed
|
||||
> a,
|
||||
.pear-admin-dark
|
||||
.layui-side
|
||||
.layui-side-scroll
|
||||
.layui-nav
|
||||
.layui-nav-itemed
|
||||
> a
|
||||
> .layui-nav-more,
|
||||
.pear-admin-dark
|
||||
.layui-side
|
||||
.layui-side-scroll
|
||||
.layui-nav
|
||||
.layui-nav-item
|
||||
> a:hover {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/** body */
|
||||
.pear-admin-dark .layui-body,
|
||||
.pear-admin-dark .layui-body .pear-tab-page-loading,
|
||||
.pear-admin-dark .layui-body .pear-page-loading {
|
||||
background-color: #0a0a0a !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-tab-title,
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-tab-title li,
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-tab-control li {
|
||||
background-color: #141414 !important;
|
||||
border-color: rgba(0, 0, 0, 0.3) !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-tab-title li > span:first-child {
|
||||
background-color: #434343;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-nav-child.layui-anim {
|
||||
border-color: #141414;
|
||||
background-color: #141414 !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-nav-child.layui-anim a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-nav-child.layui-anim a:hover {
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-body .layui-tab .layui-tab-close:hover {
|
||||
border-radius: 50%;
|
||||
background-color: #0a0a0a !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .pear-tab-page-menu ul li {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-footer {
|
||||
background-color: #141414;
|
||||
border-top: 1px solid #141414;
|
||||
}
|
||||
|
||||
/** theme */
|
||||
.pear-admin-dark .set-text,
|
||||
.pear-admin-dark .select-color-title,
|
||||
.pear-admin-dark .color-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/** search */
|
||||
.pear-admin-dark .menu-search-no-data {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pear-admin-dark .menu-search-tips * {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pear-admin-dark .menu-search-tips kbd {
|
||||
border-color: rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
|
||||
/** message center */
|
||||
.pear-admin-dark .pear-message-center .layui-tab-title,
|
||||
.pear-admin-dark .pear-message-center .message-item {
|
||||
border-color: rgba(0, 0, 0, 0.3) !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/** button */
|
||||
.pear-admin-dark .layui-btn {
|
||||
color: #ffffff;
|
||||
border-color: #4c4d4f;
|
||||
}
|
||||
|
||||
/** layer */
|
||||
.pear-admin-dark .layui-layer {
|
||||
background-color: #141414;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-layer-msg {
|
||||
border-color: #141414;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-layer-msg .layui-layer-content {
|
||||
color: #e5eaf3;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-layer .layui-layer-setwin > span,
|
||||
.pear-admin-dark .layui-layer .layui-layer-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/** card */
|
||||
.pear-admin-dark .layui-card {
|
||||
background-color: #1d1e1f !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-card .layui-card-header {
|
||||
border-bottom-color: #414243;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-card .layui-card-body {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/** input */
|
||||
.pear-admin-dark .layui-input {
|
||||
background-color: transparent;
|
||||
color: #ffffff;
|
||||
border-color: rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
|
||||
/** switch */
|
||||
.pear-admin-dark .layui-form-switch {
|
||||
border-color: #484849;
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/** table */
|
||||
.pear-admin-dark .layui-table {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-table tr:hover {
|
||||
background-color: #141414 !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-table td,
|
||||
.pear-admin-dark .layui-table th,
|
||||
.pear-admin-dark .layui-table-view,
|
||||
.pear-admin-dark .layui-table-page,
|
||||
.pear-admin-dark .layui-table-tool,
|
||||
.pear-admin-dark .layui-table-header {
|
||||
border-color: rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-laypage select,
|
||||
.pear-admin-dark .layui-laypage button {
|
||||
border-color: rgba(0, 0, 0, 0.4) !important;
|
||||
color: #ffffff !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-table-tool-self > div {
|
||||
border-color: rgba(0, 0, 0, 0.4) !important;
|
||||
color: #ffffff !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/** panel */
|
||||
.pear-admin-dark .layui-panel {
|
||||
background-color: #1d1e1f !important;
|
||||
border-color: #1d1e1f !important;
|
||||
}
|
||||
|
||||
/** menu */
|
||||
.pear-admin-dark .layui-menu {
|
||||
background-color: #1d1e1f !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-menu .layui-menu-body-title,
|
||||
.pear-admin-dark .layui-menu .layui-menu-body-title:hover {
|
||||
color: #ffffff;
|
||||
background-color: #1d1e1f !important;
|
||||
}
|
||||
|
||||
/** timeline */
|
||||
.pear-admin-dark .layui-timeline-axis {
|
||||
background-color: rgb(29, 30, 31) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .layui-timeline-item:before {
|
||||
background-color: #414243 !important;
|
||||
}
|
||||
|
||||
/** toast */
|
||||
.pear-admin-dark .iziToast {
|
||||
background-color: #1f1f1f !important;
|
||||
}
|
||||
|
||||
/** console */
|
||||
|
||||
.pear-admin-dark .deputy,
|
||||
.pear-admin-dark .shortcut-menu {
|
||||
background-color: #141414 !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .deputy:hover,
|
||||
.pear-admin-dark .shortcut-menu:hover {
|
||||
box-shadow: 0 3px 4px rgba(0, 0, 0, 0.6) !important;
|
||||
}
|
||||
|
||||
.pear-admin-dark .message-board li {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
/** analysis */
|
||||
.pear-admin-dark .top-panel-number {
|
||||
color: #ffffff !important;
|
||||
border-color: #414243;
|
||||
}
|
||||
|
||||
.pear-admin-dark .dynamic-status dd {
|
||||
border-color: #414243;
|
||||
}
|
||||
|
||||
/** success failure */
|
||||
.pear-admin-dark .pear-result .content {
|
||||
background-color: rgba(153, 153, 153, 0.12);
|
||||
color: #e5eaf3;
|
||||
}
|
||||
|
||||
.pear-admin-dark .pear-result .title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pear-admin-dark .pear-result .description {
|
||||
color: #8d9095;
|
||||
}
|
||||
|
||||
/** 403 404 500*/
|
||||
.pear-admin-dark .pear-exception .title p {
|
||||
color: #e5eaf3 !important;
|
||||
}
|
||||
|
||||
/** scroll */
|
||||
.pear-admin-dark *::-webkit-scrollbar-thumb {
|
||||
background: #141414;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pear-admin-dark *::-webkit-scrollbar-thumb:hover {
|
||||
background: #0a0a0a;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
.loader-main{
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: whitesmoke;
|
||||
z-index: 9999999;
|
||||
}
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
.top-panel {
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top-panel > .layui-card-body {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.top-panel-number {
|
||||
line-height: 60px;
|
||||
font-size: 29px;
|
||||
border-right: 1px solid #eceff9;
|
||||
}
|
||||
|
||||
.top-panel-tips {
|
||||
padding-left: 8px;
|
||||
padding-top: 16px;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.top-panel-tips i {
|
||||
font-size: 33px;
|
||||
}
|
||||
|
||||
.top-panel-tips svg {
|
||||
margin-top: -12px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.dynamic-status {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.dynamic-status dd {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dynamic-status dd div.dynamic-status-img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.dynamic-status dd div.dynamic-status-img a {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.dynamic-status dd div span {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.dynamic-status dd div a {
|
||||
color: #01aaed;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
.shortcut-menu {
|
||||
width: 100%;
|
||||
height: 66px;
|
||||
background-color: #f8f8f8;
|
||||
display: inline-block;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.shortcut-menu i {
|
||||
font-size: 30px;
|
||||
height: 66px;
|
||||
line-height: 66px;
|
||||
}
|
||||
|
||||
.shortcut-menu:hover {
|
||||
box-shadow: 2px 0 8px 0 lightgray !important;
|
||||
}
|
||||
|
||||
.shortcut-menu-label {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.deputy {
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
background-color: #f8f8f8;
|
||||
display: inline-block;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.deputy:hover {
|
||||
box-shadow: 2px 0 8px 0 lightgray !important;
|
||||
}
|
||||
|
||||
.deputy .deputy-label {
|
||||
color: gray;
|
||||
margin-top: 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.deputy .deputy-count {
|
||||
margin-top: 12px;
|
||||
color: var(--global-primary-color);
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.message-board {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.message-board li {
|
||||
position: relative;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.message-board li p {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.message-board li > span {
|
||||
color: #999;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.message-board .message-board-reply {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 12px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
.top-panel {
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top-panel>.layui-card-body {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.top-panel-number {
|
||||
line-height: 60px;
|
||||
font-size: 29px;
|
||||
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: 31.8px;
|
||||
line-height: 31.8px;
|
||||
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,121 @@
|
||||
.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;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.pear-reply {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 12px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.pear-exception {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
padding: 70px 40px;
|
||||
}
|
||||
|
||||
.pear-exception .title {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pear-exception .title p {
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.pear-exception .description {
|
||||
margin-top: 10px;
|
||||
color: #8d9095;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pear-exception .extra {
|
||||
margin: 30px;
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,180 +1,93 @@
|
||||
html,
|
||||
body,
|
||||
.layui-row {
|
||||
height: 100%;
|
||||
.layui-form {
|
||||
width: 320px !important;
|
||||
margin: auto !important;
|
||||
margin-top: 160px !important;
|
||||
}
|
||||
|
||||
.login-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
.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;
|
||||
}
|
||||
|
||||
.login-bg {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: rgb(250, 250, 250);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.layui-form-checked[lay-skin=primary] i {
|
||||
border-color: #5FB878 !important;
|
||||
background-color: #5FB878 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.login-bg-img {
|
||||
width: 90%;
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
.layui-tab-content {
|
||||
margin-top: 15px !important;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.form-center {
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
flex-flow: row wrap;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-center-box {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.top-log-title {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.top-log {
|
||||
width: 50px;
|
||||
border-radius: 12px;
|
||||
margin-right: 20px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.top-log-title span {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--global-primary-color);
|
||||
}
|
||||
|
||||
.top-desc {
|
||||
font-size: 14px;
|
||||
color: #808695;
|
||||
}
|
||||
|
||||
.tab-log-method {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding: 20px 0px;
|
||||
}
|
||||
|
||||
.tab-log-method-item {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
padding: 5px 50px;
|
||||
text-align: right;
|
||||
color: #1f2225;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tab-log-method-item:nth-child(2) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tab-log-method-item > span {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.tab-log-method-item > span:hover {
|
||||
cursor: pointer;
|
||||
color: #16baaa;
|
||||
border-bottom: 2px solid #16baaa;
|
||||
}
|
||||
|
||||
.tab-log-verification {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.verification-text {
|
||||
flex: 2;
|
||||
box-sizing: border-box;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.verification-img {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #eeeeee;
|
||||
border-radius: 4px;
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.remember-passsword {
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.remember-cehcked {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.greenText {
|
||||
color: #16baaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-btn > .layui-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.other-login {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 20px 0 0;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.other-login-methods {
|
||||
display: inline-block;
|
||||
flex: 1;
|
||||
.layui-form-item {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
.layui-input {
|
||||
border-radius: 4px;
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
height: 44px !important;
|
||||
line-height: 44px !important;
|
||||
padding-left: 15px !important;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.layui-btn {
|
||||
border-radius: 4px;
|
||||
background-color: var(--global-primary-color);
|
||||
.layui-input:focus {
|
||||
box-shadow: 0px 0px 2px 1px #5FB878 !important;
|
||||
}
|
||||
|
||||
.layui-form-danger:focus{
|
||||
box-shadow: 0px 0px 2px 1px #f56c6c !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;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
.blog-title {
|
||||
padding-left: 13.5px;
|
||||
}
|
||||
|
||||
.blog-content {
|
||||
padding-left: 13px;
|
||||
font-size: 13px;
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.message-board {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.message-board li {
|
||||
position: relative;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.message-board li p {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.message-board li > span {
|
||||
color: #999;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.message-board .message-board-reply {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 12px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
@@ -1,36 +1,39 @@
|
||||
.pear-result {
|
||||
text-align: center;
|
||||
.result {
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
.pear-result .success svg {
|
||||
color: #32c682;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
.result .success svg {
|
||||
color: #32C682;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
|
||||
}
|
||||
.pear-result .error svg {
|
||||
color: #f56c6c;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
.result .error svg {
|
||||
color: #f56c6c;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
|
||||
}
|
||||
.pear-result .title {
|
||||
margin-top: 25px;
|
||||
.result .title {
|
||||
margin-top: 25px;
|
||||
|
||||
}
|
||||
.pear-result .description {
|
||||
margin-top: 25px;
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
.result .desc {
|
||||
margin-top: 25px;
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
}
|
||||
.pear-result .content {
|
||||
margin-top: 20px;
|
||||
width: 80%;
|
||||
border-radius: 4px;
|
||||
background-color: whitesmoke;
|
||||
padding: 20px 32px;
|
||||
margin-left: 10%;
|
||||
margin-bottom: 30px;
|
||||
text-align: left;
|
||||
.result .content {
|
||||
margin-top: 20px;
|
||||
width: 80%;
|
||||
border-radius: 10px;
|
||||
background-color: whitesmoke;
|
||||
height: 200px;
|
||||
margin-left: 10%;
|
||||
}
|
||||
.pear-result .extra {
|
||||
padding-top: 10px;
|
||||
margin-top: 25px;
|
||||
.result .action {
|
||||
padding-top: 10px;
|
||||
border-top: 1px whitesmoke solid;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
.layui-dropdown {
|
||||
border-radius: var(--global-border-radius);
|
||||
}
|
||||
|
||||
.layui-input {
|
||||
border-radius: var(--global-border-radius);
|
||||
}
|
||||
|
||||
.layui-form-onswitch {
|
||||
background-color: var(--global-primary-color) !important;
|
||||
}
|
||||
|
||||
.layui-btn {
|
||||
background-color: var(--global-primary-color);
|
||||
}
|
||||
|
||||
.layui-btn.layui-btn-normal {
|
||||
background-color: #1e9fff !important;
|
||||
}
|
||||
|
||||
.layui-btn.layui-btn-danger {
|
||||
background-color: #ff5722 !important;
|
||||
}
|
||||
|
||||
.layui-btn.layui-btn-warm {
|
||||
background-color: #ffb800 !important;
|
||||
}
|
||||
|
||||
.layui-btn.layui-btn-primary {
|
||||
background-color: transparent !important;
|
||||
color: #5f5f5f !important;
|
||||
}
|
||||
|
||||
.layui-card {
|
||||
border-radius: var(--global-border-radius);
|
||||
}
|
||||
|
||||
.layui-timeline-axis {
|
||||
color: var(--global-primary-color);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
:root {
|
||||
--global-primary-color: #16baaa;
|
||||
|
||||
--global-border-radius: 4px;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"msg": "not data",
|
||||
"count": 30,
|
||||
"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": "3",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png",
|
||||
"title": "Angular",
|
||||
"remark": "希望是一个好东西,也许是最好的,好东西是不会消亡的",
|
||||
"time": "几秒前"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png",
|
||||
"title": "React",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
}, {
|
||||
"id": "5",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png",
|
||||
"title": "Alipay",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
}, {
|
||||
"id": "6",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png",
|
||||
"title": "Layui",
|
||||
"remark": "生命就像一盒巧克力,结果往往出人意料",
|
||||
"time": "几秒前"
|
||||
}, {
|
||||
"id": "7",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png",
|
||||
"title": "Angular",
|
||||
"remark": "希望是一个好东西,也许是最好的,好东西是不会消亡的",
|
||||
"time": "几秒前"
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"image": "https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png",
|
||||
"title": "React",
|
||||
"remark": "那是一种内在的东西, 他们到达不了,也无法触及的",
|
||||
"time": "几秒前"
|
||||
}
|
||||
],
|
||||
"code": 0
|
||||
}
|
||||
@@ -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"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
{
|
||||
"createTime": null,
|
||||
"createBy": null,
|
||||
"updateTime": null,
|
||||
"updateBy": null,
|
||||
"remark": null,
|
||||
"code": 0,
|
||||
"msg": null,
|
||||
"count": 167,
|
||||
"data": [{
|
||||
"id": "1305106851892822016",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T11:31:36.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305101593019940864",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T11:10:42.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305101247900024832",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T11:09:20.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305099674977304576",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T11:03:05.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305095186061197312",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T10:45:15.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305092027951611904",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T10:32:42.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305082862634008576",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T09:56:16.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305071134877679616",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T09:09:40.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305070481803575296",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T09:07:05.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}, {
|
||||
"id": "1305064691009060864",
|
||||
"title": "登录",
|
||||
"description": "登录成功",
|
||||
"businessType": "OTHER",
|
||||
"requestMethod": "POST",
|
||||
"method": "/login",
|
||||
"operateUrl": "/login",
|
||||
"operateAddress": "127.0.0.1",
|
||||
"requestParam": null,
|
||||
"requestBody": "",
|
||||
"responseBody": null,
|
||||
"success": true,
|
||||
"loggingType": "LOGIN",
|
||||
"errorMsg": null,
|
||||
"systemOs": "Windows",
|
||||
"createTime": "2020-09-13T08:44:04.000+0000",
|
||||
"operateName": "admin",
|
||||
"map": {},
|
||||
"browser": "谷歌浏览器"
|
||||
}]
|
||||
}
|
||||
+423
-305
@@ -1,305 +1,423 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"title": "工作空间",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"children": [
|
||||
{
|
||||
"id": "10",
|
||||
"title": "分析页",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/analysis/index.html"
|
||||
},
|
||||
{
|
||||
"id": "11",
|
||||
"title": "工作台",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/console/index.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"title": "列表页面",
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"type": 0,
|
||||
"children": [
|
||||
{
|
||||
"id": 91,
|
||||
"title": "查询表格",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/listing/table.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "扩展组件",
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"type": 0,
|
||||
"children": [
|
||||
{
|
||||
"id": 23,
|
||||
"title": "高级栅格",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/component/grid.html"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"title": "消息提示",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/component/toast.html"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"title": "路由过渡",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/component/nprogress.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": "_component",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "failure",
|
||||
"title": "失败页面",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/result/error.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "exception",
|
||||
"title": "异常页面",
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [
|
||||
{
|
||||
"id": "403",
|
||||
"title": "403",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/exception/403.html"
|
||||
},
|
||||
{
|
||||
"id": "404",
|
||||
"title": "404",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/exception/404.html"
|
||||
},
|
||||
{
|
||||
"id": "500",
|
||||
"title": "500",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/exception/500.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "open",
|
||||
"title": "菜单模式",
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"type": 0,
|
||||
"children": [
|
||||
{
|
||||
"id": "a",
|
||||
"title": "普通路由",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
"title": "嵌套网页",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "http://www.layui-vue.com"
|
||||
},
|
||||
{
|
||||
"id": "c",
|
||||
"title": "新建标签",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com"
|
||||
},
|
||||
{
|
||||
"id": "d",
|
||||
"title": "弹窗网页",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_layer",
|
||||
"href": "http://www.layui-vue.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deep",
|
||||
"title": "深度测试",
|
||||
"icon": "layui-icon layui-icon-auz",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-1",
|
||||
"title": "二级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 0,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-1-1",
|
||||
"title": "三级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 0,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-1-1-1",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "deep1-1-1-2",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deep1-1-2",
|
||||
"title": "三级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 0,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-1-2-1",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "deep1-1-2-2",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deep1-2",
|
||||
"title": "二级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 0,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-2-1",
|
||||
"title": "三级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 0,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-2-1-1",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "deep1-2-1-2",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deep1-2-2",
|
||||
"title": "三级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 0,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com",
|
||||
"children": [
|
||||
{
|
||||
"id": "deep1-2-2-1",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_component",
|
||||
"href": "view/result/success.html"
|
||||
},
|
||||
{
|
||||
"id": "deep1-2-2-2",
|
||||
"title": "四级菜单",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_blank",
|
||||
"href": "http://www.layui-vue.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
[{
|
||||
"id": 1,
|
||||
"title": "工作空间",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 0,
|
||||
"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.bing.com"
|
||||
}, {
|
||||
"id": 15,
|
||||
"title": "主题预览",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/system/theme.html"
|
||||
}, {
|
||||
"id": 16,
|
||||
"title": "酸爽翻倍",
|
||||
"icon": "layui-icon layui-icon-console",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/core.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": 2025,
|
||||
"title": "消息通知 (新增)",
|
||||
"icon": "layui-icon layui-icon-face-cry",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/document/toast.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/encrypt.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/department.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"
|
||||
}]
|
||||
}, {
|
||||
"id": "echarts",
|
||||
"title": "数据图表",
|
||||
"icon": "layui-icon layui-icon-chart",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 12121,
|
||||
"title": "折线图",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/echarts/line.html"
|
||||
}, {
|
||||
"id": 121212,
|
||||
"title": "柱状图",
|
||||
"icon": "layui-icon layui-icon-face-smile",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "view/echarts/column.html"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": "code",
|
||||
"title": "开发工具",
|
||||
"icon": "layui-icon layui-icon-util",
|
||||
"type": 0,
|
||||
"href": "",
|
||||
"children": [{
|
||||
"id": 801,
|
||||
"title": "表单构建",
|
||||
"icon": "layui-icon layui-icon-util",
|
||||
"type": 1,
|
||||
"openType": "_iframe",
|
||||
"href": "component/code/index.html"
|
||||
}]
|
||||
}
|
||||
]
|
||||
+127
-102
@@ -1,102 +1,127 @@
|
||||
{
|
||||
"code": 200,
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "通知",
|
||||
"children": [
|
||||
{
|
||||
"id": 11,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "你收到了 14 份新周报",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
||||
"title": "曲妮妮 已通过第三轮面试",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
||||
"title": "可以区分多种通知类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||
"title": "左侧图标用于区分不同的类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "内容不要超过两行字",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "消息",
|
||||
"children": [
|
||||
{
|
||||
"id": 11,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "你收到了 14 份新周报",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
||||
"title": "曲妮妮 已通过第三轮面试",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
||||
"title": "可以区分多种通知类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||
"title": "左侧图标用于区分不同的类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "内容不要超过两行字",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "代办",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
[{
|
||||
"id": 1,
|
||||
"title": "通知",
|
||||
"children": [{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "你收到了 14 份新周报",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}, {
|
||||
"id": 12,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
||||
"title": "曲妮妮 已通过第三轮面试",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
||||
"title": "可以区分多种通知类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}, {
|
||||
"id": 12,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||
"title": "左侧图标用于区分不同的类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "内容不要超过两行字",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "消息",
|
||||
"children": [{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "你收到了 14 份新周报",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}, {
|
||||
"id": 12,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
||||
"title": "曲妮妮 已通过第三轮面试",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
||||
"title": "可以区分多种通知类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}, {
|
||||
"id": 12,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||
"title": "左侧图标用于区分不同的类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "内容不要超过两行字",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "代办",
|
||||
"children": [{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "你收到了 14 份新周报",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}, {
|
||||
"id": 12,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
||||
"title": "曲妮妮 已通过第三轮面试",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
||||
"title": "可以区分多种通知类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}, {
|
||||
"id": 12,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||
"title": "左侧图标用于区分不同的类型",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"avatar":"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||
"title": "内容不要超过两行字",
|
||||
"context": "这是消息内容。",
|
||||
"form": "就眠仪式",
|
||||
"time": "刚刚"
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user