diff --git a/README.md b/README.md index ec8c69cc4c65455926239af4638633229e11e2c4..7954902c470866c8f8a115153e1b5dfa941b4b41 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 开 箱 即 用 的 Flask 快 速 开 发 平 台 - [预 览](http://flask.pearadmin.com) | [官 网](http://www.pearadmin.com/) | [群聊](docs/group.md) | [文档](docs/detail.md) + [预览](http://flask.pearadmin.com) | [官网](http://www.pearadmin.com/) | [群聊](docs/group.md) | [文档](docs/detail.md)
@@ -46,8 +46,48 @@ Pear Admin Flask 基于 Flask 的后台管理系统,拥抱应用广泛的pytho
#### 项目结构
+## 应用结构
+```应用结构
+Pear Admin Flask
+├─applications # 应用
+│ ├─configs # 配置文件
+│ │ ├─ common.py # 普通配置
+│ │ └─ config.py # 配置文件对象
+│ ├─extensions # 注册插件
+│ ├─models # 数据模型
+│ ├─static # 静态资源文件
+│ ├─templates # 静态模板文件
+│ └─views # 视图部分
+│ ├─admin # 后台管理视图模块
+│ └─index # 前台视图模块
+├─docs # 文档说明
+├─migrations # 迁移文件记录
+├─requirement # 依赖文件
+└─.env # 项目的配置文件
```
+## 资源结构
+```资源结构
+Pear Admin Flask
+├─static # 项目设定的 Flask 资源文件夹
+│ ├─admin # pear admin flask 的后端资源文件(与 pear admin layui 同步)
+│ ├─index # pear admin flask 的前端资源文件
+│ └─upload # 用户上传保存目录
+└─templates # 项目设定的 Flask 模板文件夹
+ ├─admin # pear admin flask 的后端管理页面模板
+ │ ├─admin_log # 日志页面
+ │ ├─common # 基本模板页面(头部模板与页脚模板)
+ │ ├─console # 系统监控页面模板
+ │ ├─dept # 部门管理页面模板
+ │ ├─dict # 数据自动页面模板
+ │ ├─mail # 邮件管理页面模板
+ │ ├─photo # 图片上传页面模板
+ │ ├─power # 权限(菜单)管理页面模板
+ │ ├─role # 角色管理页面模板
+ │ ├─task # 任务设置页面模板
+ │ └─user # 用户管理页面模板
+ ├─errors # 错误页面模板
+ └─index # 主页模板
```
#### 项目安装
diff --git a/applications/common/script/admin.py b/applications/common/script/admin.py
index fec84ab203e565335bad8cc287fdeb4d93c24eac..b49920dc363077c7b9e9d3db6efcd8654def9ea8 100644
--- a/applications/common/script/admin.py
+++ b/applications/common/script/admin.py
@@ -602,58 +602,6 @@ powerdata = [
create_time=now_time,
enable=1,
- ), Power(
- id=60,
- name='拓展插件',
- type='0',
- code='',
- url='',
- open_type='',
- parent_id='0',
- icon='layui-icon layui-icon-senior',
- sort=2,
- create_time=now_time,
- enable=1,
-
- ), Power(
- id=61,
- name='插件管理',
- type='1',
- code='admin:plugin:main',
- url='/plugin',
- open_type='_iframe',
- parent_id='60',
- icon='layui-icon layui-icon',
- sort=2,
- create_time=now_time,
- enable=1,
-
- ), Power(
- id=62,
- name='启禁插件',
- type='2',
- code='admin:plugin:enable',
- url='',
- open_type='',
- parent_id='61',
- icon='layui-icon layui-icon',
- sort=1,
- create_time=now_time,
- enable=1,
-
- ), Power(
- id=63,
- name='删除插件',
- type='2',
- code='admin:plugin:remove',
- url='',
- open_type='',
- parent_id='61',
- icon='layui-icon layui-icon',
- sort=2,
- create_time=now_time,
- enable=1,
-
)
]
diff --git a/applications/config.py b/applications/config.py
index 64959863dbb7639e7d886b752edd5124301374cc..15b278149117190f55bac46f229ab3708a00020e 100644
--- a/applications/config.py
+++ b/applications/config.py
@@ -80,8 +80,8 @@ class BaseConfig:
'max_instances': 3
}
- # 插件配置
- PLUGIN_ENABLE_FOLDERS = ["helloworld"]
+ # 插件配置,填写插件的文件名名称,默认不启用插件。
+ PLUGIN_ENABLE_FOLDERS = []
# 配置多个数据库连接的连接串写法示例
# HOSTNAME: 指数据库的IP地址、USERNAME:指数据库登录的用户名、PASSWORD:指数据库登录密码、PORT:指数据库开放的端口、DATABASE:指需要连接的数据库名称
diff --git a/applications/view/__init__.py b/applications/view/__init__.py
index ef00903e369c0c1ac26ac2ce7651505f0c095eed..6b5cf78b6fe07f81df34aeba4829e5a1639a64d5 100644
--- a/applications/view/__init__.py
+++ b/applications/view/__init__.py
@@ -11,4 +11,4 @@ def init_view(app):
register_rights_view(app)
register_passport_views(app)
register_dept_views(app)
- # register_plugin_views(app)
+ register_plugin_views(app)
diff --git a/applications/view/plugin/__init__.py b/applications/view/plugin/__init__.py
index 4eb6c34d8dd8729e4fee5c9c341835cfa5c7ebeb..ec94ef5a48701b2da9cd308828d402de98b6dc36 100644
--- a/applications/view/plugin/__init__.py
+++ b/applications/view/plugin/__init__.py
@@ -18,7 +18,7 @@ def register_plugin_views(app: Flask):
app.register_blueprint(plugin_bp)
# 载入插件过程
# plugin_folder 配置的是插件的文件夹名
- PLUGIN_ENABLE_FOLDERS = json.loads(app.config['PLUGIN_ENABLE_FOLDERS'])
+ PLUGIN_ENABLE_FOLDERS = app.config['PLUGIN_ENABLE_FOLDERS']
for plugin_folder in PLUGIN_ENABLE_FOLDERS:
plugin_info = {}
try:
@@ -39,124 +39,3 @@ def register_plugin_views(app: Flask):
info += 'repr(e):\t' + repr(e) + "\n"
info += 'traceback.format_exc():\n%s' + traceback.format_exc()
print(info)
-
-
-@plugin_bp.get('/')
-@authorize("admin:plugin:main", log=True)
-def main():
- """此处渲染管理模板"""
- return render_template('admin/plugin/main.html')
-
-
-@plugin_bp.get('/data')
-@authorize("admin:plugin:main", log=True)
-def data():
- """请求插件数据"""
- plugin_name = escape(request.args.get("plugin_name"))
- all_plugins = []
- count = 0
- for filename in os.listdir("plugins"):
- try:
- with open("plugins/" + filename + "/__init__.json", "r", encoding='utf-8') as f:
- info = json.loads(f.read())
-
- if plugin_name is None:
- if info['plugin_name'].find(plugin_name) == -1:
- continue
-
- all_plugins.append(
- {
- "plugin_name": info["plugin_name"],
- "plugin_version": info["plugin_version"],
- "plugin_description": info["plugin_description"],
- "plugin_folder_name": filename,
- "enable": "1" if filename in PLUGIN_ENABLE_FOLDERS else "0"
- }
- )
- count += 1
- except BaseException as error:
- print(filename, error)
- continue
- return table_api(data=all_plugins, count=count)
-
-
-@plugin_bp.put('/enable')
-@authorize("admin:plugin:enable", log=True)
-def enable():
- """启用插件"""
- plugin_folder_name = request.get_json(force=True).get('plugin_folder_name')
- if plugin_folder_name:
- try:
- if plugin_folder_name not in PLUGIN_ENABLE_FOLDERS:
- PLUGIN_ENABLE_FOLDERS.append(plugin_folder_name)
- with open(".flaskenv", "r", encoding='utf-8') as f:
- flaskenv = f.read() # type: str
- pos1 = flaskenv.find("PLUGIN_ENABLE_FOLDERS")
- pos2 = flaskenv.find("\n", pos1)
- with open(".flaskenv", "w", encoding='utf-8') as f:
- if pos2 == -1:
- f.write(flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS))
- else:
- f.write(
- flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS) + flaskenv[
- pos2:])
- # 启用插件事件
- try:
- getattr(importlib.import_module('plugins.' + plugin_folder_name), "event_enable")()
- except AttributeError: # 没有插件启用事件就不调用
- pass
- except BaseException as error:
- return fail_api(msg="Crash a error! Info: " + str(error))
-
- except BaseException as error:
- return fail_api(msg="Crash a error! Info: " + str(error))
- return success_api(msg="启用成功,要使修改生效需要重启程序。")
- return fail_api(msg="数据错误")
-
-
-@plugin_bp.put('/disable')
-@authorize("admin:plugin:enable", log=True)
-def disable():
- """禁用插件"""
- plugin_folder_name = request.get_json(force=True).get('plugin_folder_name')
- if plugin_folder_name:
- try:
- if plugin_folder_name in PLUGIN_ENABLE_FOLDERS:
- PLUGIN_ENABLE_FOLDERS.remove(plugin_folder_name)
- with open(".flaskenv", "r", encoding='utf-8') as f:
- flaskenv = f.read() # type: str
- pos1 = flaskenv.find("PLUGIN_ENABLE_FOLDERS")
- pos2 = flaskenv.find("\n", pos1)
- with open(".flaskenv", "w", encoding='utf-8') as f:
- if pos2 == -1:
- f.write(flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS))
- else:
- f.write(
- flaskenv[:pos1] + "PLUGIN_ENABLE_FOLDERS = " + json.dumps(PLUGIN_ENABLE_FOLDERS) + flaskenv[
- pos2:])
-
- # 禁用插件事件
- try:
- getattr(importlib.import_module('plugins.' + plugin_folder_name), "event_disable")()
- except AttributeError: # 没有插件禁用事件就不调用
- pass
- except BaseException as error:
- return fail_api(msg="Crash a error! Info: " + str(error))
-
- except BaseException as error:
- return fail_api(msg="Crash a error! Info: " + str(error))
- return success_api(msg="禁用成功,要使修改生效需要重启程序。")
- return fail_api(msg="数据错误")
-
-
-# 删除
-@plugin_bp.delete('/remove/Hello
")
+```
+
++ 基于二次开发的邮件发送函数
+
+### 函数原型
+
+函数调用位于项目代码 ```applications/common/utils/mail.py``` 中,函数原型如下:
+
+```
+def add(receiver, subject, content, user_id):
+ """
+ 发送一封邮件,若发送成功立刻提交数据库。
+
+ :param receiver: 接收者 多个用英文逗号隔开
+ :param subject: 邮件主题
+ :param content: 邮件 html
+ :param user_id: 发送用户ID(谁发送的?) 可以用 from flask_login import current_user ; current_user.id 来表示当前登录用户
+ :return: 成功与否
+ """
+ ...
+```
+
+### 示例代码
+
+```python
+#在.flaskenv中配置邮箱
+from applications.common.utils import mail
+
+mail.add("test@test.com", "subject", "Hello
", current_user)
+```
+
+
+
+## 返回格式
+
+> 后端响应时我们推荐使用规定的API响应格式。
+
+### 函数原型
+
+函数调用位于项目代码 ```applications/common/utils/http.py``` 中,函数原型如下:
+
+```
+def success_api(msg: str = "成功"):
+ """ 成功响应 默认值“成功” """
+ return jsonify(success=True, msg=msg)
+
+
+def fail_api(msg: str = "失败"):
+ """ 失败响应 默认值“失败” """
+ return jsonify(success=False, msg=msg)
+
+
+def table_api(msg: str = "", count=0, data=None, limit=10):
+ """ 动态表格渲染响应 """
+ res = {
+ 'msg': msg,
+ 'code': 0,
+ 'data': data,
+ 'count': count,
+ 'limit': limit
+
+ }
+ return jsonify(res)
+```
+
+### 示例代码
+
+```python
+from applications.common.utils.http import success_api, fail_api, table_api
+
+@admin_log.get('/operateLog')
+@authorize("admin:log:main")
+def operate_log():
+ # orm查询
+ # 使用分页获取data需要.items
+ log = AdminLog.query.filter(
+ AdminLog.url != '/passport/login').order_by(
+ desc(AdminLog.create_time)).layui_paginate()
+ count = log.total
+ return table_api(data=model_to_dicts(schema=LogOutSchema, data=log.items), count=count)
+```
+
+```python
+from applications.common.utils.http import success_api, fail_api, table_api
+
+@admin_power.post('/save')
+@authorize("admin:power:add", log=True)
+def save():
+ ... # 若干操作
+ if success:
+ return success_api(msg="成功")
+ return fail_api(msg="成功")
+```
diff --git a/docs/group.md b/docs/group.md
deleted file mode 100644
index 0da9fe9cb001905e4914ba30ba1bb095b0171858..0000000000000000000000000000000000000000
--- a/docs/group.md
+++ /dev/null
@@ -1,2 +0,0 @@
-
-微信群不定期在qq群更新二维码
\ No newline at end of file
diff --git a/docs/install.md b/docs/install.md
new file mode 100644
index 0000000000000000000000000000000000000000..7b54e72a0a8f1f17f325d7c741f502c78dd98258
--- /dev/null
+++ b/docs/install.md
@@ -0,0 +1,113 @@
+### 环境要求 :id=install
+- Python >= 3.6
+- Mysql >= 5.7.0
+
+### 安装配置
+
+#### 克隆远程仓库
+
+您可以使用 git 来克隆远程仓库:
+
+```shell
+# 进入项目主目录
+cd Pear Admin Flask
+
+# 使用 git 克隆远程仓库
+git clone https://gitee.com/pear-admin/pear-admin-flask.git
+
+# 切换分支
+git checkout master # master, main or mini
+```
+
+或者直接前往 Pear Admin Flask 项目的[Gitee 主页](https://gitee.com/pear-admin/pear-admin-flask)下载项目仓库。
+
+#### 搭建开发环境
+
+我们推荐使用 Python 的虚拟环境来开发该项目,这样便于项目的迁移与二次开发。当然,您也可以选择使用原 Python 环境。
+
+如果你想创建 Python 虚拟环境,你可以使用下面的命令行:
+
+```shell
+# 在当前目录的venv文件夹创建虚拟环境
+python -m venv venv
+
+# Windows 激活虚拟环境
+.\test_env\Scripts\Activate.ps1
+
+# Linux 和 Mac 激活虚拟环境
+source ./test_env/bin/activate
+```
+
+**如果在创建虚拟环境时报错 “ModuleNotFoundError” ,这说明您的 Python 版本小于 3.3 。**
+
+#### 安装项目依赖
+
+```shell
+# 使用 pip 安装必要模块(对于 master 分支)
+pip install -r requirement\requirement.txt
+
+# 使用 pip 安装必要模块(对于 mini 分支)
+pip install -r requirement.txt
+```
+
+或者您可以尝试:
+
+```shell
+# 使用 pip 安装必要模块
+python -m pip install -r requirement.txt
+```
+
+#### 配置数据库
+
+在 `applications/config.py` 中配置好数据库。
+
+> 由于结构与目录调整,我们简化了项目结构,废弃了文件 ```.flaskenv``` ,请使用 config.py 配置程序。
+
+并使用以下命令生成数据库文件:
+
+```shell
+# 初始化数据库
+flask db init
+flask db migrate
+flask db upgrade
+flask admin init
+```
+
+#### 运行项目
+
+```shell
+python app.py
+
+# 或者可以使用
+flask run
+```
+
+#### 使用docker-compose运行项目
+
+```shell
+# 安装docker-compose
+curl -L https://github.com/docker/compose/releases/download/1.26.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
+chmod +x /usr/local/bin/docker-compose
+ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
+
+docker-compose --version
+docker-compose up -d # -d后台运行
+docker-compose stop # 停止启动
+docker-compose down # 清除容器
+
+dockerdata/config.py # 配置文件
+dockerdata/mysql/initdb/ # MySQL初始化数据在
+rm -rf dockerdata/mysql/{log,data}/* # down掉容器后启动需要清除删除log,dat
+```
+
+### 二次开发
+
+恭喜!现在您已经成功搭建并运行了 Pear Admin Flask ,是时候参与开发了:
+
+请阅读:
+
++ Pear Admin Flask [目录结构](list.md) 章节
++ Pear Admin Flask [开发函数](function.md) 章节
++ Pear Admin Flask [插件开发](plugin.md) 章节
+
+其它章节等待更新。
diff --git a/docs/list.md b/docs/list.md
new file mode 100644
index 0000000000000000000000000000000000000000..891fa730731909584abb523fc463fda1268bf095
--- /dev/null
+++ b/docs/list.md
@@ -0,0 +1,43 @@
+## 应用结构 :id=config
+```应用结构
+Pear Admin Flask
+├─applications # 应用
+│ ├─configs # 配置文件
+│ │ ├─ common.py # 普通配置
+│ │ └─ config.py # 配置文件对象
+│ ├─extensions # 注册插件
+│ ├─models # 数据模型
+│ ├─static # 静态资源文件
+│ ├─templates # 静态模板文件
+│ └─views # 视图部分
+│ ├─admin # 后台管理视图模块
+│ └─index # 前台视图模块
+├─docs # 文档说明
+├─migrations # 迁移文件记录
+├─requirement # 依赖文件
+└─.env # 项目的配置文件
+```
+
+## 资源结构 :id=static
+```资源结构
+Pear Admin Flask
+├─static # 项目设定的 Flask 资源文件夹
+│ ├─admin # pear admin flask 的后端资源文件(与 pear admin layui 同步)
+│ ├─index # pear admin flask 的前端资源文件
+│ └─upload # 用户上传保存目录
+└─templates # 项目设定的 Flask 模板文件夹
+ ├─admin # pear admin flask 的后端管理页面模板
+ │ ├─admin_log # 日志页面
+ │ ├─common # 基本模板页面(头部模板与页脚模板)
+ │ ├─console # 系统监控页面模板
+ │ ├─dept # 部门管理页面模板
+ │ ├─dict # 数据自动页面模板
+ │ ├─mail # 邮件管理页面模板
+ │ ├─photo # 图片上传页面模板
+ │ ├─power # 权限(菜单)管理页面模板
+ │ ├─role # 角色管理页面模板
+ │ ├─task # 任务设置页面模板
+ │ └─user # 用户管理页面模板
+ ├─errors # 错误页面模板
+ └─index # 主页模板
+```
\ No newline at end of file
diff --git a/docs/plugin.md b/docs/plugin.md
new file mode 100644
index 0000000000000000000000000000000000000000..9ccb1d479995e76f965e27e20ed4a1c1402432e6
--- /dev/null
+++ b/docs/plugin.md
@@ -0,0 +1,47 @@
+### 说明
+
+插件功能旨在最大限度不修改原框架的前提下添加新功能,我们提供了三个示例插件。
+
+
+### 插件配置
+
+将插件文件夹放置在 ```applications/config.py``` 文件夹中,并且在 .flaskenv 中配置。再配置项中填入插件的文件夹名,已 json 格式写入其中。
+
+```python
+# 插件配置
+PLUGIN_ENABLE_FOLDERS = ["helloworld"]
+```
+
+### 插件目录
+
+```
+Plugin
+│ __init__.json
+└─ __init__.py
+```
+
+这是一个非常简单的插件。
+
+### 插件信息
+
+插件信息保存在 ```__init__.py``` 中,以测试插件“helloword”为例。插件的数据应该不少于下面三项:
+
+```json
+{
+ "plugin_name": "Hello World",
+ "plugin_version": "1.0.0.1",
+ "plugin_description": "一个测试的插件。"
+}
+```
+
+### 插件格式
+
+插件的入口点为 ```__init__.py``` 文件,在插件被启用后,程序启动时此 Python 文件中的 ```event_init``` 函数。代码如下:
+
+```python
+from flask import Flask
+
+def event_init(app: Flask):
+ """初始化完成时会调用这里"""
+ print("加载完毕后,我会输出一句话。")
+```
\ No newline at end of file
diff --git a/plugins/helloworld/__init__.py b/plugins/helloworld/__init__.py
index efbe8aef6ebd3855f422ec6d0b3935131e3f3173..d6b218ea575edceb9d88aa2ee42363cfb2940455 100644
--- a/plugins/helloworld/__init__.py
+++ b/plugins/helloworld/__init__.py
@@ -10,14 +10,6 @@ from .main import helloworld_blueprint
dir_path = os.path.dirname(__file__).replace("\\", "/")
folder_name = dir_path[dir_path.rfind("/") + 1:] # 插件文件夹名称
-def event_enable():
- """当此插件被启用时会调用此处"""
- print(f"启用插件,dir_path: {dir_path} ; folder_name: {folder_name}")
-
-def event_disable():
- """当此插件被禁用时会调用此处"""
- print(f"禁用插件,dir_path: {dir_path} ; folder_name: {folder_name}")
-
def event_init(app: Flask):
"""初始化完成时会调用这里"""
app.register_blueprint(helloworld_blueprint)
\ No newline at end of file
diff --git a/plugins/realip/__init__.py b/plugins/realip/__init__.py
index f6971fd981b0dc6f1a2cd8ad46a25bf09da1caf3..50e18b4fdaa71c5d11466c1c97c6bb59c1cfa35f 100644
--- a/plugins/realip/__init__.py
+++ b/plugins/realip/__init__.py
@@ -10,15 +10,6 @@ from . import console
dir_path = os.path.dirname(__file__).replace("\\", "/")
folder_name = dir_path[dir_path.rfind("/") + 1:] # 插件文件夹名称
-def event_enable():
- """当此插件被启用时会调用此处"""
- print(f"启用插件,dir_path: {dir_path} ; folder_name: {folder_name}")
-
-
-def event_disable():
- """当此插件被禁用时会调用此处"""
- print(f"禁用插件,dir_path: {dir_path} ; folder_name: {folder_name}")
-
def event_init(app: Flask):
"""初始化完成时会调用这里"""
# 移除原有的输出日志
diff --git a/plugins/replacePage/__init__.py b/plugins/replacePage/__init__.py
index 3756f0a66c66b50c9d6dfc0276bace98854bfdba..6e4cd7e40952ddd4c6610b6ac28b33ba36d1ce0a 100644
--- a/plugins/replacePage/__init__.py
+++ b/plugins/replacePage/__init__.py
@@ -8,15 +8,6 @@ from flask import Flask, render_template_string
dir_path = os.path.dirname(__file__).replace("\\", "/")
folder_name = dir_path[dir_path.rfind("/") + 1:] # 插件文件夹名称
-def event_enable():
- """当此插件被启用时会调用此处"""
- print(f"启用插件,dir_path: {dir_path} ; folder_name: {folder_name}")
-
-
-def event_disable():
- """当此插件被禁用时会调用此处"""
- print(f"禁用插件,dir_path: {dir_path} ; folder_name: {folder_name}")
-
def event_init(app: Flask):
"""初始化完成时会调用这里"""
# 使用下面的代码 查看所有注册的视图函数。对于 Flask app.route 函数的实现,请参考 https://www.jianshu.com/p/dff3bc2f4836
diff --git a/templates/admin/plugin/main.html b/templates/admin/plugin/main.html
deleted file mode 100644
index b0e4f181ff2731fb97f0a58851f571e121676162..0000000000000000000000000000000000000000
--- a/templates/admin/plugin/main.html
+++ /dev/null
@@ -1,288 +0,0 @@
-
-
-
-
-