insistence
1 year ago
committed by
Gitee
69 changed files with 1913 additions and 1763 deletions
@ -1,36 +0,0 @@ |
|||
# RuoYi-Vue3-FastAPI |
|||
|
|||
#### Description |
|||
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} |
|||
|
|||
#### Software Architecture |
|||
Software architecture description |
|||
|
|||
#### Installation |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### Instructions |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### Contribution |
|||
|
|||
1. Fork the repository |
|||
2. Create Feat_xxx branch |
|||
3. Commit your code |
|||
4. Create Pull Request |
|||
|
|||
|
|||
#### Gitee Feature |
|||
|
|||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md |
|||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com) |
|||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) |
|||
4. The most valuable open source project [GVP](https://gitee.com/gvp) |
|||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) |
|||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
@ -1,39 +1,184 @@ |
|||
# RuoYi-Vue3-FastAPI |
|||
<p align="center"> |
|||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png"> |
|||
</p> |
|||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.0.0</h1> |
|||
<h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4> |
|||
<p align="center"> |
|||
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/stargazers"><img src="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/badge/star.svg?theme=dark"></a> |
|||
<a href="https://github.com/insistence/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/github/stars/insistence/RuoYi-Vue3-FastAPI?style=social"></a> |
|||
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/badge/RuoYiVue3FastAPI-v1.0.0-brightgreen.svg"></a> |
|||
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> |
|||
<img src="https://img.shields.io/badge/python-≥3.8-blue"> |
|||
<img src="https://img.shields.io/badge/MySQL-≥5.7-blue"> |
|||
</p> |
|||
|
|||
#### 介绍 |
|||
{**以下是 Gitee 平台说明,您可以替换此简介** |
|||
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 |
|||
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} |
|||
## 平台简介 |
|||
|
|||
#### 软件架构 |
|||
软件架构说明 |
|||
RuoYi-Vue-FastAPI是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 |
|||
|
|||
* 前端采用Vue、Element Plus,基于<u>[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3)</u>前端项目修改。 |
|||
* 后端采用FastAPI、sqlalchemy、MySQL、Redis、OAuth2 & Jwt。 |
|||
* 权限认证使用OAuth2 & Jwt,支持多终端认证系统。 |
|||
* 支持加载动态权限菜单,多方式轻松权限控制。 |
|||
* Vue2版本: |
|||
- Gitte仓库地址:https://gitee.com/insistence2022/RuoYi-Vue-FastAPI。 |
|||
- GitHub仓库地址:https://github.com/insistence/RuoYi-Vue-FastAPI。 |
|||
* 纯Python版本: |
|||
- Gitte仓库地址:https://gitee.com/insistence2022/dash-fastapi-admin。 |
|||
- GitHub仓库地址:https://github.com/insistence/Dash-FastAPI-Admin。 |
|||
* 特别鸣谢:<u>[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3)</u>。 |
|||
|
|||
#### 安装教程 |
|||
## 内置功能 |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 |
|||
2. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 |
|||
3. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 |
|||
4. 部门管理:配置系统组织机构(公司、部门、小组)。 |
|||
5. 岗位管理:配置系统用户所属担任职务。 |
|||
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 |
|||
7. 参数管理:对系统动态配置常用参数。 |
|||
8. 通知公告:系统通知公告信息发布维护。 |
|||
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 |
|||
10. 登录日志:系统登录日志记录查询包含登录异常。 |
|||
11. 在线用户:当前系统中活跃用户状态监控。 |
|||
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 |
|||
13. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 |
|||
14. 缓存监控:对系统的缓存信息查询,命令统计等。 |
|||
15. 系统接口:根据业务代码自动生成相关的api接口文档。 |
|||
|
|||
#### 使用说明 |
|||
## 演示图 |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
<table> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/login.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dashboard.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/user.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/role.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/menu.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dept.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/post.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dict.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/config.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/notice.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/operLog.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/loginLog.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/online.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/job.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/server.png"/></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/cache.png"/></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/cacheList.png"></td> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/api.png"></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/profile.png"/></td> |
|||
</tr> |
|||
</table> |
|||
|
|||
#### 参与贡献 |
|||
## 在线体验 |
|||
- *账号:admin* |
|||
- *密码:admin123* |
|||
- 演示地址:<a href="https://vfadmin.insistence.tech">vfadmin管理系统<a> |
|||
|
|||
1. Fork 本仓库 |
|||
2. 新建 Feat_xxx 分支 |
|||
3. 提交代码 |
|||
4. 新建 Pull Request |
|||
## 项目开发及发布相关 |
|||
|
|||
### 开发 |
|||
|
|||
#### 特技 |
|||
```bash |
|||
# 克隆项目 |
|||
git clone https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git |
|||
|
|||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md |
|||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) |
|||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 |
|||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 |
|||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) |
|||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
|||
# 进入项目根目录 |
|||
cd RuoYi-Vue3-FastAPI |
|||
``` |
|||
|
|||
#### 前端 |
|||
```bash |
|||
# 进入前端目录 |
|||
cd ruoyi-fastapi-frontend |
|||
|
|||
# 安装依赖 |
|||
npm install 或 yarn --registry=https://registry.npmmirror.com |
|||
|
|||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 |
|||
npm install --registry=https://registry.npmmirror.com |
|||
|
|||
# 启动服务 |
|||
npm run dev 或 yarn dev |
|||
``` |
|||
|
|||
#### 后端 |
|||
```bash |
|||
# 进入后端目录 |
|||
cd ruoyi-fastapi-backend |
|||
|
|||
# 安装项目依赖环境 |
|||
pip3 install -r requirements.txt |
|||
|
|||
# 配置环境 |
|||
在.env.dev文件中配置开发环境的数据库和redis |
|||
|
|||
# 运行sql文件 |
|||
1.新建数据库ruoyi-fastapi(默认,可修改) |
|||
2.使用命令或数据库连接工具运行sql文件夹下的ruoyi-fastapi.sql |
|||
|
|||
# 运行后端 |
|||
python3 app.py --env=dev |
|||
``` |
|||
|
|||
#### 访问 |
|||
```bash |
|||
# 默认账号密码 |
|||
账号:admin |
|||
密码:admin123 |
|||
|
|||
# 浏览器访问 |
|||
地址:http://localhost:80 |
|||
``` |
|||
|
|||
### 发布 |
|||
|
|||
#### 前端 |
|||
```bash |
|||
# 构建测试环境 |
|||
npm run build:stage 或 yarn build:stage |
|||
|
|||
# 构建生产环境 |
|||
npm run build:prod 或 yarn build:prod |
|||
``` |
|||
|
|||
#### 后端 |
|||
```bash |
|||
# 配置环境 |
|||
在.env.prod文件中配置生产环境的数据库和redis |
|||
|
|||
# 运行后端 |
|||
python3 app.py --env=prod |
|||
``` |
|||
|
|||
## 交流与赞助 |
|||
如果有对本项目及FastAPI感兴趣的朋友,欢迎加入知识星球一起交流学习,让我们一起变得更强。如果你觉得这个项目帮助到了你,你可以请作者喝杯咖啡表示鼓励☕。扫描下面微信二维码添加微信备注VF-Admin即可进群。 |
|||
<table> |
|||
<tr> |
|||
<td><img alt="zsxq" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/zsxq.jpg"></td> |
|||
<td><img alt="zanzhu" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/zanzhu.jpg"></td> |
|||
</tr> |
|||
<tr> |
|||
<td><img alt="wxcode" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/wxcode.jpg"></td> |
|||
</tr> |
|||
</table> |
@ -0,0 +1,50 @@ |
|||
# -------- 应用配置 -------- |
|||
# 应用运行环境 |
|||
APP_ENV = 'dev' |
|||
# 应用名称 |
|||
APP_NAME = 'RuoYi-FasAPI' |
|||
# 应用代理路径 |
|||
APP_ROOT_PATH = '/dev-api' |
|||
# 应用主机 |
|||
APP_HOST = '0.0.0.0' |
|||
# 应用端口 |
|||
APP_PORT = 9099 |
|||
# 应用版本 |
|||
APP_VERSION= '1.0.0' |
|||
# 应用是否开启热重载 |
|||
APP_RELOAD = true |
|||
|
|||
# -------- Jwt配置 -------- |
|||
# Jwt秘钥 |
|||
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' |
|||
# Jwt算法 |
|||
JWT_ALGORITHM = 'HS256' |
|||
# 令牌过期时间 |
|||
JWT_EXPIRE_MINUTES = 1440 |
|||
# redis中令牌过期时间 |
|||
JWT_REDIS_EXPIRE_MINUTES = 30 |
|||
|
|||
|
|||
# -------- 数据库配置 -------- |
|||
# 数据库主机 |
|||
DB_HOST = '127.0.0.1' |
|||
# 数据库端口 |
|||
DB_PORT = 3306 |
|||
# 数据库用户名 |
|||
DB_USERNAME = 'root' |
|||
# 数据库密码 |
|||
DB_PASSWORD = 'mysqlroot' |
|||
# 数据库名称 |
|||
DB_DATABASE = 'ruoyi-fastapi' |
|||
|
|||
# -------- Redis配置 -------- |
|||
# Redis主机 |
|||
REDIS_HOST = '127.0.0.1' |
|||
# Redis端口 |
|||
REDIS_PORT = 6379 |
|||
# Redis用户名 |
|||
REDIS_USERNAME = '' |
|||
# Redis密码 |
|||
REDIS_PASSWORD = '' |
|||
# Redis数据库 |
|||
REDIS_DATABASE = 2 |
@ -0,0 +1,50 @@ |
|||
# -------- 应用配置 -------- |
|||
# 应用运行环境 |
|||
APP_ENV = 'prod' |
|||
# 应用名称 |
|||
APP_NAME = 'RuoYi-FasAPI' |
|||
# 应用代理路径 |
|||
APP_ROOT_PATH = '/prod-api' |
|||
# 应用主机 |
|||
APP_HOST = '0.0.0.0' |
|||
# 应用端口 |
|||
APP_PORT = 9099 |
|||
# 应用版本 |
|||
APP_VERSION= '1.0.0' |
|||
# 应用是否开启热重载 |
|||
APP_RELOAD = false |
|||
|
|||
# -------- Jwt配置 -------- |
|||
# Jwt秘钥 |
|||
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' |
|||
# Jwt算法 |
|||
JWT_ALGORITHM = 'HS256' |
|||
# 令牌过期时间 |
|||
JWT_EXPIRE_MINUTES = 1440 |
|||
# redis中令牌过期时间 |
|||
JWT_REDIS_EXPIRE_MINUTES = 30 |
|||
|
|||
|
|||
# -------- 数据库配置 -------- |
|||
# 数据库主机 |
|||
DB_HOST = '127.0.0.1' |
|||
# 数据库端口 |
|||
DB_PORT = 3306 |
|||
# 数据库用户名 |
|||
DB_USERNAME = 'root' |
|||
# 数据库密码 |
|||
DB_PASSWORD = 'root' |
|||
# 数据库名称 |
|||
DB_DATABASE = 'ruoyi-fastapi' |
|||
|
|||
# -------- Redis配置 -------- |
|||
# Redis主机 |
|||
REDIS_HOST = '127.0.0.1' |
|||
# Redis端口 |
|||
REDIS_PORT = 6379 |
|||
# Redis用户名 |
|||
REDIS_USERNAME = '' |
|||
# Redis密码 |
|||
REDIS_PASSWORD = '' |
|||
# Redis数据库 |
|||
REDIS_DATABASE = 2 |
@ -1,119 +1,12 @@ |
|||
from fastapi import FastAPI, Request |
|||
from fastapi.exceptions import HTTPException |
|||
from fastapi.middleware.cors import CORSMiddleware |
|||
from fastapi.staticfiles import StaticFiles |
|||
import uvicorn |
|||
from contextlib import asynccontextmanager |
|||
from module_admin.controller.login_controller import loginController |
|||
from module_admin.controller.captcha_controller import captchaController |
|||
from module_admin.controller.user_controller import userController |
|||
from module_admin.controller.menu_controller import menuController |
|||
from module_admin.controller.dept_controller import deptController |
|||
from module_admin.controller.role_controller import roleController |
|||
from module_admin.controller.post_controler import postController |
|||
from module_admin.controller.dict_controller import dictController |
|||
from module_admin.controller.config_controller import configController |
|||
from module_admin.controller.notice_controller import noticeController |
|||
from module_admin.controller.log_controller import logController |
|||
from module_admin.controller.online_controller import onlineController |
|||
from module_admin.controller.job_controller import jobController |
|||
from module_admin.controller.server_controller import serverController |
|||
from module_admin.controller.cache_controller import cacheController |
|||
from module_admin.controller.common_controller import commonController |
|||
from config.env import UploadConfig |
|||
from config.get_redis import RedisUtil |
|||
from config.get_db import init_create_table |
|||
from config.get_scheduler import SchedulerUtil |
|||
from utils.response_util import * |
|||
from utils.log_util import logger |
|||
from utils.common_util import worship |
|||
from server import app, AppConfig |
|||
|
|||
|
|||
@asynccontextmanager |
|||
async def lifespan(app: FastAPI): |
|||
logger.info("RuoYi-FastAPI开始启动") |
|||
worship() |
|||
await init_create_table() |
|||
app.state.redis = await RedisUtil.create_redis_pool() |
|||
await RedisUtil.init_sys_dict(app.state.redis) |
|||
await RedisUtil.init_sys_config(app.state.redis) |
|||
await SchedulerUtil.init_system_scheduler() |
|||
logger.info("RuoYi-FastAPI启动成功") |
|||
yield |
|||
await RedisUtil.close_redis_pool(app) |
|||
await SchedulerUtil.close_system_scheduler() |
|||
|
|||
|
|||
app = FastAPI( |
|||
title='RuoYi-FastAPI', |
|||
description='RuoYi-FastAPI接口文档', |
|||
version='1.0.0', |
|||
lifespan=lifespan |
|||
) |
|||
|
|||
# 前端页面url |
|||
origins = [ |
|||
"http://localhost:81", |
|||
"http://127.0.0.1:81", |
|||
] |
|||
|
|||
# 后台api允许跨域 |
|||
app.add_middleware( |
|||
CORSMiddleware, |
|||
allow_origins=origins, |
|||
allow_credentials=True, |
|||
allow_methods=["*"], |
|||
allow_headers=["*"], |
|||
) |
|||
|
|||
# 实例化UploadConfig,确保应用启动时上传目录存在 |
|||
upload_config = UploadConfig() |
|||
|
|||
# 挂载静态文件路径 |
|||
app.mount(f"{upload_config.UPLOAD_PREFIX}", StaticFiles(directory=f"{upload_config.UPLOAD_PATH}"), name="profile") |
|||
|
|||
|
|||
# 自定义token检验异常 |
|||
@app.exception_handler(AuthException) |
|||
async def auth_exception_handler(request: Request, exc: AuthException): |
|||
return ResponseUtil.unauthorized(data=exc.data, msg=exc.message) |
|||
|
|||
|
|||
# 自定义权限检验异常 |
|||
@app.exception_handler(PermissionException) |
|||
async def permission_exception_handler(request: Request, exc: PermissionException): |
|||
return ResponseUtil.forbidden(data=exc.data, msg=exc.message) |
|||
|
|||
|
|||
@app.exception_handler(HTTPException) |
|||
async def http_exception_handler(request: Request, exc: HTTPException): |
|||
return JSONResponse( |
|||
content=jsonable_encoder({"message": exc.detail, "code": exc.status_code}), |
|||
status_code=exc.status_code |
|||
) |
|||
|
|||
|
|||
controller_list = [ |
|||
{'router': loginController, 'tags': ['登录模块']}, |
|||
{'router': captchaController, 'tags': ['验证码模块']}, |
|||
{'router': userController, 'tags': ['系统管理-用户管理']}, |
|||
{'router': roleController, 'tags': ['系统管理-角色管理']}, |
|||
{'router': menuController, 'tags': ['系统管理-菜单管理']}, |
|||
{'router': deptController, 'tags': ['系统管理-部门管理']}, |
|||
{'router': postController, 'tags': ['系统管理-岗位管理']}, |
|||
{'router': dictController, 'tags': ['系统管理-字典管理']}, |
|||
{'router': configController, 'tags': ['系统管理-参数管理']}, |
|||
{'router': noticeController, 'tags': ['系统管理-通知公告管理']}, |
|||
{'router': logController, 'tags': ['系统管理-日志管理']}, |
|||
{'router': onlineController, 'tags': ['系统监控-在线用户']}, |
|||
{'router': jobController, 'tags': ['系统监控-定时任务']}, |
|||
{'router': serverController, 'tags': ['系统监控-菜单管理']}, |
|||
{'router': cacheController, 'tags': ['系统监控-缓存监控']}, |
|||
{'router': commonController, 'tags': ['通用模块']} |
|||
] |
|||
|
|||
for controller in controller_list: |
|||
app.include_router(router=controller.get('router'), tags=controller.get('tags')) |
|||
|
|||
if __name__ == '__main__': |
|||
uvicorn.run(app='app:app', host="0.0.0.0", port=9099, root_path='/dev-api', reload=True) |
|||
uvicorn.run( |
|||
app='app:app', |
|||
host=AppConfig.app_host, |
|||
port=AppConfig.app_port, |
|||
root_path=AppConfig.app_root_path, |
|||
reload=AppConfig.app_reload |
|||
) |
|||
|
Before Width: | Height: | Size: 79 KiB |
@ -0,0 +1,28 @@ |
|||
class LoginException(Exception): |
|||
""" |
|||
自定义登录异常LoginException |
|||
""" |
|||
|
|||
def __init__(self, data: str = None, message: str = None): |
|||
self.data = data |
|||
self.message = message |
|||
|
|||
|
|||
class AuthException(Exception): |
|||
""" |
|||
自定义令牌异常AuthException |
|||
""" |
|||
|
|||
def __init__(self, data: str = None, message: str = None): |
|||
self.data = data |
|||
self.message = message |
|||
|
|||
|
|||
class PermissionException(Exception): |
|||
""" |
|||
自定义权限异常PermissionException |
|||
""" |
|||
|
|||
def __init__(self, data: str = None, message: str = None): |
|||
self.data = data |
|||
self.message = message |
@ -0,0 +1,27 @@ |
|||
from fastapi import FastAPI, Request |
|||
from fastapi.exceptions import HTTPException |
|||
from exceptions.exception import AuthException, PermissionException |
|||
from utils.response_util import ResponseUtil, JSONResponse, jsonable_encoder |
|||
|
|||
|
|||
def handle_exception(app: FastAPI): |
|||
""" |
|||
全局异常处理 |
|||
""" |
|||
# 自定义token检验异常 |
|||
@app.exception_handler(AuthException) |
|||
async def auth_exception_handler(request: Request, exc: AuthException): |
|||
return ResponseUtil.unauthorized(data=exc.data, msg=exc.message) |
|||
|
|||
# 自定义权限检验异常 |
|||
@app.exception_handler(PermissionException) |
|||
async def permission_exception_handler(request: Request, exc: PermissionException): |
|||
return ResponseUtil.forbidden(data=exc.data, msg=exc.message) |
|||
|
|||
# 处理其他http请求异常 |
|||
@app.exception_handler(HTTPException) |
|||
async def http_exception_handler(request: Request, exc: HTTPException): |
|||
return JSONResponse( |
|||
content=jsonable_encoder({"code": exc.status_code, "msg": exc.detail}), |
|||
status_code=exc.status_code |
|||
) |
@ -0,0 +1,19 @@ |
|||
from fastapi import FastAPI |
|||
from fastapi.middleware.cors import CORSMiddleware |
|||
|
|||
|
|||
def add_cors_middleware(app: FastAPI): |
|||
# 前端页面url |
|||
origins = [ |
|||
"http://localhost:80", |
|||
"http://127.0.0.1:80", |
|||
] |
|||
|
|||
# 后台api允许跨域 |
|||
app.add_middleware( |
|||
CORSMiddleware, |
|||
allow_origins=origins, |
|||
allow_credentials=True, |
|||
allow_methods=["*"], |
|||
allow_headers=["*"], |
|||
) |
@ -0,0 +1,10 @@ |
|||
from fastapi import FastAPI |
|||
from middlewares.cors_middleware import add_cors_middleware |
|||
|
|||
|
|||
def handle_middleware(app: FastAPI): |
|||
""" |
|||
全局中间件处理 |
|||
""" |
|||
# 加载跨域中间件 |
|||
add_cors_middleware(app) |
@ -0,0 +1,15 @@ |
|||
APScheduler==3.10.4 |
|||
DateTime==5.4 |
|||
fastapi[all]==0.109.0 |
|||
loguru==0.7.2 |
|||
openpyxl==3.1.2 |
|||
pandas==2.1.4 |
|||
passlib[bcrypt]==1.7.4 |
|||
Pillow==10.2.0 |
|||
psutil==5.9.7 |
|||
PyMySQL==1.1.0 |
|||
python-jose[cryptography]==3.3.0 |
|||
redis==5.0.1 |
|||
requests==2.31.0 |
|||
SQLAlchemy==2.0.25 |
|||
user-agents==2.2.0 |
@ -0,0 +1,83 @@ |
|||
from fastapi import FastAPI |
|||
from contextlib import asynccontextmanager |
|||
from sub_applications.handle import handle_sub_applications |
|||
from middlewares.handle import handle_middleware |
|||
from exceptions.handle import handle_exception |
|||
from module_admin.controller.login_controller import loginController |
|||
from module_admin.controller.captcha_controller import captchaController |
|||
from module_admin.controller.user_controller import userController |
|||
from module_admin.controller.menu_controller import menuController |
|||
from module_admin.controller.dept_controller import deptController |
|||
from module_admin.controller.role_controller import roleController |
|||
from module_admin.controller.post_controler import postController |
|||
from module_admin.controller.dict_controller import dictController |
|||
from module_admin.controller.config_controller import configController |
|||
from module_admin.controller.notice_controller import noticeController |
|||
from module_admin.controller.log_controller import logController |
|||
from module_admin.controller.online_controller import onlineController |
|||
from module_admin.controller.job_controller import jobController |
|||
from module_admin.controller.server_controller import serverController |
|||
from module_admin.controller.cache_controller import cacheController |
|||
from module_admin.controller.common_controller import commonController |
|||
from config.env import AppConfig |
|||
from config.get_redis import RedisUtil |
|||
from config.get_db import init_create_table |
|||
from config.get_scheduler import SchedulerUtil |
|||
from utils.log_util import logger |
|||
from utils.common_util import worship |
|||
|
|||
|
|||
# 生命周期事件 |
|||
@asynccontextmanager |
|||
async def lifespan(app: FastAPI): |
|||
logger.info(f"{AppConfig.app_name}开始启动") |
|||
worship() |
|||
await init_create_table() |
|||
app.state.redis = await RedisUtil.create_redis_pool() |
|||
await RedisUtil.init_sys_dict(app.state.redis) |
|||
await RedisUtil.init_sys_config(app.state.redis) |
|||
await SchedulerUtil.init_system_scheduler() |
|||
logger.info(f"{AppConfig.app_name}启动成功") |
|||
yield |
|||
await RedisUtil.close_redis_pool(app) |
|||
await SchedulerUtil.close_system_scheduler() |
|||
|
|||
|
|||
# 初始化FastAPI对象 |
|||
app = FastAPI( |
|||
title=AppConfig.app_name, |
|||
description=f'{AppConfig.app_name}接口文档', |
|||
version=AppConfig.app_version, |
|||
lifespan=lifespan |
|||
) |
|||
|
|||
# 挂载子应用 |
|||
handle_sub_applications(app) |
|||
# 加载中间件处理方法 |
|||
handle_middleware(app) |
|||
# 加载全局异常处理方法 |
|||
handle_exception(app) |
|||
|
|||
|
|||
# 加载路由列表 |
|||
controller_list = [ |
|||
{'router': loginController, 'tags': ['登录模块']}, |
|||
{'router': captchaController, 'tags': ['验证码模块']}, |
|||
{'router': userController, 'tags': ['系统管理-用户管理']}, |
|||
{'router': roleController, 'tags': ['系统管理-角色管理']}, |
|||
{'router': menuController, 'tags': ['系统管理-菜单管理']}, |
|||
{'router': deptController, 'tags': ['系统管理-部门管理']}, |
|||
{'router': postController, 'tags': ['系统管理-岗位管理']}, |
|||
{'router': dictController, 'tags': ['系统管理-字典管理']}, |
|||
{'router': configController, 'tags': ['系统管理-参数管理']}, |
|||
{'router': noticeController, 'tags': ['系统管理-通知公告管理']}, |
|||
{'router': logController, 'tags': ['系统管理-日志管理']}, |
|||
{'router': onlineController, 'tags': ['系统监控-在线用户']}, |
|||
{'router': jobController, 'tags': ['系统监控-定时任务']}, |
|||
{'router': serverController, 'tags': ['系统监控-菜单管理']}, |
|||
{'router': cacheController, 'tags': ['系统监控-缓存监控']}, |
|||
{'router': commonController, 'tags': ['通用模块']} |
|||
] |
|||
|
|||
for controller in controller_list: |
|||
app.include_router(router=controller.get('router'), tags=controller.get('tags')) |
@ -0,0 +1,10 @@ |
|||
from fastapi import FastAPI |
|||
from sub_applications.staticfiles import mount_staticfiles |
|||
|
|||
|
|||
def handle_sub_applications(app: FastAPI): |
|||
""" |
|||
全局处理子应用挂载 |
|||
""" |
|||
# 挂载静态文件 |
|||
mount_staticfiles(app) |
@ -0,0 +1,10 @@ |
|||
from fastapi import FastAPI |
|||
from fastapi.staticfiles import StaticFiles |
|||
from config.env import UploadConfig |
|||
|
|||
|
|||
def mount_staticfiles(app: FastAPI): |
|||
""" |
|||
挂载静态文件 |
|||
""" |
|||
app.mount(f"{UploadConfig.UPLOAD_PREFIX}", StaticFiles(directory=f"{UploadConfig.UPLOAD_PATH}"), name="profile") |
@ -0,0 +1,68 @@ |
|||
<template> |
|||
<div class="linkGroup"> |
|||
<a v-for="(item, index) in links" :key="index" :href="item.href"> |
|||
{{ item.title }} |
|||
</a> |
|||
<a-button size="small" type="primary" ghost> |
|||
<PlusOutlined /> 添加 |
|||
</a-button> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { Button } from "ant-design-vue"; |
|||
|
|||
export default { |
|||
components: { |
|||
AButton: Button, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<script setup> |
|||
import { PlusOutlined } from "@ant-design/icons-vue"; |
|||
|
|||
const links = [ |
|||
{ |
|||
title: "操作一", |
|||
href: "", |
|||
}, |
|||
{ |
|||
title: "操作二", |
|||
href: "", |
|||
}, |
|||
{ |
|||
title: "操作三", |
|||
href: "", |
|||
}, |
|||
{ |
|||
title: "操作四", |
|||
href: "", |
|||
}, |
|||
{ |
|||
title: "操作五", |
|||
href: "", |
|||
}, |
|||
{ |
|||
title: "操作六", |
|||
href: "", |
|||
}, |
|||
]; |
|||
</script> |
|||
|
|||
<style scoped lang="less"> |
|||
.linkGroup { |
|||
padding: 20px 0 8px 24px; |
|||
font-size: 0; |
|||
& > a { |
|||
display: inline-block; |
|||
width: 25%; |
|||
margin-bottom: 13px; |
|||
color: rgba(0, 0, 0, 0.65); |
|||
font-size: 14px; |
|||
&:hover { |
|||
color: #1890ff; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,738 @@ |
|||
<template> |
|||
<div> |
|||
<div class="pageHeaderContent"> |
|||
<div class="avatar"> |
|||
<a-avatar size="large" :src="currentUser.avatar" /> |
|||
</div> |
|||
<div class="content"> |
|||
<div class="contentTitle"> |
|||
早安, |
|||
{{ currentUser.name }} |
|||
,祝你开心每一天! |
|||
</div> |
|||
<div>{{ currentUser.title }} |{{ currentUser.group }}</div> |
|||
</div> |
|||
<div class="extraContent"> |
|||
<div class="statItem"> |
|||
<a-statistic title="项目数" :value="56" /> |
|||
</div> |
|||
<div class="statItem"> |
|||
<a-statistic title="团队内排名" :value="8" suffix="/ 24" /> |
|||
</div> |
|||
<div class="statItem"> |
|||
<a-statistic title="项目访问" :value="2223" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div style="padding: 10px"> |
|||
<a-row :gutter="24"> |
|||
<a-col :xl="16" :lg="24" :md="24" :sm="24" :xs="24"> |
|||
<a-card |
|||
class="projectList" |
|||
:style="{ marginBottom: '24px' }" |
|||
title="进行中的项目" |
|||
:bordered="false" |
|||
:loading="false" |
|||
:body-style="{ padding: 0 }" |
|||
> |
|||
<template #extra> |
|||
<a href=""> <span style="color: #1890ff">全部项目</span> </a> |
|||
</template> |
|||
<a-card-grid |
|||
v-for="item in projectNotice" |
|||
:key="item.id" |
|||
class="projectGrid" |
|||
> |
|||
<a-card |
|||
:body-style="{ padding: 0 }" |
|||
style="box-shadow: none" |
|||
:bordered="false" |
|||
> |
|||
<a-card-meta :description="item.description" class="w-full"> |
|||
<template #title> |
|||
<div class="cardTitle"> |
|||
<a-avatar size="small" :src="item.logo" /> |
|||
<a :href="item.href"> |
|||
{{ item.title }} |
|||
</a> |
|||
</div> |
|||
</template> |
|||
</a-card-meta> |
|||
<div class="projectItemContent"> |
|||
<a :href="item.memberLink"> |
|||
{{ item.member || "" }} |
|||
</a> |
|||
<span class="datetime" ml-2 :title="item.updatedAt"> |
|||
{{ item.updatedAt }} |
|||
</span> |
|||
</div> |
|||
</a-card> |
|||
</a-card-grid> |
|||
</a-card> |
|||
<a-card |
|||
:body-style="{ padding: 0 }" |
|||
:bordered="false" |
|||
class="activeCard" |
|||
title="动态" |
|||
:loading="false" |
|||
> |
|||
<a-list :data-source="activities" class="activitiesList"> |
|||
<template #renderItem="{ item }"> |
|||
<a-list-item :key="item.id"> |
|||
<a-list-item-meta> |
|||
<template #title> |
|||
<span> |
|||
<a class="username">{{ item.user.name }}</a |
|||
> |
|||
<span class="event"> |
|||
<span>{{ item.template1 }}</span |
|||
> |
|||
<a href="" style="color: #1890ff"> |
|||
{{ item?.group?.name }} </a |
|||
> <span>{{ item.template2 }}</span |
|||
> |
|||
<a href="" style="color: #1890ff"> |
|||
{{ item?.project?.name }} |
|||
</a> |
|||
</span> |
|||
</span> |
|||
</template> |
|||
<template #avatar> |
|||
<a-avatar :src="item.user.avatar" /> |
|||
</template> |
|||
<template #description> |
|||
<span class="datetime" :title="item.updatedAt"> |
|||
{{ item.updatedAt }} |
|||
</span> |
|||
</template> |
|||
</a-list-item-meta> |
|||
</a-list-item> |
|||
</template> |
|||
</a-list> |
|||
</a-card> |
|||
</a-col> |
|||
<a-col :xl="8" :lg="24" :md="24" :sm="24" :xs="24"> |
|||
<a-card |
|||
:style="{ marginBottom: '24px' }" |
|||
title="快速开始 / 便捷导航" |
|||
:bordered="false" |
|||
:body-style="{ padding: 0 }" |
|||
> |
|||
<EditableLinkGroup /> |
|||
</a-card> |
|||
<a-card |
|||
:style="{ marginBottom: '24px' }" |
|||
:bordered="false" |
|||
title="XX 指数" |
|||
> |
|||
<div class="chart"> |
|||
<div ref="radarContainer" /> |
|||
</div> |
|||
</a-card> |
|||
<a-card |
|||
:body-style="{ paddingTop: '12px', paddingBottom: '12px' }" |
|||
:bordered="false" |
|||
title="团队" |
|||
> |
|||
<div class="members"> |
|||
<a-row :gutter="48"> |
|||
<a-col |
|||
v-for="item in projectNotice" |
|||
:key="`members-item-${item.id}`" |
|||
:span="12" |
|||
> |
|||
<a :href="item.href"> |
|||
<a-avatar :src="item.logo" size="small" /> |
|||
<span class="member">{{ item.member }}</span> |
|||
</a> |
|||
</a-col> |
|||
</a-row> |
|||
</div> |
|||
</a-card> |
|||
</a-col> |
|||
</a-row> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { |
|||
Statistic, |
|||
Row, |
|||
Col, |
|||
Card, |
|||
CardGrid, |
|||
CardMeta, |
|||
List, |
|||
ListItem, |
|||
ListItemMeta, |
|||
Avatar, |
|||
} from "ant-design-vue"; |
|||
import 'ant-design-vue/dist/reset.css'; |
|||
|
|||
export default { |
|||
components: { |
|||
AStatistic: Statistic, |
|||
ARow: Row, |
|||
ACol: Col, |
|||
ACard: Card, |
|||
ACardGrid: CardGrid, |
|||
ACardMeta: CardMeta, |
|||
AList: List, |
|||
AListItem: ListItem, |
|||
AListItemMeta: ListItemMeta, |
|||
AAvatar: Avatar, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
|
|||
<script setup> |
|||
import { Radar } from "@antv/g2plot"; |
|||
import EditableLinkGroup from "./editable-link-group.vue"; |
|||
|
|||
defineOptions({ |
|||
name: "DashBoard", |
|||
}); |
|||
|
|||
const currentUser = { |
|||
avatar: "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", |
|||
name: "吴彦祖", |
|||
userid: "00000001", |
|||
email: "antdesign@alipay.com", |
|||
signature: "海纳百川,有容乃大", |
|||
title: "交互专家", |
|||
group: "蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED", |
|||
}; |
|||
|
|||
const projectNotice = [ |
|||
{ |
|||
id: "xxx1", |
|||
title: "Alipay", |
|||
logo: "https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png", |
|||
description: "那是一种内在的东西,他们到达不了,也无法触及的", |
|||
updatedAt: "几秒前", |
|||
member: "科学搬砖组", |
|||
href: "", |
|||
memberLink: "", |
|||
}, |
|||
{ |
|||
id: "xxx2", |
|||
title: "Angular", |
|||
logo: "https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png", |
|||
description: "希望是一个好东西,也许是最好的,好东西是不会消亡的", |
|||
updatedAt: "6 年前", |
|||
member: "全组都是吴彦祖", |
|||
href: "", |
|||
memberLink: "", |
|||
}, |
|||
{ |
|||
id: "xxx3", |
|||
title: "Ant Design", |
|||
logo: "https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png", |
|||
description: "城镇中有那么多的酒馆,她却偏偏走进了我的酒馆", |
|||
updatedAt: "几秒前", |
|||
member: "中二少女团", |
|||
href: "", |
|||
memberLink: "", |
|||
}, |
|||
{ |
|||
id: "xxx4", |
|||
title: "Ant Design Pro", |
|||
logo: "https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png", |
|||
description: "那时候我只会想自己想要什么,从不想自己拥有什么", |
|||
updatedAt: "6 年前", |
|||
member: "程序员日常", |
|||
href: "", |
|||
memberLink: "", |
|||
}, |
|||
{ |
|||
id: "xxx5", |
|||
title: "Bootstrap", |
|||
logo: "https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png", |
|||
description: |
|||
"凛冬将至", |
|||
updatedAt: "6 年前", |
|||
member: "高逼格设计天团", |
|||
href: "", |
|||
memberLink: "", |
|||
}, |
|||
{ |
|||
id: "xxx6", |
|||
title: "React", |
|||
logo: "https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png", |
|||
description: "生命就像一盒巧克力,结果往往出人意料", |
|||
updatedAt: "6 年前", |
|||
member: "骗你来学计算机", |
|||
href: "", |
|||
memberLink: "", |
|||
}, |
|||
]; |
|||
|
|||
const activities = [ |
|||
{ |
|||
id: "trend-1", |
|||
updatedAt: "几秒前", |
|||
user: { |
|||
name: "曲丽丽", |
|||
avatar: |
|||
"https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", |
|||
}, |
|||
group: { |
|||
name: "高逼格设计天团", |
|||
link: "http://github.com/", |
|||
}, |
|||
project: { |
|||
name: "六月迭代", |
|||
link: "http://github.com/", |
|||
}, |
|||
template1: "在", |
|||
template2: "新建项目", |
|||
}, |
|||
{ |
|||
id: "trend-2", |
|||
updatedAt: "几秒前", |
|||
user: { |
|||
name: "付小小", |
|||
avatar: |
|||
"https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png", |
|||
}, |
|||
group: { |
|||
name: "高逼格设计天团", |
|||
link: "http://github.com/", |
|||
}, |
|||
project: { |
|||
name: "六月迭代", |
|||
link: "http://github.com/", |
|||
}, |
|||
template1: "在", |
|||
template2: "新建项目", |
|||
}, |
|||
{ |
|||
id: "trend-3", |
|||
updatedAt: "几秒前", |
|||
user: { |
|||
name: "林东东", |
|||
avatar: |
|||
"https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png", |
|||
}, |
|||
group: { |
|||
name: "中二少女团", |
|||
link: "http://github.com/", |
|||
}, |
|||
project: { |
|||
name: "六月迭代", |
|||
link: "http://github.com/", |
|||
}, |
|||
template1: "在", |
|||
template2: "新建项目", |
|||
}, |
|||
{ |
|||
id: "trend-4", |
|||
updatedAt: "几秒前", |
|||
user: { |
|||
name: "周星星", |
|||
avatar: |
|||
"https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png", |
|||
}, |
|||
group: { |
|||
name: "5 月日常迭代", |
|||
link: "http://github.com/", |
|||
}, |
|||
template1: "将", |
|||
template2: "更新至已发布状态", |
|||
}, |
|||
{ |
|||
id: "trend-5", |
|||
updatedAt: "几秒前", |
|||
user: { |
|||
name: "朱偏右", |
|||
avatar: |
|||
"https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png", |
|||
}, |
|||
group: { |
|||
name: "工程效能", |
|||
link: "http://github.com/", |
|||
}, |
|||
project: { |
|||
name: "留言", |
|||
link: "http://github.com/", |
|||
}, |
|||
template1: "在", |
|||
template2: "发布了", |
|||
}, |
|||
{ |
|||
id: "trend-6", |
|||
updatedAt: "几秒前", |
|||
user: { |
|||
name: "乐哥", |
|||
avatar: |
|||
"https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png", |
|||
}, |
|||
group: { |
|||
name: "程序员日常", |
|||
link: "http://github.com/", |
|||
}, |
|||
project: { |
|||
name: "品牌迭代", |
|||
link: "http://github.com/", |
|||
}, |
|||
template1: "在", |
|||
template2: "新建项目", |
|||
}, |
|||
]; |
|||
|
|||
const radarContainer = ref(); |
|||
const radarData = [ |
|||
{ |
|||
name: "个人", |
|||
label: "引用", |
|||
value: 10, |
|||
}, |
|||
{ |
|||
name: "个人", |
|||
label: "口碑", |
|||
value: 8, |
|||
}, |
|||
{ |
|||
name: "个人", |
|||
label: "产量", |
|||
value: 4, |
|||
}, |
|||
{ |
|||
name: "个人", |
|||
label: "贡献", |
|||
value: 5, |
|||
}, |
|||
{ |
|||
name: "个人", |
|||
label: "热度", |
|||
value: 7, |
|||
}, |
|||
{ |
|||
name: "团队", |
|||
label: "引用", |
|||
value: 3, |
|||
}, |
|||
{ |
|||
name: "团队", |
|||
label: "口碑", |
|||
value: 9, |
|||
}, |
|||
{ |
|||
name: "团队", |
|||
label: "产量", |
|||
value: 6, |
|||
}, |
|||
{ |
|||
name: "团队", |
|||
label: "贡献", |
|||
value: 3, |
|||
}, |
|||
{ |
|||
name: "团队", |
|||
label: "热度", |
|||
value: 1, |
|||
}, |
|||
{ |
|||
name: "部门", |
|||
label: "引用", |
|||
value: 4, |
|||
}, |
|||
{ |
|||
name: "部门", |
|||
label: "口碑", |
|||
value: 1, |
|||
}, |
|||
{ |
|||
name: "部门", |
|||
label: "产量", |
|||
value: 6, |
|||
}, |
|||
{ |
|||
name: "部门", |
|||
label: "贡献", |
|||
value: 5, |
|||
}, |
|||
{ |
|||
name: "部门", |
|||
label: "热度", |
|||
value: 7, |
|||
}, |
|||
]; |
|||
let radar; |
|||
onMounted(() => { |
|||
radar = new Radar(radarContainer.value, { |
|||
data: radarData, |
|||
xField: "label", |
|||
yField: "value", |
|||
seriesField: "name", |
|||
point: { |
|||
size: 4, |
|||
}, |
|||
legend: { |
|||
layout: "horizontal", |
|||
position: "bottom", |
|||
}, |
|||
}); |
|||
radar.render(); |
|||
}); |
|||
|
|||
onBeforeUnmount(() => { |
|||
radar?.destroy?.(); |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped lang="less"> |
|||
.textOverflow() { |
|||
overflow: hidden; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
word-break: break-all; |
|||
} |
|||
|
|||
// mixins for clearfix |
|||
// ------------------------ |
|||
.clearfix() { |
|||
zoom: 1; |
|||
&::before, |
|||
&::after { |
|||
display: table; |
|||
content: " "; |
|||
} |
|||
&::after { |
|||
clear: both; |
|||
height: 0; |
|||
font-size: 0; |
|||
visibility: hidden; |
|||
} |
|||
} |
|||
|
|||
.activitiesList { |
|||
padding: 0 24px 8px 24px; |
|||
.username { |
|||
color: rgba(0, 0, 0, 0.65); |
|||
} |
|||
.event { |
|||
font-weight: normal; |
|||
} |
|||
} |
|||
|
|||
.pageHeaderContent { |
|||
display: flex; |
|||
padding: 12px; |
|||
margin-bottom: 24px; |
|||
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px; |
|||
.avatar { |
|||
flex: 0 1 72px; |
|||
& > span { |
|||
display: block; |
|||
width: 72px; |
|||
height: 72px; |
|||
border-radius: 72px; |
|||
} |
|||
} |
|||
.content { |
|||
position: relative; |
|||
top: 4px; |
|||
flex: 1 1 auto; |
|||
margin-left: 24px; |
|||
color: rgba(0, 0, 0, 0.45); |
|||
line-height: 22px; |
|||
.contentTitle { |
|||
margin-bottom: 12px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
font-weight: 500; |
|||
font-size: 20px; |
|||
line-height: 28px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.extraContent { |
|||
.clearfix(); |
|||
|
|||
float: right; |
|||
white-space: nowrap; |
|||
.statItem { |
|||
position: relative; |
|||
display: inline-block; |
|||
padding: 0 32px; |
|||
> p:first-child { |
|||
margin-bottom: 4px; |
|||
color: rgba(0, 0, 0, 0.45); |
|||
font-size: 14px; |
|||
line-height: 22px; |
|||
} |
|||
> p { |
|||
margin: 0; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
font-size: 30px; |
|||
line-height: 38px; |
|||
> span { |
|||
color: rgba(0, 0, 0, 0.45); |
|||
font-size: 20px; |
|||
} |
|||
} |
|||
&::after { |
|||
position: absolute; |
|||
top: 8px; |
|||
right: 0; |
|||
width: 1px; |
|||
height: 40px; |
|||
background-color: #e8e8e8; |
|||
content: ""; |
|||
} |
|||
&:last-child { |
|||
padding-right: 0; |
|||
&::after { |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.members { |
|||
a { |
|||
display: block; |
|||
height: 24px; |
|||
margin: 12px 0; |
|||
color: rgba(0, 0, 0, 0.65); |
|||
transition: all 0.3s; |
|||
.textOverflow(); |
|||
.member { |
|||
margin-left: 12px; |
|||
font-size: 14px; |
|||
line-height: 24px; |
|||
vertical-align: top; |
|||
} |
|||
&:hover { |
|||
color: #1890ff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.projectList { |
|||
:deep(.ant-card-meta-description) { |
|||
height: 44px; |
|||
overflow: hidden; |
|||
color: rgba(0, 0, 0, 0.45); |
|||
line-height: 22px; |
|||
} |
|||
.cardTitle { |
|||
font-size: 0; |
|||
a { |
|||
display: inline-block; |
|||
height: 24px; |
|||
margin-left: 12px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
font-size: 14px; |
|||
line-height: 24px; |
|||
vertical-align: top; |
|||
&:hover { |
|||
color: #1890ff; |
|||
} |
|||
} |
|||
} |
|||
.projectGrid { |
|||
width: 33.33%; |
|||
} |
|||
.projectItemContent { |
|||
display: flex; |
|||
flex-basis: 100%; |
|||
height: 20px; |
|||
margin-top: 8px; |
|||
overflow: hidden; |
|||
font-size: 12px; |
|||
line-height: 20px; |
|||
.textOverflow(); |
|||
a { |
|||
display: inline-block; |
|||
flex: 1 1 0; |
|||
color: rgba(0, 0, 0, 0.45); |
|||
.textOverflow(); |
|||
&:hover { |
|||
color: #1890ff; |
|||
} |
|||
} |
|||
.datetime { |
|||
flex: 0 0 auto; |
|||
float: right; |
|||
color: rgba(0, 0, 0, 0.25); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.datetime { |
|||
color: rgba(0, 0, 0, 0.25); |
|||
} |
|||
|
|||
@media screen and (max-width: 1200px) and (min-width: 992px) { |
|||
.activeCard { |
|||
margin-bottom: 24px; |
|||
} |
|||
.members { |
|||
margin-bottom: 0; |
|||
} |
|||
.extraContent { |
|||
margin-left: -44px; |
|||
.statItem { |
|||
padding: 0 16px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media screen and (max-width: 992px) { |
|||
.activeCard { |
|||
margin-bottom: 24px; |
|||
} |
|||
.members { |
|||
margin-bottom: 0; |
|||
} |
|||
.extraContent { |
|||
float: none; |
|||
margin-right: 0; |
|||
.statItem { |
|||
padding: 0 16px; |
|||
text-align: left; |
|||
&::after { |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media screen and (max-width: 768px) { |
|||
.extraContent { |
|||
margin-left: -16px; |
|||
} |
|||
.projectList { |
|||
.projectGrid { |
|||
width: 50%; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media screen and (max-width: 576px) { |
|||
.pageHeaderContent { |
|||
display: block; |
|||
.content { |
|||
margin-left: 0; |
|||
} |
|||
} |
|||
.extraContent { |
|||
.statItem { |
|||
float: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@media screen and (max-width: 480px) { |
|||
.projectList { |
|||
.projectGrid { |
|||
width: 100%; |
|||
} |
|||
} |
|||
} |
|||
</style> |
File diff suppressed because it is too large
Loading…
Reference in new issue