diff --git a/README.md b/README.md index 3713112859a1debfc62ea659066a05b9e1faae03..197e56c5c49976d9662f270e7be5ea35d880008f 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,19 @@ -# 学生选课系统 +## Vue3+FastAPI 小 demo -## 预览 +### frontend -+ [Vue3++TS+ElementPlus+Vite](http://8.136.82.204:8001/) -+ [FastAPI接口预览](http://8.136.82.204:8000/) -+ 🎉🎉🎉感谢 [**wendingming**](https://gitee.com/wendingming) 整理的 [项目部署的准备工作](https://gitee.com/zxiaosi/fast-api/issues/I4V6WV) -+ 🎉🎉🎉感谢 [**dreamrise**](https://gitee.com/dreamrise) 整理的 [运行配置介绍](https://gitee.com/zxiaosi/fast-api/issues/I56HPN) +- 框架:[Vue3.js](https://staging-cn.vuejs.org/) +- UI 库:[Naive UI](https://www.naiveui.com/zh-CN/os-theme) _待定_ -## 安装 +### backend -+ **后端安装**:[FastAPI](https://gitee.com/zxiaosi/fast-api/tree/master/backend#安装)(代码参考[CharmCode](https://www.charmcode.cn/category/FastAPI?page=1)) -+ **前端安装**:[Vue3+Ts](https://gitee.com/zxiaosi/fast-api/tree/master/frontend#安装) (代码参考[Vue-Manage-System](https://github.com/lin-xin/vue-manage-system)) +- 框架:[FastAPI](https://fastapi.tiangolo.com/zh/) +- ORM:[Tortoise ORM ](https://tortoise.github.io/) _待定_ +- Redis:[aioredis](https://aioredis.readthedocs.io/en/latest/) -## 版本 +### deploy -+ `1.0` 测试数据的增删改查已完成 -+ `1.1` 院系表的增删改查已完成(见`信息表格`) -+ `1.2` 首页仪表盘信息的优化 -+ `1.3` 院系表的增删改查初步完成 -+ `1.4` 整理代码 -+ `1.5` 添加了教师表 -+ `1.6` 添加了学生表、课程表、选课表 -+ `1.7` 重构前端代码 -+ `1.8` 封装组件,取出冗余代码 -+ `1.9` 自定义表格组件 -+ `2.0` 部署项目 -+ `2.1` 重构FastAPI -+ `2.2` 配置nginx以及SSL证书(域名未备案,ssl证书未生效) -+ `2.3` 添加Redis -+ `2.4` 加入TS -+ `2.5` 支持PostgreSQL,实现图片上传 -+ `2.6` 前端文件分离(vue与ts),后端实现权限管理 -+ `2.7` 简单实现权限管理 -+ `2.8` 调整数据库结构&&简单实现学生选课 -+ `2.9` 简单实现教师讲授课程 +- nginx +- docker ->TODO:优化代码 - -## 开启服务 - -1. 后端 - - + 进入到 `backend` 项目下 - + 找到 `main.py` 右键运行(建议用Pycharm启动) - - >接口文档:http://127.0.0.1:8000/docs - -2. 前端 - - + 进到 `frontend` 目录下 - + `npm run dev` 运行项目(建议用Vscode) - - >服务接口:http://localhost:3000/ - -3. 效果 - -+ 登录界面 - - + `用户名`:`admin` - - + `密码`:`123` - - + 如图 - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/frontend-login.png) - -+ 首页(假数据) - - ![home](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/home.png) - -+ 数据的`增` - - ![add](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/add.gif) - -+ 数据的`删` - - ![delete](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/delete.gif) - -+ 数据的`改` - - ![update](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/update.gif) - -+ 搜索数据 - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/search.gif) - -+ 多选删除 - - ![selectedDelete](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/selectedDelete.gif) +**后端安装教程** >>> `backend/README.md` diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 9d18d6d34a59c82adfbf93c5afc393bef08fd12c..0000000000000000000000000000000000000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -# Python版本 -FROM python:3.9 - -# 工作路径 -WORKDIR /code - -# 方便下面使用缓存加载 -COPY ./requirements.txt /code/requirements.txt - -# 使用缓存下载安装包 -#RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt - -# 使用缓存下载安装包(镜像) -RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=1000 --no-cache-dir --upgrade -r /code/requirements.txt - -# docker部署(https://fastapi.tiangolo.com/zh/deployment/docker/) -# .表示同级目录下 -COPY . /code/ - -# 启动命令 -#CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] - -# main.py启动命令 -CMD ["python", "main.py"] \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index 74be6c05583536c1d92eb28885ff5a85183e1169..954de3b798c078766a66a9c13f9fcc3d7ec53cb4 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,194 +1,22 @@ -# FastAPI +## backend -## 版本迭代 +### 1. 环境 -+ `V1.0` FastAPI学习 -+ `V2.0` 搭建FastAPI脚手架 -+ `V2.1` 创建所需的表 -+ `V2.2` 已成功调试Mysql、Sqlite, 未调试Postgresql -+ `V2.3` 初始化表数据(调试) -+ `V2.4` 优化创建表问题 -+ `V2.5` 初始化所有表数据 -+ `V2.6` 封装日志模块 -+ `V2.7` 封装多进程日志模块(线程锁) -+ `V2.8` 优化了目录结构 -+ `V2.9` 优化代码&&调试了user表的增删查接口 -+ `V3.0` 添加了防止跨域请求代码&&调试了接口 -+ `V3.1` 去除了测试表的自增 -+ `V3.2` 添加了department表的接口 -+ `V3.3` 添加了major表的接口 -+ `V3.4` 更新了major表接口的部分代码 -+ `V3.5` 更换了日志模块(loguru)&&添加了后端数据验证 -+ `V3.5` 添加了teacher表的接口 -+ `V3.6` 添加了student表的接口 -+ `V3.7` 添加了course表和selectCourse表的接口 -+ `V3.8` 更新了所需的包 -+ `V3.9` 修改了查询单个信息的数据 -+ `V4.0` 删除了获取所有数据以及获取单个数据的方法 -+ `V4.1` 修改了校验规则 -+ `V4.1.1` 删除了整型和浮点型的正则校验规则 -+ `v4.2` 尝试部署中。。。 -+ `v4.3` 部署成功,修复了部分bug -+ `v4.4` 测试token -+ `v4.5` 调试token成功(admin, 123) -+ `v4.6` 重构FastAPI -+ `v4.7` 添加redis -+ `v4.8` 重构后端 -+ `v4.9` 支持PostgreSQL,以及图片上传 -+ `v5.0` 后台实现权限管理模块 -+ `v5.1` 整理接口,简单实现权限管理模块 -+ `v5.2` 调整数据库结构 +- Python 版本 `Python >= 3.9` -## 安装 +- 安装所需的包 -1. 配置Python3.9(及以上)的虚拟环境 + ```sh + pip install -r requirements.txt + ``` -2. 安装运行所需的包 +### 2. 配置文件 - ```python - # 默认装了Mysql与ProgreSQL - pip install requirements.txt - - # 或者 - pip install fastapi - pip install uvicorn[fastapi] - pip install loguru - pip install SQLAlchemy - pip install aioredis - pip install python-jose - pip install passlib - pip install bcrypt - pip install python-multipart - pip install orjson - - # 使用Sqlite(异步), 请安装下面包 - pip install aiosqlite - - # 使用MySQL(异步), 请安装下面包 - pip install asyncmy - - # 使用ProgreSQL(异步), 请安装下面包 - pip install asyncpg - ``` +- `backend/core/config.py` +- 配置数据库连接 +### 3. 启动服务 -3. 启动服务 - - + 进入到 `backend` 项目下 - + 找到 `main.py` 右键运行 - + `core/config` 配置文件(默认数据库是sqlite) - - > 接口文档:http://127.0.0.1:8000/docs - -## 项目截图 - -+ 成功运行的图片 - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/image-20211021164103094.png) - -+ 接口图 - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/backend-%E6%8E%A5%E5%8F%A3.png) - -## 项目目录(待整理) - -```sh -|-- backend - - |-- api # 接口 - |-- admin - |-- __init__.py # 管理员接口 - |-- course.py # 课程表接口 - |-- department.py # 院系表接口 - |-- major.py # 专业表接口 - |-- index.py # 管理员首页 - |-- elective.py # 选课表接口 - |-- student.py # 学生表接口 - |-- teacher.py # 教师表接口 - |-- common - |-- __init__.py # 共用接口 - |-- login.py # 登录接口 - |-- redis_check.py # 检查redis是否连接成功 - |-- upload.py # 图片上传 - |-- __init__.py - |-- deps.py # 依赖项 - |-- api_router.py # admin接口汇总 - - |-- core - |-- __init__.py # 核心内容 - |-- config.py # 配置文件 - |-- security.py # 安全配置 - - |-- crud - |-- __init__.py # 数据库的增删改查操作 - |-- base.py # 封装数据库增删改查方法 - |-- course.py # 课程表 - |-- department.py # 院系表 - |-- major.py # 专业表 - |-- elective.py # 选课表 - |-- teacher.py # 教师表 - |-- student.py # 学生表 - - |-- db - |-- __init__.py # 初始数据库以及表数据 - |-- data.py # 所有数据 - |-- init_data.py # 两种初始化表数据的方式 - |-- init_db.py # 创建和删除base中的表 - |-- session.py # 创建数据库连接会话 - |-- redis.py # 注册Redis - - |-- logs # 日志模块(自动生成) - |-- 2021-10-06_23-46-45.log - |-- 2021-10-06_23-46-47.log - |-- 2021-10-06_23-46-49.log - - |-- models - |-- __init__.py # ORM模型映射 - |-- base.py # 自动生成 表名 - |-- index.py # 管理员表 - |-- course.py # 课程表 - |-- department.py # 院系表 - |-- major.py # 专业表 - |-- elective.py # 选课表 - |-- student.py # 学生表 - |-- teacher.py # 教师表 - - |-- register - |-- __init__.py # 注册中心 - |-- cors.py # 注册跨域请求 - |-- exception.py # 注册全局异常 - |-- middleware.py # 注册请求响应拦截 - |-- router.py # 注册路由 - - |-- schemas - |-- __init__.py # 数据模型 - |-- admin.py # 管理员表模型 - |-- common.py # 公用表模型 - |-- course.py # 课程表模型 - |-- department.py # 院系表模型 - |-- login.py # 登录模型 - |-- major.py # 专业表模型 - |-- result.py # 返回数据模型 - |-- elective.py # 选课表模型 - |-- student.py # 学生表模型 - |-- teacher.py # 教师表模型 - |-- todo.py # 待办模型 - |-- token.py # token模型 - - |-- utils # 工具 - |-- __init__.py # 抛出工具类 - |-- create_dir.py # 创建文件夹类(位置勿动) - |-- custon_exc.py # 自定义异常 - |-- ip_address.py # 根据ip获取位置 - |-- logger.py # 日志模块 - |-- permission_assign.py # 权限管理 - |-- resp_code.py # 状态码 - - |-- __init__.py - |-- main.py # 主程序 - |-- Dockerfile # Dockerfile文件 - |-- README.md # Readme文件 - |-- requirements.txt # 所需的包 - |-- sql_app.db # sqlite数据库 -``` - +- 进入到 `backend` 项目下 +- 找到 `main.py` 右键运行 +- 接口文档:[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) diff --git a/backend/apis/__init__.py b/backend/apis/__init__.py index fdb36b74646ae2a9321b2a40dd1dc756f5a3ccc4..9803b2080c668ce67eb4b101c6fc2a728ddf4d88 100644 --- a/backend/apis/__init__.py +++ b/backend/apis/__init__.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/10/15 19:57 # @Author : zxiaosi -# @desc : 所有接口 -from .api_router import app_router +# @Time : 2022/7/1 14:41 +# @desc : diff --git a/backend/apis/api_router.py b/backend/apis/api_router.py deleted file mode 100644 index b7ce22fd0ddb3b25864e1e343f22c14da79b209e..0000000000000000000000000000000000000000 --- a/backend/apis/api_router.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/15 20:01 -# @Author : zxiaosi -# @desc : 接口汇总 -from fastapi import APIRouter - -from apis.common import upload, student, teacher, department, major, course, taught, elective - -app_router = APIRouter() - -# include_in_schema=False 隐藏属性 -# deprecated=True 弃用属性 - -# 上传图片 -app_router.include_router(upload.router, tags=["Upload"]) - -# common -app_router.include_router(elective.router, prefix="/elective", tags=["elective"]) -app_router.include_router(department.router, prefix="/department", tags=["Department"]) -app_router.include_router(major.router, prefix="/major", tags=["major"]) -app_router.include_router(course.router, prefix="/course", tags=["course"]) -app_router.include_router(teacher.router, prefix="/teacher", tags=["teacher"]) -app_router.include_router(student.router, prefix="/student", tags=["student"]) -app_router.include_router(taught.router, prefix="/taught", tags=["taught"]) - - -# Admin - -# teacher - -# student diff --git a/backend/apis/common/__init__.py b/backend/apis/common/__init__.py deleted file mode 100644 index 0f878d998e075c691792c480a79e4f72a1270cf9..0000000000000000000000000000000000000000 --- a/backend/apis/common/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/2/24 9:43 -# @Author : zxiaosi -# @desc : 共用接口 diff --git a/backend/apis/common/course.py b/backend/apis/common/course.py deleted file mode 100644 index 845e1ecad9c826556eaec3e9909451b3568d5797..0000000000000000000000000000000000000000 --- a/backend/apis/common/course.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/17 16:42 -# @Author : zxiaosi -# @desc : 课程表接口 -from typing import List -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from schemas import CourseCreate, CourseUpdate, CourseOut as Course, Result, ResultPlus -from crud import course -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Course], summary='根据 id 查询课程信息') -async def read_course(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - _course = await course.get(db, id) - if not _course: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的课程.") - return resp_200(data=_course, msg=f"查询到了 id 为 {id} 的课程.") - - -@router.get("/", response_model=ResultPlus[Course], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有课程') -async def read_courses(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有院系 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await course.get_number(db) - _courses = await course.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _courses}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个课程信息.") - - -@router.post("/", response_model=Result, summary='添加课程信息') -async def create_course(course_in: CourseCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - await course.create(db, obj_in=course_in) - return resp_200(msg=f"添加了 id 为 {course_in.id} 的课程信息.") - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新课程信息') -async def update_course(id: int, course_in: CourseUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await course.update(db, id=id, obj_in=course_in) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的课程.") - return resp_200(msg=f"更新了 id 为 {id} 的课程信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除课程信息') -async def delete_course(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=["admin"])): - rowcount = await course.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的课程.") - return resp_200(msg=f"删除了 id 为 {id} 的课程信息.") - - -@router.post("/del/", response_model=Result, summary='同时删除多个课程信息') -async def delete_courses(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await course.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='成功删除多个课程信息.') diff --git a/backend/apis/common/dashboard.py b/backend/apis/common/dashboard.py deleted file mode 100644 index 69b79159ea3faf4577c6c55f76d6985d34d7559b..0000000000000000000000000000000000000000 --- a/backend/apis/common/dashboard.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/17 15:15 -# @Author : zxiaosi -# @desc : 首页 -import json -from fastapi import APIRouter, Depends -from fastapi.responses import ORJSONResponse - -from db import MyRedis -from apis.deps import get_redis -from schemas import Todo, TodoId -from utils import resp_200 - -router = APIRouter() - - -@router.get("/dashboard", response_class=ORJSONResponse, summary='访问量 && 待办数 && 请求数 && 待办事项') -async def get_visit_todo_request(redis: MyRedis = Depends(get_redis)): - """ 查询首页数据(访问量 && 待办事项 && 请求数 && 待办事项) """ - visit_num = await redis.get('visit_num') - todo_num = await redis.llen('todo_list') - request_num = await redis.get('request_num') - todo_list = await redis.list_loads('todo_list', 6) - data = {'visit_num': visit_num, 'todo_num': todo_num, 'request_num': request_num, 'todo_list': todo_list} - return resp_200(data=data, msg='查询了访问量 && 待办事项 && 请求数 && 待办事项') - - -@router.post("/todo/add", summary='添加待办') -async def add_todo(todo_in: Todo, redis: MyRedis = Depends(get_redis)): - """ 添加待办 """ - text = {'title': todo_in.title, 'status': todo_in.status} - await redis.cus_lpush('todo_list', text) - return resp_200(msg='添加待办成功!') - - -@router.post("/todo/update", response_class=ORJSONResponse, summary='根据索引更新待办') -async def update_todo(todo_in: TodoId, redis: MyRedis = Depends(get_redis)): - """ 更新待办 """ - obj = await redis.get_list_by_index('todo_list', todo_in.id) - obj["status"] = bool(1 - obj["status"]) # noqa 取反 - await redis.lset('todo_list', todo_in.id, json.dumps(obj)) - return resp_200(msg='更新待办成功!') diff --git a/backend/apis/common/department.py b/backend/apis/common/department.py deleted file mode 100644 index 8bf2251b0da723783031ed6d9bda0a7e23203aaf..0000000000000000000000000000000000000000 --- a/backend/apis/common/department.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/28 19:18 -# @Author : zxiaosi -# @desc : 院系表接口 -from typing import List - -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from schemas import DepartmentUpdate, DepartmentCreate, DepartmentOut as Department, Result, ResultPlus -from crud import department -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Department], summary='根据 id 查询院系信息') -async def read_department(id: int, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - _department = await department.get(db, id) - if not _department: - raise IdNotExist(f"系统中不存在 id 为 {id} 的院系.") - return resp_200(data=_department, msg=f"查询到了 id 为 {id} 的院系.") - - -@router.get("/", response_model=ResultPlus[Department], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有院系') -async def read_departments(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有院系 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await department.get_number(db) - _departments = await department.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _departments}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个院系信息.") - - -@router.post("/", response_model=Result, summary='添加院系信息') -async def create_department(department_in: DepartmentCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - await department.create(db, obj_in=department_in) - return resp_200(msg=f"添加了 id 为 {department_in.id} 的院系信息.") - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新院系信息') -async def update_department(id: int, department_in: DepartmentUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await department.update(db, id=id, obj_in=department_in) - if not rowcount: # 每次更新, 当前数据的更新时间会变, 只要id存在, 就会一直返回1 - raise IdNotExist(f"系统中不存在 id 为 {id} 的院系.") - return resp_200(msg=f"更新了 id 为 {id} 的院系信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除院系信息') -async def delete_department(id: int, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await department.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的院系.") - return resp_200(msg=f'成功删除 id 为 {id} 的院系信息.') - - -@router.post("/del/", response_model=Result, summary='同时删除多个院系信息') -async def delete_departments(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await department.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='成功删除多个院系信息.') - - -@router.get("/sort/{name}", summary='根据字段排序') -async def get_select_courses(name: str, pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db)): - """ 查询所有院系 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await department.get_number(db) - _departments = await department.sort(db, name, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _departments}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个院系信息.") diff --git a/backend/apis/common/elective.py b/backend/apis/common/elective.py deleted file mode 100644 index 4e72e4482a58d80b338d9d69663a85305aa025b7..0000000000000000000000000000000000000000 --- a/backend/apis/common/elective.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/18 10:25 -# @Author : zxiaosi -# @desc : 选课表接口 -from typing import List -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from models import Student -from schemas import ElectiveCreate, ElectiveUpdate, ElectiveOut as Elective, Result, ResultPlus -from crud import elective -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Elective], summary='根据 id 查询选课信息') -async def read_elective(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - _elective = await elective.get(db, id) - if not _elective: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的选课.") - return resp_200(data=_elective, msg=f"查询到了 id 为 {id} 的选课.") - - -@router.get("/", response_model=ResultPlus[Elective], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有选课') -async def read_electives(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有院系 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await elective.get_number(db) - _electives = await elective.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _electives}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个选课信息.") - - -@router.post("/", response_model=Result, summary='添加选课信息') -async def create_elective(elective_in: ElectiveCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - await elective.create(db, obj_in=elective_in) - return resp_200(msg='添加了选课信息.') - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新选课信息') -async def update_elective(id: int, elective_in: ElectiveUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - rowcount = await elective.update(db, id=id, obj_in=elective_in) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的选课.") - return resp_200(msg=f"更新了 id 为 {id} 的选课信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除选课信息') -async def delete_elective(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - rowcount = await elective.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的选课.") - return resp_200(msg=f"删除了 id 为 {id} 的选课信息.") - - -@router.post("/del/", response_model=Result, summary='同时删除多个选课信息') -async def delete_electives(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - rowcount = await elective.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='成功删除多个选课信息.') - - -@router.post("/add/{courseId}", response_model=Result, summary='添加选课信息') -async def create_elective_by_course_id(courseId: int, db: AsyncSession = Depends(get_db), - user: Student = Security(get_current_user, scopes=[])): - obj = await elective.is_exist(db, courseId=courseId, studentId=user.id) - if obj: - data, msg = 0, '数据已存在.' - else: - data, msg = 1, '添加了选课信息.' - await elective.create(db, obj_in={'grade': 0, 'studentId': user.id, 'courseId': courseId}) - return resp_200(data=data, msg=msg) - - -@router.post("/del/{courseId}", response_model=Result, summary='通过其他字段删除选课信息') -async def del_elective_by_filed(courseId: int, db: AsyncSession = Depends(get_db), - user: Student = Security(get_current_user, scopes=[])): - obj = await elective.is_exist(db, courseId=courseId, studentId=user.id) - if obj: - data, msg = 1, '数据存在, 退课成功' - rowcount = await elective.remove(db, id=obj.id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的选课.") - else: - data, msg = 0, '数据不存在, 退课失败!' - return resp_200(data=data, msg=msg) - - -@router.get("/detail/", response_model=Result, summary='获取学生选课信息详情') -async def get_course_detail(db: AsyncSession = Depends(get_db), user: Student = Security(get_current_user, scopes=[])): - data = await elective.get_course(db, id=user.id) - return resp_200(data=data, msg='获取学生选课信息详情.') diff --git a/backend/apis/common/login.py b/backend/apis/common/login.py deleted file mode 100644 index 729219c7683fdaad9c945544f0de311a4659f84d..0000000000000000000000000000000000000000 --- a/backend/apis/common/login.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/12/28 19:16 -# @Author : zxiaosi -# @desc : 登录 -import json -from datetime import timedelta - -from sqlalchemy.ext.asyncio import AsyncSession -from fastapi.encoders import jsonable_encoder -from fastapi import APIRouter, Depends, Request, Security -from fastapi.security import OAuth2PasswordRequestForm - -from core import settings, create_access_token -from db import MyRedis -from models import Admin, Teacher, Student -from schemas import Result, Token, AdminOut, TeacherOut, StudentOut -from apis.deps import get_redis, get_db, get_current_user -from utils import resp_200, SetRedis, ErrorUser, by_ip_get_address -from utils.permission_assign import by_scopes_get_crud - -router = APIRouter() - - -# 用户登录推荐看一下这个库: https://fastapi-users.github.io/fastapi-users/ -# 这里的登录借助的是OAuth2,存储用的JWT,与redis存储的token已经没有关系 (前端请求要发送 表单请求) -# OAuth2中token过期时间与 设置的时间 以及 服务的开启关闭 有关, 时间到期或者服务关闭token过期 -@router.post("/login", response_model=Token, summary="docs接口文档登录 && 登录接口") -async def login_access_token( - request: Request, - db: AsyncSession = Depends(get_db), - form_data: OAuth2PasswordRequestForm = Depends() -): - """ 兼容OAuth2的令牌登录,为接口文档的请求获取访问令牌 """ - crud_obj = by_scopes_get_crud(form_data.scopes) # 权限分配 - _user = await crud_obj.authenticate(db, username=form_data.username, password=form_data.password) - if not _user: - raise ErrorUser() - - address = by_ip_get_address(request.client.host) # 根据ip获取地址 - await crud_obj.update(db, id=_user.id, obj_in={'address': address}) - - access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) - token = create_access_token({"sub": str(_user.id), "scopes": form_data.scopes}, access_token_expires) - - try: - await request.app.state.redis.incr('visit_num') # 用户访问量 自增1 - await request.app.state.redis.set(token, json.dumps(jsonable_encoder(_user)), access_token_expires) - except Exception as e: - raise SetRedis(f'Redis存储 token 失败!-- {e}') - - # 这里'access_token'和'token_type'一定要写,否则get_current_user依赖拿不到token - # 可添加字段(先修改schemas/token里面的Token返回模型) - return {"access_token": token, "token_type": "bearer"} - - -@router.get("/admin/index", response_model=Result[AdminOut], summary="获取当前管理员") -def get_current_admin(current_user: Admin = Security(get_current_user, scopes=["admin"])): - return resp_200(data=jsonable_encoder(current_user), msg='获取当前管理员信息!') - - -@router.get("/teacher/index", response_model=Result[TeacherOut], summary="获取当前教师") -def get_current_teacher(current_user: Teacher = Security(get_current_user, scopes=["teacher"])): - return resp_200(data=jsonable_encoder(current_user), msg='获取当前教师信息!') - - -@router.get("/student/index", response_model=Result[StudentOut], summary="获取当前学生") -def get_current_student(current_user: Student = Security(get_current_user, scopes=["student"])): - return resp_200(data=jsonable_encoder(current_user), msg='获取当前学生信息!') - - -@router.post("/logout", response_model=Result, summary="退出登录(已隐藏)", include_in_schema=False) -async def logout_token(request: Request, redis: MyRedis = Depends(get_redis)): - if 'authorization' in request.headers.keys(): - token = request.headers.get('authorization')[7:] # 去除token前面的 Bearer - await redis.delete(token) - return resp_200(msg='退出登录') diff --git a/backend/apis/common/major.py b/backend/apis/common/major.py deleted file mode 100644 index 0ed3ee4c608e36b2873549519144236e2eba4eb0..0000000000000000000000000000000000000000 --- a/backend/apis/common/major.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/1 21:17 -# @Author : zxiaosi -# @desc : 专业表接口 -from typing import List - -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from schemas import MajorCreate, MajorUpdate, MajorOut as Major, Result, ResultPlus -from crud import major -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Major], summary='根据 id 查询专业信息') -async def read_major(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - _major = await major.get(db, id) - if not _major: - raise IdNotExist(f"系统中不存在 id 为 {id} 的专业.") - return resp_200(data=_major, msg=f"查询到了 id 为 {id} 的专业.") - - -@router.get("/", response_model=ResultPlus[Major], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有专业') -async def read_majors(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有专业 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await major.get_number(db) - _majors = await major.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _majors}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个专业信息.") - - -@router.post("/", response_model=Result, summary='添加专业信息') -async def create_major(major_in: MajorCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - await major.create(db, obj_in=major_in) - return resp_200(msg=f"添加了 id 为 {major_in.id} 的专业信息.") - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新专业信息') -async def update_major(id: int, major_in: MajorUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await major.update(db, id=id, obj_in=major_in) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的专业.") - return resp_200(msg=f"更新了 id 为 {id} 的专业信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除专业信息') -async def delete_major(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=["admin"])): - rowcount = await major.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的专业.") - return resp_200(msg=f"删除了 id 为 {id} 的专业信息.") - - -@router.post("/del/", response_model=Result, summary='同时删除多个专业信息') -async def delete_majors(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=["admin"])): - rowcount = await major.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='同时删除多个专业信息.') diff --git a/backend/apis/common/redis_check.py b/backend/apis/common/redis_check.py deleted file mode 100644 index 86ab789bac3c67274143d84e09b54214872f4f94..0000000000000000000000000000000000000000 --- a/backend/apis/common/redis_check.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/5 13:55 -# @Author : zxiaosi -# @desc : 检查redis -from fastapi import APIRouter, Depends - -from db import MyRedis -from apis.deps import get_redis - -router = APIRouter() - - -@router.get("/health-check") -async def health_check(redis: MyRedis = Depends(get_redis)): - try: - # key:test:keys => key/test/keys - value = await redis.get('request_num') - msg = "开启Redis成功!!!" - except Exception as e: # noqa: E722 - value = 0 - msg = f"对不起,不能打开Redis!!! {e}" - return {"request": f"总计请求次数 {value}", "msg": msg} diff --git a/backend/apis/common/student.py b/backend/apis/common/student.py deleted file mode 100644 index 23c781dd903ca90683dedad8c9ea490538943918..0000000000000000000000000000000000000000 --- a/backend/apis/common/student.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/17 11:12 -# @Author : zxiaosi -# @desc : 学生表接口 -from typing import List -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from schemas import StudentCreate, StudentUpdate, StudentOut as Student, Result, ResultPlus -from crud import student -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Student], summary='根据 id 查询学生信息') -async def read_student(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - _student = await student.get(db, id) - if not _student: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的学生.") - return resp_200(data=_student, msg=f"查询到了 id 为 {id} 的学生.") - - -@router.get("/", response_model=ResultPlus[Student], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有学生') -async def read_students(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有院系 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await student.get_number(db) - _students = await student.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _students}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个学生信息.") - - -@router.post("/", response_model=Result, summary='添加学生信息') -async def create_student(student_in: StudentCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - await student.create(db, obj_in=student_in) - return resp_200(msg=f"添加了 id 为 {student_in.id} 的学生信息.") - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新学生信息') -async def update_student(id: int, student_in: StudentUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await student.update(db, id=id, obj_in=student_in) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的学生.") - return resp_200(msg=f"更新了 id 为 {id} 的学生信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除学生信息') -async def delete_student(id: int, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await student.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的学生.") - return resp_200(msg=f"删除了 id 为 {id} 的学生信息.") - - -@router.post("/del/", response_model=Result, summary='同时删除多个学生信息') -async def delete_students(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await student.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='成功删除多个学生信息.') - - -# @router.get("/detail/{id}", summary='得到详细信息') -# async def get_detail(id: int, db: AsyncSession = Depends(get_db)): -# _data = await student.get_detail(db, id) -# return _data diff --git a/backend/apis/common/taught.py b/backend/apis/common/taught.py deleted file mode 100644 index 28f605989b184f5220c76cf79a3ab3ff5b9be92c..0000000000000000000000000000000000000000 --- a/backend/apis/common/taught.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/5/10 21:08 -# @Author : zxiaosi -# @desc : 讲授表接口 -from typing import List -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from models import Teacher -from schemas import TaughtCreate, TaughtUpdate, TaughtOut as Taught, Result, ResultPlus -from crud import taught -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Taught], summary='根据 id 查询讲授信息') -async def read_taught(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - _taught = await taught.get(db, id) - if not _taught: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的讲授.") - return resp_200(data=_taught, msg=f"查询到了 id 为 {id} 的讲授.") - - -@router.get("/", response_model=ResultPlus[Taught], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有讲授') -async def read_taughts(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有院系 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await taught.get_number(db) - _taughts = await taught.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _taughts}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个讲授信息.") - - -@router.post("/", response_model=Result, summary='添加讲授信息') -async def create_taught(taught_in: TaughtCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - await taught.create(db, obj_in=taught_in) - return resp_200(msg="添加了讲授信息.") - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新讲授信息') -async def update_taught(id: int, taught_in: TaughtUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await taught.update(db, id=id, obj_in=taught_in) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的讲授.") - return resp_200(msg=f"更新了 id 为 {id} 的讲授信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除讲授信息') -async def delete_taught(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=['admin'])): - rowcount = await taught.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的讲授.") - return resp_200(msg=f"删除了 id 为 {id} 的讲授信息.") - - -@router.post("/del/", response_model=Result, summary='同时删除多个讲授信息') -async def delete_taughts(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await taught.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='成功删除多个讲授信息.') - - -@router.get("/detail/", response_model=Result, summary='获取教师讲授信息详情') -async def get_course_detail(db: AsyncSession = Depends(get_db), user: Teacher = Security(get_current_user, scopes=[])): - data = await taught.get_course(db, id=user.id) - return resp_200(data=data, msg='获取教师讲授信息详情.') diff --git a/backend/apis/common/teacher.py b/backend/apis/common/teacher.py deleted file mode 100644 index 29cc25a7e4e4fa8654bae2bf3caeb55302810a27..0000000000000000000000000000000000000000 --- a/backend/apis/common/teacher.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/16 9:48 -# @Author : zxiaosi -# @desc : 教师表接口 -from typing import List - -from fastapi import APIRouter, Depends, Security -from sqlalchemy.ext.asyncio import AsyncSession - -from apis.deps import get_db, get_current_user -from schemas import TeacherCreate, TeacherUpdate, TeacherOut as Teacher, Result, ResultPlus -from crud import teacher -from utils import resp_200, IdNotExist - -router = APIRouter() - - -@router.get("/{id}", response_model=Result[Teacher], summary='根据 id 查询教师信息') -async def read_teacher(id: int, db: AsyncSession = Depends(get_db), user=Security(get_current_user, scopes=[])): - _teacher = await teacher.get(db, id) - if not _teacher: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的教师.") - return resp_200(data=_teacher, msg=f"查询到了 id 为 {id} 的教师.") - - -@router.get("/", response_model=ResultPlus[Teacher], summary='根据页码 pageIndex 和每页个数 pageSize 查询所有教师') -async def read_teachers(pageIndex: int = 1, pageSize: int = 10, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=[])): - """ 查询所有教师 (pageIndex = -1 && pageSize = -1 表示查询所有) """ - _count = await teacher.get_number(db) - _teachers = await teacher.get_multi(db, pageIndex, pageSize) - return resp_200(data={"count": _count, "list": _teachers}, msg=f"查询了第 {pageIndex} 页中的 {pageSize} 个教师信息.") - - -@router.post("/", response_model=Result, summary='添加教师信息') -async def create_teacher(teacher_in: TeacherCreate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - await teacher.create(db, obj_in=teacher_in) - return resp_200(msg=f"添加了 id 为 {teacher_in.id} 的教师信息.") - - -@router.put("/{id}", response_model=Result, summary='通过 id 更新教师信息') -async def update_teacher(id: int, teacher_in: TeacherUpdate, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await teacher.update(db, id=id, obj_in=teacher_in) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的教师.") - return resp_200(msg=f"更新了 id 为 {id} 的教师信息.") - - -@router.delete("/{id}", response_model=Result, summary='通过 id 删除教师信息') -async def delete_teacher(id: int, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await teacher.remove(db, id) - if not rowcount: - raise IdNotExist(err_desc=f"系统中不存在 id 为 {id} 的教师.") - return resp_200(msg=f"删除了 id 为 {id} 的教师信息.") - - -@router.post("/del/", response_model=Result, summary='同时删除多个教师信息') -async def delete_teachers(idList: List, db: AsyncSession = Depends(get_db), - user=Security(get_current_user, scopes=['admin'])): - rowcount = await teacher.remove_multi(db, id_list=idList) - if not rowcount: - raise IdNotExist(err_desc="系统中不存在列表中的id.") - return resp_200(msg='成功删除多个教师信息.') diff --git a/backend/apis/common/upload.py b/backend/apis/common/upload.py deleted file mode 100644 index 191a3725ef84cb807b491c1a1bfee99521a9527b..0000000000000000000000000000000000000000 --- a/backend/apis/common/upload.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/3/4 11:42 -# @Author : zxiaosi -# @desc : 上传图片 https://fastapi.tiangolo.com/zh/tutorial/request-files/?h=up#uploadfile -import shutil -from pathlib import Path -from tempfile import NamedTemporaryFile -from fastapi import APIRouter, Depends, File, UploadFile -from sqlalchemy.orm import Session - -import crud -from core import settings -from core.logger import logger -from apis.deps import get_db, get_current_user -from models import Admin -from utils import resp_200 -from utils.create_dir import create_dir - -router = APIRouter() - - -@router.post("/upload/file/", summary="上传头像") -async def upload_image( - file: UploadFile = File(...), - db: Session = Depends(get_db), - current_user: Admin = Depends(get_current_user) -): - static_path = create_dir(settings.STATIC_DIR) - logger.info(f"用户 {current_user.name} 正在上传图片 {file.filename}.") - try: - suffix = Path(file.filename).suffix - with NamedTemporaryFile(delete=False, suffix=suffix, dir=static_path) as tmp: - shutil.copyfileobj(file.file, tmp) - tmp_file_name = Path(tmp.name).name - finally: - file.file.close() - user = crud.admin.update(db, db_obj=current_user, - obj_in={'image': f"{settings.BASE_URL}/{settings.STATIC_DIR}/{tmp_file_name}"}) - return resp_200(data=user, msg='上传成功!') diff --git a/backend/apis/debug.py b/backend/apis/debug.py new file mode 100644 index 0000000000000000000000000000000000000000..d63c8e999e91613657dad05f3f56edc4ccf4358c --- /dev/null +++ b/backend/apis/debug.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/1 14:46 +# @desc : 调试接口 +from typing import List + +from fastapi import APIRouter, HTTPException +from tortoise.contrib.fastapi import HTTPNotFoundError + +from models.user import User, UserIn, UserOut +from schemas.result import Status + +router = APIRouter() + + +@router.get("/") +async def root(): + return {"message": "Hello World"} + + +@router.get("/hello/{name}") +async def say_hello(name: str): + return {"message": f"Hello {name}"} + + +@router.get("/user/", response_model=List[UserOut]) +async def get_users(): + return await UserOut.from_queryset(User.all()) + + +@router.post("/user/create", response_model=UserOut) +async def create_user(user: UserIn): + result = await User.create(**user.dict(exclude_unset=True)) + return await UserOut.from_tortoise_orm(result) + + +@router.get("/user/{user_id}", response_model=UserOut, responses={404: {"model": HTTPNotFoundError}}) +async def get_user(user_id: int): + return await UserOut.from_queryset_single(User.get(id=user_id)) + + +@router.put("/user/{user_id}", response_model=UserOut, responses={404: {"model": HTTPNotFoundError}}) +async def update_users(user_id: int, user: UserIn): + await User.filter(id=user_id).update(**user.dict(exclude_unset=True)) + return await UserOut.from_queryset_single(User.get(id=user_id)) + + +@router.delete("/user/{user_id}", response_model=Status, responses={404: {"model": HTTPNotFoundError}}) +async def delete_user(user_id: int): + deleted_count = await User.filter(id=user_id).delete() + if not deleted_count: + raise HTTPException(status_code=404, detail=f"User {user_id} not found") + return Status(message=f"Deleted user {user_id}") diff --git a/backend/apis/deps.py b/backend/apis/deps.py deleted file mode 100644 index 0c74d5a06dada71c82ee4542a784e43d1c2f0c0d..0000000000000000000000000000000000000000 --- a/backend/apis/deps.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/15 20:10 -# @Author : zxiaosi -# @desc : 依赖项 -from typing import AsyncGenerator - -from fastapi import Depends, Security, HTTPException -from fastapi.security import SecurityScopes, OAuth2PasswordBearer -from sqlalchemy.ext.asyncio import AsyncSession -from starlette.requests import Request - -from core import check_jwt_token, settings -from db import async_session, MyRedis -from schemas import TokenData -from utils import UserNotExist, PermissionNotEnough -from utils.permission_assign import by_scopes_get_crud, handle_oauth2_scopes - -get_token = OAuth2PasswordBearer(tokenUrl=f"{settings.API_PREFIX}/login", scopes=handle_oauth2_scopes()) - - -async def get_db() -> AsyncGenerator[AsyncSession, None]: - """ sql连接会话 """ - async with async_session() as session: - yield session - - -async def get_redis(request: Request) -> MyRedis: - """ redis连接对象 """ - return await request.app.state.redis - - -async def get_current_user( - security_scopes: SecurityScopes, - db: AsyncSession = Depends(get_db), - token: str = Depends(get_token) -): - """ 得到当前用户(docs接口文档) """ - payload = await check_jwt_token(token) # 检验token是否过期 - token_scopes = payload.get("scopes", []) # 得不到值,返回[] - token_data = TokenData(scopes=token_scopes, sub=payload.get("sub")) # token存储的用户权限 - crud_obj = by_scopes_get_crud(token_scopes) # 验证用户是否存在 - user = await crud_obj.get(db, id=payload.get("sub")) - if not user: - raise UserNotExist() - for scope in security_scopes.scopes: # 勾选的用户权限 - if scope not in token_data.scopes: - raise PermissionNotEnough('权限不足,拒绝访问') - return user - -# def get_current_active_user(current_user: Admin = Security(get_current_user, scopes=["admin"])): -# """ 得到当前登录用户 """ -# if not admin.is_active_def(current_user): -# raise HTTPException(status_code=400, detail="用户未登录!!!") -# return current_user diff --git a/backend/core/__init__.py b/backend/core/__init__.py index d32b14e2eeb690807b8525b13fc17b6e14ba1f2e..9803b2080c668ce67eb4b101c6fc2a728ddf4d88 100644 --- a/backend/core/__init__.py +++ b/backend/core/__init__.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/10/18 19:35 # @Author : zxiaosi -# @desc : 核心文件 -from .config import settings -from .security import create_access_token, verify_password, get_password_hash, check_jwt_token +# @Time : 2022/7/1 14:41 +# @desc : diff --git a/backend/core/config.py b/backend/core/config.py index 251fcb8e30b7e5711eedde2ebcb3ab968e39d2ec..d9ff570a7b4b0a9e5a77b9390d38e9d12ddc5868 100644 --- a/backend/core/config.py +++ b/backend/core/config.py @@ -1,47 +1,18 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 17:02 # @Author : zxiaosi +# @Time : 2022/7/1 14:47 # @desc : 配置文件 -import secrets -from typing import Union, List -from pydantic import BaseSettings, AnyHttpUrl - -project_desc = """ - 🎉 管理员接口汇总 🎉 - ✨ 账号: admin ✨ - ✨ 密码: 123456 ✨ - ✨ 权限(scopes): admin ✨ -""" +from pydantic import BaseSettings class Settings(BaseSettings): - PROJECT_DESC: str = project_desc # 描述 - PROJECT_VERSION: Union[int, str] = 5.0 # 版本 - BASE_URL: AnyHttpUrl = "http://127.0.0.1:8000" # 开发环境 - - API_PREFIX: str = "/api" # 接口前缀 - STATIC_DIR: str = 'static' # 静态文件目录 - GLOBAL_ENCODING: str = 'utf-8' # 全局编码 - CORS_ORIGINS: List[AnyHttpUrl] = ["http://localhost:3000", "http://8.136.82.204:8001"] # 跨域请求 - - SECRET_KEY: str = secrets.token_urlsafe(32) # 密钥(每次重启服务密钥都会改变, token解密失败导致过期, 可设置为常量) - ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 1 # token过期时间: 60 minutes * 24 hours * 1 days = 1 days - - REDIS_URI: str = "redis://:123456@localhost:6379/1" # redis - # DATABASE_URI: str = "sqlite+aiosqlite:///./sql_app.db?check_same_thread=False" # Sqlite(异步) - DATABASE_URI: str = "mysql+asyncmy://root:123456@localhost:3306/elective_system?charset=utf8" # MySQL(异步) - # DATABASE_URI: str = "postgresql+asyncpg://postgres:123456@localhost:5432/postgres" # PostgreSQL(异步) - DATABASE_ECHO: bool = False # 是否打印数据库日志 (可看到创建表、表数据增删改查的信息) - - LOGGER_DIR: str = "logs" # 日志文件夹名 - LOGGER_NAME: str = '{time:YYYY-MM-DD_HH-mm-ss}.log' # 日志文件名 (时间格式) - LOGGER_LEVEL: str = 'DEBUG' # 日志等级: ['DEBUG' | 'INFO'] - LOGGER_ROTATION: str = "12:00" # 日志分片: 按 时间段/文件大小 切分日志. 例如 ["500 MB" | "12:00" | "1 week"] - LOGGER_RETENTION: str = "7 days" # 日志保留的时间: 超出将删除最早的日志. 例如 ["1 days"] + PRJ_DESC: str = "接口文档" # 描述 + PRJ_VERSION: int | str = 1.0 # 版本 - # 权限数据表 (格式务必为 {'名称', '描述'}) - PERMISSION_DATA: List[dict] = [{'admin': '管理员权限'}, {'teacher': '教师权限'}, {'student': '学生权限'}] + API_PRE: str = "/api" # 接口前缀 + DB_URI: str = "mysql://root:123456@localhost:3306/demo?charset=utf8" # MySQL + DB_TZ: str = "Asia/Shanghai" # 时区 class Config: case_sensitive = True # 区分大小写 diff --git a/backend/core/logger.py b/backend/core/logger.py deleted file mode 100644 index 773c64e1a9205a7881dadc9903c118367cb5cd5e..0000000000000000000000000000000000000000 --- a/backend/core/logger.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/8 10:44 -# @Author : zxiaosi -# @desc : 日志 -import os - -from loguru import logger - -from core import settings -from utils import create_dir - - -# 创建日志文件名 -def logger_file() -> str: - """ 创建日志文件名 """ - log_path = create_dir(settings.LOGGER_DIR) - - """ 保留日志文件夹下最大个数(本地调试用) - 本地调式需要多次重启, 日志轮转片不会生效 """ - file_list = os.listdir(log_path) - if len(file_list) > 3: - os.remove(os.path.join(log_path, file_list[0])) - - # 日志输出路径 - return os.path.join(log_path, settings.LOGGER_NAME) - - -# 详见: https://loguru.readthedocs.io/en/stable/overview.html#features -logger.add( - logger_file(), - encoding=settings.GLOBAL_ENCODING, - level=settings.LOGGER_LEVEL, - rotation=settings.LOGGER_ROTATION, - retention=settings.LOGGER_RETENTION, - enqueue=True -) diff --git a/backend/core/security.py b/backend/core/security.py deleted file mode 100644 index ac998a4ec67f06d9df5da7c0e1bb559512a5d1d9..0000000000000000000000000000000000000000 --- a/backend/core/security.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/19 19:49 -# @Author : zxiaosi -# @desc : 安全配置 https://fastapi.tiangolo.com/zh/advanced/security/oauth2-scopes/#global-view -from typing import Any, Union, Optional -from datetime import datetime, timedelta -from fastapi import Header -from jose import jwt -from passlib.context import CryptContext - -from core import settings -from utils import AccessTokenFail - -ALGORITHM = "HS256" # 加密算法 - -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # 加密密码 - - -def get_password_hash(password: str) -> str: - """ 加密明文密码 """ - return pwd_context.hash(password) - - -def verify_password(password: str, hashed_password: str) -> bool: - """ 验证明文密码 与 加密后的密码 是否一致 """ - return pwd_context.verify(password, hashed_password) - - -def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: - """ - 生成token - - :param data: 存储数据 - :param expires_delta: 有效时间 - :return: 加密后的token - """ - to_encode = data.copy() - if expires_delta: - expire = datetime.utcnow() + expires_delta - else: - expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) - to_encode.update({"exp": expire}) # eg: {'sub': '1', scopes: ['items'] 'exp': '123'} - encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM) - return encoded_jwt - - -# https://www.cnblogs.com/CharmCode/p/14191112.html?ivk_sa=1024320u -async def check_jwt_token(token: Optional[str] = Header(...)) -> Union[str, Any]: - """ 解密token """ - try: - payload = jwt.decode(token=token, key=settings.SECRET_KEY, algorithms=[ALGORITHM]) - return payload - except Exception as e: # jwt.JWTError, jwt.ExpiredSignatureError, AttributeError - raise AccessTokenFail(f'token已过期! -- {e}') - - -if __name__ == '__main__': - # 对 '123456' 加密后得到的值不相同 - print(get_password_hash('123456')) - - # 但 加密前 和 加密后 验证是一致的 - print(verify_password('123456', '$2b$12$I5lfn4eO8M0oH4yYQWjSQ.t4VJz9cGKXA.ht6syIG6tAXmbnQywqa')) # True - print(verify_password('123456', '$2b$12$h58wHhABGgNSRfQCqYFod.0mycfuLZIWQmtvKgP9s0VyYs78In6b.')) # True diff --git a/backend/crud/__init__.py b/backend/crud/__init__.py deleted file mode 100644 index cfdf6178c391cd9fc1876f3b524be97d0596d7cf..0000000000000000000000000000000000000000 --- a/backend/crud/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/14 16:59 -# @Author : zxiaosi -# @desc : 数据库的增删改查操作(dao层) -from .base import ModelType, CRUDBase -from .department import department -from .major import major -from .teacher import teacher -from .student import student -from .course import course -from .elective import elective -from .taught import taught -from .admin import admin diff --git a/backend/crud/admin.py b/backend/crud/admin.py deleted file mode 100644 index 4e7bbf72f862b128de8c975e5364444691dc3fcb..0000000000000000000000000000000000000000 --- a/backend/crud/admin.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/17 11:05 -# @Author : zxiaosi -# @desc : 操作管理员表 -from typing import Union, Dict, Any -from sqlalchemy import insert, update -from sqlalchemy.ext.asyncio import AsyncSession - -from core import get_password_hash -from crud import CRUDBase -from models import Admin -from schemas import AdminCreate, AdminUpdate - - -class CRUDAdmin(CRUDBase[Admin, AdminCreate, AdminUpdate]): - async def create(self, db: AsyncSession, obj_in: AdminCreate) -> int: - """ 添加管理员信息 """ - setattr(obj_in, 'id', int(obj_in.id)) # postgresql 字段类型限制 - obj_in_data = {} - for k, v in obj_in.dict().items(): # 排除空值 - if v: - if k == 'password': - obj_in_data['hashed_password'] = get_password_hash(obj_in.password) - else: - obj_in_data[k] = v - sql = insert(self.model).values(obj_in_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def update(self, db: AsyncSession, id: int, obj_in: Union[AdminUpdate, Dict[str, Any]]) -> int: - """ 更新管理员信息 """ - if isinstance(obj_in, dict): # 判断对象是否为字典类型(更新部分字段) - teacher_data = obj_in - else: - teacher_data = obj_in.dict(exclude_unset=True) - obj_data = {} - for k, v in teacher_data.items(): # 排除空值 - if v: - if k == 'password': - obj_data['hashed_password'] = get_password_hash(obj_in.password) - else: - obj_data[k] = v - sql = update(self.model).where(self.model.id == id).values(obj_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - -admin = CRUDAdmin(Admin) diff --git a/backend/crud/base.py b/backend/crud/base.py deleted file mode 100644 index ea37dbf4efe7e696be264c0970ef8b031f3c3f65..0000000000000000000000000000000000000000 --- a/backend/crud/base.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/14 17:00 -# @Author : zxiaosi -# @desc : 封装数据库增删改查方法 -from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union -from pydantic import BaseModel -from sqlalchemy import func, distinct, select, insert, update, desc, delete -from sqlalchemy.ext.asyncio import AsyncSession - -from core import verify_password -from models import Base -from utils import obj_as_dict, list_obj_as_dict - -ModelType = TypeVar("ModelType", bound=Base) -CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) -UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) - - -# db.scalar(sql) 返回的是标量(原始数据) -# db.execute(sql) 返回的是元组 () -# db.scalars(sql).all() [, , ] -# db.execute(sql).fetchall() [(,), (,), (,)] -class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): - """ CRUD 增 查 改 删 """ - - def __init__(self, model: Type[ModelType]): - self.model = model - - async def get(self, db: AsyncSession, id: Any) -> Optional[ModelType]: - """ 通过 id 获取对象 """ - sql = select(self.model).where(self.model.id == id) - result = await db.scalar(sql) - # print(obj_as_dict(await db.scalar(sql))) - await db.close() # 释放会话 - return result - - async def get_multi(self, db: AsyncSession, pageIndex: int = 1, pageSize: int = 10) -> List[ModelType]: - """ 获取第 pageIndex 页的 pageSize 数据 """ - if pageIndex == -1 and pageSize == -1: - sql = select(self.model).order_by(desc(self.model.id)) - else: - sql = select(self.model).offset((pageIndex - 1) * pageSize).limit(pageSize).order_by(desc(self.model.id)) - result = await db.scalars(sql) - # print(list_obj_as_dict(await db.scalars(sql))) - await db.close() # 释放会话 - return result.all() - - async def get_number(self, db: AsyncSession) -> int: - """ 获取表的总条数 """ - sql = select(func.count(distinct(self.model.id))) - result = await db.scalar(sql) - await db.close() # 释放会话 - return result - - async def create(self, db: AsyncSession, obj_in: CreateSchemaType) -> int: - """ 添加对象 """ - if self.model.__tablename__ not in ['taught', 'elective']: - setattr(obj_in, 'id', int(obj_in.id)) # postgresql 字段类型限制 - sql = insert(self.model).values(obj_in.dict()) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def update(self, db: AsyncSession, id: int, obj_in: Union[UpdateSchemaType, Dict[str, Any]]) -> int: - """ 通过 id 更新对象 """ - if isinstance(obj_in, dict): # 判断对象是否为字典类型(更新部分字段) - obj_data = obj_in - else: - obj_data = obj_in.dict() - sql = update(self.model).where(self.model.id == id).values(obj_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def remove(self, db: AsyncSession, id: int) -> int: - """ 通过 id 删除对象 """ - sql = delete(self.model).where(self.model.id == id) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def remove_multi(self, db: AsyncSession, id_list: list): - """ 同时删除多个对象 """ - id_list = [int(i) for i in id_list] # postgresql 字段类型限制 - sql = delete(self.model).where(self.model.id.in_(id_list)) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def get_multi_relation(self, db: AsyncSession): - """ 获取关系字段 """ - sql = select(self.model.id, self.model.name).order_by(desc(self.model.id)).distinct() - result = await db.execute(sql) - await db.close() - return result.fetchall() - - async def get_by_name(self, db: AsyncSession, name: str) -> Optional[ModelType]: - """ 通过名字得到用户 """ - sql = select(self.model).where(self.model.name == name) - result = await db.scalar(sql) - await db.close() # 释放会话 - return result - - async def authenticate(self, db: AsyncSession, username: str, password: str) -> Optional[ModelType]: - """ 验证用户 """ - user = await self.get_by_name(db, name=username) - if not user: - return None - if not verify_password(password, user.hashed_password): - return None - return user - - async def sort(self, db: AsyncSession, name: str, pageIndex: int = 1, pageSize: int = 10) -> List[ModelType]: - """ 验证用户 """ - filed_name = self.model.__table__.c[name] - if pageIndex == -1 and pageSize == -1: - sql = select(self.model).order_by(desc(filed_name)) - else: - sql = select(self.model).offset((pageIndex - 1) * pageSize).limit(pageSize).order_by(desc(filed_name)) - result = await db.scalars(sql) - # print(list_obj_as_dict(await db.scalars(sql))) - await db.close() # 释放会话 - return result.all() diff --git a/backend/crud/course.py b/backend/crud/course.py deleted file mode 100644 index 3f02f4cfeab33b899ab02bedf3ff9372b486b43c..0000000000000000000000000000000000000000 --- a/backend/crud/course.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/17 16:29 -# @Author : zxiaosi -# @desc : 操作课程表 -from crud.base import CRUDBase -from models import Course -from schemas import CourseCreate, CourseUpdate - - -class CRUDCourse(CRUDBase[Course, CourseCreate, CourseUpdate]): - pass - - -course = CRUDCourse(Course) diff --git a/backend/crud/department.py b/backend/crud/department.py deleted file mode 100644 index 8ec4aea7dacd67354aca818ddcc0550ab6877c26..0000000000000000000000000000000000000000 --- a/backend/crud/department.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/10/28 19:01 -# @Author : zxiaosi -# @desc : 操作院系表 -from crud import CRUDBase -from models import Department -from schemas import DepartmentCreate, DepartmentUpdate - - -class CRUDDepartment(CRUDBase[Department, DepartmentCreate, DepartmentUpdate]): - pass - - -department = CRUDDepartment(Department) diff --git a/backend/crud/elective.py b/backend/crud/elective.py deleted file mode 100644 index 2af6690d9c0855662b4c84ae586b35807c36cdf1..0000000000000000000000000000000000000000 --- a/backend/crud/elective.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/18 10:09 -# @Author : zxiaosi -# @desc : 操作选课表 -from typing import Any, Union - -from sqlalchemy import select, insert -from sqlalchemy.ext.asyncio import AsyncSession - -from crud.base import CRUDBase -from models import Elective, Taught, Student, Teacher, Course -from schemas import ElectiveCreate, ElectiveUpdate -from utils import obj_as_dict, list_obj_as_dict - - -class CRUDElective(CRUDBase[Elective, ElectiveCreate, ElectiveUpdate]): - async def get_course(self, db: AsyncSession, id: Any) -> Any: - """ 得到课程详情 """ - fields = [self.model.courseId, Course.name.label('courseName'), - Teacher.id.label('teacherId'), Teacher.name.label('teacherName'), - self.model.grade, self.model.create_time] - sql = select(*fields).where(self.model.studentId == int(id)) \ - .join(Course, self.model.courseId == Course.id) \ - .join(Taught, self.model.courseId == Taught.courseId, isouter=True) \ - .join(Teacher, Taught.teacherId == Teacher.id, isouter=True) - print(id, sql) - result = await db.execute(sql) - # print(list_obj_as_dict(result.all())) - await db.close() # 释放会话 - return result.fetchall() - - async def is_exist(self, db: AsyncSession, studentId: int, courseId: int) -> int: - """ 判断数据是否已经存在 """ - sql1 = select(self.model) \ - .where(self.model.studentId == studentId) \ - .where(self.model.courseId == courseId) - result = await db.scalar(sql1) - return result - - async def create(self, db: AsyncSession, obj_in: Union[Elective]) -> int: - """ 添加对象 """ - if isinstance(obj_in, dict): # 判断对象是否为字典类型(更新部分字段) - obj_data = obj_in - else: - obj_data = obj_in.dict() - sql = insert(self.model).values(obj_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - -elective = CRUDElective(Elective) diff --git a/backend/crud/major.py b/backend/crud/major.py deleted file mode 100644 index 34031436b047d3c0c600e49bde35a0eafbf51c6e..0000000000000000000000000000000000000000 --- a/backend/crud/major.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/1 21:03 -# @Author : zxiaosi -# @desc : 操作专业表 -from crud import CRUDBase -from models import Major -from schemas import MajorCreate, MajorUpdate - - -class CRUDMajor(CRUDBase[Major, MajorCreate, MajorUpdate]): - pass - - -major = CRUDMajor(Major) diff --git a/backend/crud/student.py b/backend/crud/student.py deleted file mode 100644 index 74baeaa0a506c0d5a4c8536e02d407d0eb24cd3d..0000000000000000000000000000000000000000 --- a/backend/crud/student.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/17 11:05 -# @Author : zxiaosi -# @desc : 操作学生表 -from typing import Union, Dict, Any - -from sqlalchemy import update, insert, select -from sqlalchemy.ext.asyncio import AsyncSession - -from core import get_password_hash -from crud import CRUDBase -from models import Student, Major -from schemas import StudentCreate, StudentUpdate -from utils import obj_as_dict - - -class CRUDStudent(CRUDBase[Student, StudentCreate, StudentUpdate]): - async def create(self, db: AsyncSession, obj_in: StudentCreate) -> int: - """ 添加学生信息 """ - setattr(obj_in, 'id', int(obj_in.id)) # postgresql 字段类型限制 - obj_in_data = {} - for k, v in obj_in.dict().items(): # 排除空值 - if v: - if k == 'password': - obj_in_data['hashed_password'] = get_password_hash(obj_in.password) - else: - obj_in_data[k] = v - sql = insert(self.model).values(obj_in_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def update(self, db: AsyncSession, id: int, obj_in: Union[StudentUpdate, Dict[str, Any]]) -> int: - """ 更新学生信息 """ - if isinstance(obj_in, dict): # 判断对象是否为字典类型(更新部分字段) - teacher_data = obj_in - else: - teacher_data = obj_in.dict(exclude_unset=True) - obj_data = {} - for k, v in teacher_data.items(): # 排除空值 - if v: - if k == 'password': - obj_data['hashed_password'] = get_password_hash(obj_in.password) - else: - obj_data[k] = v - sql = update(self.model).where(self.model.id == id).values(obj_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - # async def get_detail(self, db: AsyncSession, id: int): - # sql = select(self.model, Major.name.label("major_name")) \ - # .join(Major, self.model.majorId == Major.id) \ - # .where(self.model.id == id) - # result = await db.execute(sql) - # await db.close() # 释放会话 - # return result.fetchone() - - -student = CRUDStudent(Student) diff --git a/backend/crud/taught.py b/backend/crud/taught.py deleted file mode 100644 index 99f560a64bb230829fb05c9dc8a10ec0fc56eccb..0000000000000000000000000000000000000000 --- a/backend/crud/taught.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/5/10 21:00 -# @Author : zxiaosi -# @desc : 操作讲授表 -from typing import Any - -from sqlalchemy import select -from sqlalchemy.ext.asyncio import AsyncSession - -from crud import CRUDBase -from models import Taught, Course, Student, Elective -from schemas import TaughtCreate, TaughtUpdate - - -class CRUDTaught(CRUDBase[Taught, TaughtCreate, TaughtUpdate]): - async def get_course(self, db: AsyncSession, id: Any) -> Any: - """ 得到课程详情 """ - fields = [self.model.courseId, Course.name.label('courseName'), - Elective.studentId, Student.name.label('studentName'), - Elective.id, Elective.grade, Elective.update_time] - sql = select(*fields).where(self.model.teacherId == int(id)) \ - .join(Elective, self.model.courseId == Elective.courseId, isouter=True) \ - .join(Course, self.model.courseId == Course.id) \ - .join(Student, Elective.studentId == Student.id) - result = await db.execute(sql) - # print(list_obj_as_dict(result.all())) - await db.close() # 释放会话 - return result.fetchall() - - -taught = CRUDTaught(Taught) diff --git a/backend/crud/teacher.py b/backend/crud/teacher.py deleted file mode 100644 index 9352fb26158149b88c536c07cfcaa3570cddaf8f..0000000000000000000000000000000000000000 --- a/backend/crud/teacher.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/15 19:53 -# @Author : zxiaosi -# @desc : 操作教师表 -from typing import Union, Dict, Any - -from sqlalchemy import insert, update -from sqlalchemy.ext.asyncio import AsyncSession - -from core import get_password_hash -from crud.base import CRUDBase -from models import Teacher -from schemas import TeacherCreate, TeacherUpdate - - -class CRUDTeacher(CRUDBase[Teacher, TeacherCreate, TeacherUpdate]): - async def create(self, db: AsyncSession, obj_in: TeacherCreate) -> int: - """ 添加教师信息 """ - setattr(obj_in, 'id', int(obj_in.id)) # postgresql 字段类型限制 - obj_in_data = {} - for k, v in obj_in.dict().items(): # 排除空值 - if v: - if k == 'password': - obj_in_data['hashed_password'] = get_password_hash(obj_in.password) - else: - obj_in_data[k] = v - sql = insert(self.model).values(obj_in_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - async def update(self, db: AsyncSession, id: int, obj_in: Union[TeacherUpdate, Dict[str, Any]]) -> int: - """ 更新教师信息 """ - if isinstance(obj_in, dict): # 判断对象是否为字典类型(更新部分字段) - teacher_data = obj_in - else: - teacher_data = obj_in.dict(exclude_unset=True) - obj_data = {} - for k, v in teacher_data.items(): # 排除空值 - if v: - if k == 'password': - obj_data['hashed_password'] = get_password_hash(obj_in.password) - else: - obj_data[k] = v - sql = update(self.model).where(self.model.id == id).values(obj_data) - result = await db.execute(sql) - await db.commit() - return result.rowcount - - -teacher = CRUDTeacher(Teacher) diff --git a/backend/db/__init__.py b/backend/db/__init__.py deleted file mode 100644 index 485a4b01f270a9f2597c971799d86fdbf51cc127..0000000000000000000000000000000000000000 --- a/backend/db/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 17:14 -# @Author : zxiaosi -# @desc : 初始数据库以及表数据 -from .session import engine, async_session -from .redis import MyRedis, init_redis_pool -from .init_db import init_db, drop_db, init_data diff --git a/backend/db/data.py b/backend/db/data.py deleted file mode 100644 index 6743991e58839de7c54d2c6dda4813366988bb3d..0000000000000000000000000000000000000000 --- a/backend/db/data.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/25 23:00 -# @Author : zxiaosi -# @desc : 假数据 -""" 免责声明: 数据来自网络,如有侵权,请联系删除!!! """ -from datetime import date - -departmentData = [ - {'id': 1001, 'name': '计算机工程学院', 'chairman': '张强', 'phone': '13312312312'}, - {'id': 1002, 'name': '医学院', 'chairman': '马建国', 'phone': '13312312312'}, -] - -majorData = [ - # 计算机工程学院 - {'id': 100101, 'name': '计算机科学与技术', 'assistant': '钱伟', 'phone': '13312312312', 'departmentId': 1001}, - {'id': 100102, 'name': '物联网工程', 'assistant': '吴晓红', 'phone': '13312312312', 'departmentId': 1001}, - {'id': 100103, 'name': '软件工程技术', 'assistant': '柯刚', 'phone': '13312312312', 'departmentId': 1001}, - # 医学院 - {'id': 100201, 'name': '中药学专业', 'assistant': '赵露', 'phone': '13312312312', 'departmentId': 1002}, - {'id': 100202, 'name': '护理学专业', 'assistant': '朱壮', 'phone': '13312312312', 'departmentId': 1002}, - {'id': 100203, 'name': '针灸推拿学专业', 'assistant': '李勇', 'phone': '13312312312', 'departmentId': 1002}, -] - -studentData = [ - # Sqlite: 'birthday': date(1999, 9, 9) - # Mysql: 'birthday': '1999-09-09' - - # 计算机科学与技术 - {'id': 1810010101, 'name': '王芳', 'sex': '1', 'birthday': date(1999, 9, 9), 'majorId': 100101}, - {'id': 1810010102, 'name': '孙悟空', 'sex': '0', 'birthday': date(1999, 5, 23), 'majorId': 100101}, - {'id': 1810010103, 'name': '猪悟能', 'sex': '0', 'birthday': date(1998, 2, 12), 'majorId': 100101}, - {'id': 1810010104, 'name': '唐三葬', 'sex': '0', 'birthday': date(2000, 1, 7), 'majorId': 100101}, - {'id': 1810010105, 'name': '张悟本能', 'sex': '0', 'birthday': date(2000, 3, 29), 'majorId': 100101}, - # 物联网工程 - {'id': 1810010201, 'name': '宋清江', 'sex': '1', 'birthday': date(2000, 2, 10), 'majorId': 100102}, - {'id': 1810010202, 'name': '甄无用', 'sex': '0', 'birthday': date(1999, 6, 21), 'majorId': 100102}, - {'id': 1810010203, 'name': '关不胜', 'sex': '0', 'birthday': date(1999, 7, 19), 'majorId': 100102}, - {'id': 1810010204, 'name': '秦复明', 'sex': '1', 'birthday': date(2001, 1, 26), 'majorId': 100102}, - {'id': 1810010205, 'name': '花繁荣', 'sex': '1', 'birthday': date(2000, 5, 27), 'majorId': 100102}, - # 软件工程技术 - {'id': 1810010301, 'name': '柴进门', 'sex': '0', 'birthday': date(1999, 4, 26), 'majorId': 100103}, - {'id': 1810010302, 'name': '李天应', 'sex': '1', 'birthday': date(1999, 6, 4), 'majorId': 100103}, - {'id': 1810010303, 'name': '董不平', 'sex': '0', 'birthday': date(1999, 9, 30), 'majorId': 100103}, - {'id': 1810010304, 'name': '徐宁静', 'sex': '1', 'birthday': date(2000, 8, 20), 'majorId': 100103}, - {'id': 1810010305, 'name': '索超越', 'sex': '0', 'birthday': date(1999, 7, 20), 'majorId': 100103}, - # 中药学专业 - {'id': 1810020101, 'name': '刘唐氏', 'sex': '1', 'birthday': date(1999, 1, 17), 'majorId': 100201}, - {'id': 1810020102, 'name': '李逵悟', 'sex': '0', 'birthday': date(1999, 5, 13), 'majorId': 100201}, - {'id': 1810020103, 'name': '阮小三', 'sex': '0', 'birthday': date(1999, 9, 16), 'majorId': 100201}, - {'id': 1810020104, 'name': '石秀气', 'sex': '1', 'birthday': date(2000, 1, 18), 'majorId': 100201}, - {'id': 1810020105, 'name': '谢宝庆', 'sex': '0', 'birthday': date(2000, 2, 25), 'majorId': 100201}, - # 护理学专业 - {'id': 1810020201, 'name': '燕青青', 'sex': '1', 'birthday': date(2000, 7, 29), 'majorId': 100202}, - {'id': 1810020202, 'name': '朱文武', 'sex': '0', 'birthday': date(1999, 2, 12), 'majorId': 100202}, - {'id': 1810020203, 'name': '鲍旭日', 'sex': '0', 'birthday': date(1999, 8, 25), 'majorId': 100202}, - {'id': 1810020204, 'name': '孔明亮', 'sex': '1', 'birthday': date(1999, 3, 12), 'majorId': 100202}, - {'id': 1810020205, 'name': '童威猛', 'sex': '0', 'birthday': date(1999, 4, 16), 'majorId': 100202}, - # 针灸推拿学专业 - {'id': 1810020301, 'name': '朱富贵', 'sex': '0', 'birthday': date(1998, 2, 15), 'majorId': 100203}, - {'id': 1810020302, 'name': '孙大嫂', 'sex': '1', 'birthday': date(1999, 3, 27), 'majorId': 100203}, - {'id': 1810020303, 'name': '王小二', 'sex': '0', 'birthday': date(1999, 6, 28), 'majorId': 100203}, - {'id': 1810020304, 'name': '白胜利', 'sex': '1', 'birthday': date(1999, 8, 10), 'majorId': 100203}, - {'id': 1810020305, 'name': '段金柱', 'sex': '0', 'birthday': date(1999, 2, 12), 'majorId': 100203}, -] - -teacherData = [ - # 计算机工程学院 - {'id': 180101, 'name': '陈江川', 'sex': '0', 'birthday': date(1988, 9, 9), 'education': '1', 'title': '3', - 'departmentId': 1001}, - {'id': 180102, 'name': '李小平', 'sex': '0', 'birthday': date(1993, 8, 17), 'education': '2', 'title': '2', - 'departmentId': 1001}, - {'id': 180103, 'name': '赵希明', 'sex': '1', 'birthday': date(1988, 3, 28), 'education': '3', 'title': '4', - 'departmentId': 1001}, - {'id': 180104, 'name': '张红', 'sex': '1', 'birthday': date(1995, 6, 15), 'education': '1', 'title': '1', - 'departmentId': 1001}, - # 医学院 - {'id': 180201, 'name': '吴小龚', 'sex': '1', 'birthday': date(1988, 4, 20), 'education': '2', 'title': '2', - 'departmentId': 1002}, - {'id': 180202, 'name': '张进明', 'sex': '0', 'birthday': date(1989, 10, 29), 'education': '1', 'title': '1', - 'departmentId': 1002}, - {'id': 180203, 'name': '李历宁', 'sex': '0', 'birthday': date(1996, 3, 19), 'education': '1', 'title': '3', - 'departmentId': 1002}, -] - -courseData = [ - {'id': 1101, 'name': '计算机导论', 'credit': 2, 'period': 16}, - {'id': 1102, 'name': '计算机网络', 'credit': 2, 'period': 16}, - {'id': 1103, 'name': 'Java', 'credit': 4, 'period': 16}, - {'id': 1104, 'name': 'C语言', 'credit': 4, 'period': 16}, - {'id': 1201, 'name': '基础化学', 'credit': 2, 'period': 16}, - {'id': 1202, 'name': '生理学', 'credit': 3, 'period': 16}, - {'id': 1203, 'name': '中医药基础', 'credit': 4, 'period': 64}, - {'id': 1301, 'name': '有机化学实验', 'credit': 2, 'period': 32}, - {'id': 1302, 'name': '方剂学', 'credit': 4, 'period': 64}, - {'id': 1303, 'name': '药用化学', 'credit': 2, 'period': 32}, - {'id': 1401, 'name': '马克思主义基本原理概论', 'credit': 2, 'period': 32}, - {'id': 1402, 'name': '大学生心理健康教育', 'credit': 2, 'period': 32}, - {'id': 1403, 'name': '职业生涯与发展规划', 'credit': 1, 'period': 16}, - {'id': 1404, 'name': '形式与政策', 'credit': 1, 'period': 16}, -] - -taughtData = [ - {'teacherId': 180101, 'courseId': 1101}, - {'teacherId': 180101, 'courseId': 1102}, - {'teacherId': 180102, 'courseId': 1103}, - {'teacherId': 180102, 'courseId': 1104}, -] - -electiveData = [ - {'studentId': 1810010101, 'courseId': 1101}, - {'studentId': 1810010101, 'courseId': 1102}, - {'studentId': 1810010102, 'courseId': 1102}, - {'studentId': 1810010201, 'courseId': 1103}, - {'studentId': 1810010202, 'courseId': 1104}, - {'studentId': 1810020301, 'courseId': 1201}, - {'studentId': 1810020302, 'courseId': 1202}, -] - -adminData = [{'name': 'admin'}] diff --git a/backend/db/init_db.py b/backend/db/init_db.py deleted file mode 100644 index b537e877c50cd0b66ece047e8300b81ee0ff6619..0000000000000000000000000000000000000000 --- a/backend/db/init_db.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/25 22:08 -# @Author : zxiaosi -# @desc : 创建与删除所有表, 初始化数据 -from core.logger import logger -from db import engine -from db.data import * -from models import Base, Department, Major, Student, Teacher, Admin, Course, Taught, Elective - - -async def init_db(): - """ 创建 models/__init__ 下的所有表 """ - try: - await drop_db() # 删除所有的表 - async with engine.begin() as conn: - await conn.run_sync(Base.metadata.create_all) - logger.info("创建表成功!!!") - except Exception as e: - logger.error(f"创建表失败!!! -- 错误信息如下:\n{e}") - finally: - await engine.dispose() - - -async def drop_db(): - """ 删除 models/__init__ 下的所有表 """ - try: - async with engine.begin() as conn: - await conn.run_sync(Base.metadata.drop_all) - logger.info("删除表成功!!!") - except Exception as e: - logger.error(f"删除表失败!!! -- 错误信息如下:\n{e}") - finally: - await engine.dispose() - - -async def init_data(): - """ 初始化表数据 """ - try: - async with engine.begin() as conn: - await conn.execute(Department.__table__.insert(), [department for department in departmentData]) - await conn.execute(Major.__table__.insert(), [major for major in majorData]) - await conn.execute(Student.__table__.insert(), [student for student in studentData]) - await conn.execute(Teacher.__table__.insert(), [teacher for teacher in teacherData]) - await conn.execute(Admin.__table__.insert(), [admin for admin in adminData]) - await conn.execute(Course.__table__.insert(), [course for course in courseData]) - await conn.execute(Taught.__table__.insert(), [selectCourse for selectCourse in taughtData]) - await conn.execute(Elective.__table__.insert(), [selectCourse for selectCourse in electiveData]) - logger.info(f"成功初始化表数据!!!") - except Exception as e: - logger.error(f"初始化表数据失败!!! -- 错误信息如下:\n{e}") - finally: - await engine.dispose() diff --git a/backend/db/redis.py b/backend/db/redis.py deleted file mode 100644 index 019f776e347e09f9a59e8e4e1bafdf59b7248a80..0000000000000000000000000000000000000000 --- a/backend/db/redis.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/2/24 10:09 -# @Author : zxiaosi -# @desc : redis -import json -from typing import Union -from aioredis import Redis - -from core import settings - - -class MyRedis(Redis): - """ 继承Redis,并添加自己的方法 """ - - # 写 __init__ 的话就取消下面注释 - # def __init__(self, connection_pool): - # # print(connection_pool) - # super(RedisPlus, self).__init__(connection_pool=connection_pool) - - async def list_loads(self, key: str, num: int = -1) -> list: - """ - 将列表字符串转为对象 - - :param key: 列表的key - :param num: 最大长度(默认值 0-全部) - :return: 列表对象 - """ - todo_list = await self.lrange(key, 0, (num - 1) if num > -1 else num) - return [json.loads(todo) for todo in todo_list] - - async def cus_lpush(self, key: str, value: Union[str, list, dict]): - """ - 向列表右侧插入数据 - - :param key: 列表的key - :param value: 插入的值 - """ - text = json.dumps(value) - await self.lpush(key, text) - - async def get_list_by_index(self, key: str, id: int) -> object: - """ - 根据索引得到列表值 - - :param key: 列表的值 - :param id: 索引值 - :return: - """ - value = await self.lindex(key, id) - return json.loads(value) - - -# 参考: https://github.com/grillazz/fastapi-redis/tree/main/app -async def init_redis_pool() -> MyRedis: - """ 连接redis """ - redis = await MyRedis.from_url(url=settings.REDIS_URI, encoding=settings.GLOBAL_ENCODING, decode_responses=True) - return redis diff --git a/backend/db/session.py b/backend/db/session.py deleted file mode 100644 index 955484d35d34e8648b694acb3ecf2907e7cbe194..0000000000000000000000000000000000000000 --- a/backend/db/session.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 17:27 -# @Author : zxiaosi -# @desc : 数据库会话 -# https://www.osgeo.cn/sqlalchemy/orm/extensions/asyncio.html?highlight=async#asynchronous-i-o-asyncio -from asyncio import current_task - -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_scoped_session -from sqlalchemy.orm import sessionmaker - -from core import settings - -# 创建表引擎 -engine = create_async_engine( - url=settings.DATABASE_URI, # 数据库uri - echo=settings.DATABASE_ECHO, # 是否打印日志 - # pool_size=10, # 队列池个数 - # max_overflow=20, # 队列池最大溢出个数 -) - -# 操作表会话 -async_session_factory = sessionmaker( - bind=engine, - class_=AsyncSession, - expire_on_commit=False # 防止提交后属性过期 -) - -async_session = async_scoped_session(async_session_factory, scopefunc=current_task) diff --git a/backend/main.py b/backend/main.py index 07e59bbcd2a76df9b0277bd4c89934b0f9bb70e5..746da1c22a0ffa7f9fe2b324b36198a94c6d104d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,47 +1,51 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2022/4/17 17:08 # @Author : zxiaosi +# @Time : 2022/7/1 14:33 # @desc : 主函数 import uvicorn from fastapi import FastAPI +from loguru import logger +from tortoise import Tortoise -from core import settings -from core.logger import logger -from db import init_db, init_data, init_redis_pool -from register import register_mount, register_exception, register_cors, register_middleware, register_router +from apis import debug +from core.config import settings +from models.user import User, SexEnum +from utils.file_util import get_models_file -app = FastAPI(description=settings.PROJECT_DESC, version=settings.PROJECT_VERSION) +app = FastAPI(title=settings.PRJ_DESC, version=settings.PRJ_VERSION) - -def create_app(): - """ 注册中心 """ - register_mount(app) # 挂载静态文件 - - register_exception(app) # 注册捕获全局异常 - - register_router(app) # 注册路由 - - register_middleware(app) # 注册请求响应拦截 - - register_cors(app) # 注册跨域请求 - - logger.info("日志初始化成功!!!") # 初始化日志 +app.include_router(debug.router, prefix=settings.API_PRE) @app.on_event("startup") -async def startup(): - create_app() # 加载注册中心 - # await init_db() # 初始化表 - # await init_data() # 初始化数据 - app.state.redis = await init_redis_pool() # redis +async def startup_event(): + # https://github.com/tortoise/tortoise-orm/issues/872 + logger.success("💡💡💡 项目启动 💡💡💡") + + await Tortoise.init(db_url=settings.DB_URI, + modules={"models": [ + "models.user", + "models.permission", + "models.role", + "models.user_role", + "models.role_permission", + ]}, + timezone=settings.DB_TZ) + await Tortoise.generate_schemas() + # 批量插入数据 + # await User.bulk_create([ + # User(name="admin", sex=SexEnum.man), + # User(name="zxiaosi", sex=SexEnum.woman), + # ]) @app.on_event("shutdown") -async def shutdown(): - await app.state.redis.close() # 关闭 redis +async def shutdown_event(): + await Tortoise.close_connections() + logger.success("项目关闭") if __name__ == '__main__': - uvicorn.run(app='main:app', host="127.0.0.1", port=8000) - # uvicorn.run(app='main:app', host="127.0.0.1", port=8000, debug=True, reload=True) + uvicorn.run(app="main:app", host="127.0.0.1", port=8000) + # uvicorn.run(app="main:app", host="127.0.0.1", port=8000, reload=True, debug=True, workers=5) diff --git a/backend/models/__init__.py b/backend/models/__init__.py index a5b212a6c298169132b1d8ce1669dd8b8b0dc672..3e50d3b4bb7a385c667b8952c705dd22966642d6 100644 --- a/backend/models/__init__.py +++ b/backend/models/__init__.py @@ -1,14 +1,5 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 17:12 # @Author : zxiaosi -# @desc : ORM模型对象(创建/删除表的顺序, 慎动) -from .base import Base -from .department import Department -from .major import Major -from .teacher import Teacher -from .student import Student -from .admin import Admin -from .course import Course -from .taught import Taught -from .elective import Elective +# @Time : 2022/7/1 14:40 +# @desc : diff --git a/backend/models/admin.py b/backend/models/admin.py deleted file mode 100644 index 3182ef9bd39e0416293fe42513283c0f2ca0dac8..0000000000000000000000000000000000000000 --- a/backend/models/admin.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:24 -# @Author : zxiaosi -# @desc : 管理员表 -from sqlalchemy import Column, Integer, String, text - -from core import settings, get_password_hash -from models import Base - - -class Admin(Base): - """ 管理员表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='编号') - - name = Column(String(10), nullable=False, index=True, comment='姓名') - - address = Column(String(20), server_default='广东省广州市', comment='上次登录地址') - - image = Column(String(60), server_default=f'{settings.BASE_URL}/{settings.STATIC_DIR}/author.jpg', comment='头像') - - hashed_password = Column(String(60), server_default=get_password_hash('123456'), comment='密码') diff --git a/backend/models/base.py b/backend/models/base.py index bb508c59f9aa638479917a15d0412585f1fb9ad8..85f71ac224463a6a2264692fa079f5a1b961e442 100644 --- a/backend/models/base.py +++ b/backend/models/base.py @@ -1,31 +1,27 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 17:14 # @Author : zxiaosi -# @desc : ORM父类 -from sqlalchemy import Column, DateTime, func -from sqlalchemy.ext.declarative import as_declarative, declared_attr +# @Time : 2022/7/1 16:20 +# @desc : ORM抽象类 +from tortoise import Model +from tortoise.fields import DatetimeField +from utils.date_util import handle_datetime -# id 也能写在这里, 不过为了方便看字段, 就在每个表内创建了 -# https://www.osgeo.cn/sqlalchemy/orm/mapping_api.html#sqlalchemy.orm.as_declarative -@as_declarative() -class Base: - """ 基本表 """ - __name__: str # 表名 - __table_args__ = {"mysql_charset": "utf8"} # 设置表的字符集 - # __mapper_args__ = {"eager_defaults": True} # 防止 insert 插入后不刷新 +class Base(Model): + created = DatetimeField(auto_now_add=True, description="创建时间") + modified = DatetimeField(auto_now=True, description="修改时间") - # 将类名小写并转化为表名 __tablename__ - @declared_attr - def __tablename__(cls) -> str: - return cls.__name__.lower() + def created_at(self) -> str: # 返回类型一定要带 + return handle_datetime(self.created) - @declared_attr - def create_time(cls): # 创建时间 - return Column(DateTime(timezone=True), server_default=func.now(), comment='创建时间') + def modified_at(self) -> str: + return handle_datetime(self.modified) - @declared_attr - def update_time(cls): # 更新时间 - return Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), comment='更新时间') + class PydanticMeta: + computed = ["created_at", "modified_at"] + exclude = ["created", "modified"] + + class Meta: + abstract = True # 抽象类 diff --git a/backend/models/course.py b/backend/models/course.py deleted file mode 100644 index fb04476b65d543e4555ee5493690250896bdb979..0000000000000000000000000000000000000000 --- a/backend/models/course.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:24 -# @Author : zxiaosi -# @desc : 课程表 -from sqlalchemy import Column, String, Float, SmallInteger, Integer -from sqlalchemy.orm import relationship - -from models import Base - - -class Course(Base): - """ 课程表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='编号') - - name = Column(String(20), unique=True, nullable=False, comment='名字') - - credit = Column(Float, default=0, server_default='0', comment='学分') - - period = Column(SmallInteger, server_default='0', comment='课时') - - taught = relationship('Taught') # 不是字段, 可以通过 course ORM对象引用 taught 表的类集合 - - elective = relationship('Elective') # 不是字段, 可以通过 elective ORM对象引用 taught 表的类集合 diff --git a/backend/models/department.py b/backend/models/department.py deleted file mode 100644 index a9df47086771fd693da4f560a246ce9515c2a4b9..0000000000000000000000000000000000000000 --- a/backend/models/department.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:23 -# @Author : zxiaosi -# @desc : 院系表 -from sqlalchemy import Column, Integer, String -from sqlalchemy.orm import relationship - -from models import Base - - -class Department(Base): - """ 院系表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='编号') - - name = Column(String(20), unique=True, nullable=False, index=True, comment='名字') - - chairman = Column(String(10), nullable=False, comment='主任姓名') - - phone = Column(String(11), comment='主任手机号') - - majors = relationship('Major') # 不是字段, 可以通过 department ORM对象引用 major 表的类集合 - - teachers = relationship('Teacher') # 不是字段, 可以通过 department ORM对象引用 teacher 表的类集合 diff --git a/backend/models/elective.py b/backend/models/elective.py deleted file mode 100644 index 293e0c6b2929d2dc2e9ba0a5c86feb28805053a3..0000000000000000000000000000000000000000 --- a/backend/models/elective.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:24 -# @Author : zxiaosi -# @desc : 选课表 -from sqlalchemy import Column, ForeignKey, Integer - -from models import Base - - -class Elective(Base): - """ 选课表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='编号') - - grade = Column(Integer, default=0, server_default='0', comment='成绩') - - studentId = Column(Integer, ForeignKey('student.id', ondelete='CASCADE'), nullable=False, comment='学号') - - courseId = Column(Integer, ForeignKey('course.id', ondelete='CASCADE'), nullable=False, comment='课程编号') diff --git a/backend/models/major.py b/backend/models/major.py deleted file mode 100644 index 13484f26f5e87a78575a44e92d73a82e08a3fe8a..0000000000000000000000000000000000000000 --- a/backend/models/major.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:23 -# @Author : zxiaosi -# @desc : 专业表 -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.orm import relationship - -from models import Base - - -class Major(Base): - """ 专业表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='编号') - - name = Column(String(20), unique=True, nullable=False, comment='名字') - - assistant = Column(String(10), nullable=False, comment='辅导员姓名') - - phone = Column(String(11), comment='辅导员手机号') - - departmentId = Column(Integer, ForeignKey('department.id', ondelete='CASCADE'), nullable=False, comment='院系编号') - - students = relationship('Student') # 不是字段, 可以通过 major ORM对象引用 student 表的类集合 diff --git a/backend/models/permission.py b/backend/models/permission.py new file mode 100644 index 0000000000000000000000000000000000000000..51efbababf0bea148bb32f341e77e318576984d4 --- /dev/null +++ b/backend/models/permission.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/4 22:06 +# @desc : 权限表 +from tortoise.fields import IntField, CharField, ReverseRelation + +from models.base import Base + + +class Permission(Base): + name = CharField(max_length=30, unique=True, description="菜单名") + pid = IntField(description="父级id") + code = CharField(max_length=20, description="权限编号") + page = CharField(max_length=50, description="菜单url") + z_index = IntField(description="排序") + + role_permission = ReverseRelation["models.role_permission"] + + class Meta: + table = "permission" + table_description = "权限表" diff --git a/backend/models/role.py b/backend/models/role.py new file mode 100644 index 0000000000000000000000000000000000000000..f0d9027c8e174ff38f017f7586e9d2756a5893a7 --- /dev/null +++ b/backend/models/role.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/4 22:00 +# @desc : 角色表 +from tortoise.fields import CharField, ReverseRelation + +from models.base import Base + + +class Role(Base): + name = CharField(max_length=30, unique=True, description="角色名") + desc = CharField(max_length=50, description="描述") + code = CharField(max_length=20, description="角色编号") + + user_role = ReverseRelation["models.user_role"] + role_permission = ReverseRelation["models.role_permission"] + + class Meta: + table = "role" + table_description = "角色表" diff --git a/backend/models/role_permission.py b/backend/models/role_permission.py new file mode 100644 index 0000000000000000000000000000000000000000..f24b6c7f81a3d672dd3cb86091cb93d507409a44 --- /dev/null +++ b/backend/models/role_permission.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/4 22:24 +# @desc : 角色权限表 +from tortoise import Model +from tortoise.fields import ForeignKeyRelation, ForeignKeyField + +from models.permission import Permission +from models.role import Role + + +class RolePermission(Model): + role_id: ForeignKeyRelation[Role] = ForeignKeyField( + "models.Role", + related_name="role_permission", + description="角色id" + ) + + permission_id: ForeignKeyRelation[Permission] = ForeignKeyField( + "models.Permission", + related_name="role_permission", + description="权限id" + ) + + class Meta: + table = "role_permission" + table_description = "角色权限表" diff --git a/backend/models/student.py b/backend/models/student.py deleted file mode 100644 index 0ad8b8db5e470a06c0db981306a3f554773bce04..0000000000000000000000000000000000000000 --- a/backend/models/student.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 13:48 -# @Author : zxiaosi -# @desc : 学生表 -from datetime import date - -from sqlalchemy import Column, Integer, String, Date, text, ForeignKey -from sqlalchemy.orm import relationship - -from core import settings, get_password_hash -from models import Base -from utils import check_or_enum - - -class Student(Base): - """ 学生表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='学号') - - name = Column(String(10), unique=False, nullable=False, comment='姓名') - - sex = check_or_enum(name='sex', enumList=['0', '1'], comment='性别: 0->男, 1->女') - - birthday = Column(Date, default=date(2012, 1, 1), comment='生日') - - address = Column(String(20), server_default=text("'广东省广州市'"), comment='地址') - - image = Column(String(60), server_default=f'{settings.BASE_URL}/{settings.STATIC_DIR}/author.jpg', comment='头像') - - hashed_password = Column(String(60), server_default=get_password_hash('123456'), comment='密码') - - majorId = Column(Integer, ForeignKey('major.id', ondelete='CASCADE'), nullable=False, comment='专业编号') - - elective = relationship('Elective') # 不是字段, 可以通过 student ORM对象引用 elective 表的类集合 diff --git a/backend/models/taught.py b/backend/models/taught.py deleted file mode 100644 index 3ead50e554891f887c0534068d7db679e28203da..0000000000000000000000000000000000000000 --- a/backend/models/taught.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:24 -# @Author : zxiaosi -# @desc : 讲授表 -from sqlalchemy import Column, ForeignKey, Integer - -from models import Base - - -class Taught(Base): - """ 讲授表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='编号') - - teacherId = Column(Integer, ForeignKey('teacher.id', ondelete='CASCADE'), comment='职工号') - - courseId = Column(Integer, ForeignKey('course.id', ondelete='CASCADE'), comment='课程编号') diff --git a/backend/models/teacher.py b/backend/models/teacher.py deleted file mode 100644 index de4e3e915cd774e8ff3466c00fde7c28a2826f0c..0000000000000000000000000000000000000000 --- a/backend/models/teacher.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/19 22:24 -# @Author : zxiaosi -# @desc : 教师表 -from datetime import date -from sqlalchemy import Column, String, Integer, Date, ForeignKey -from sqlalchemy.orm import relationship - -from core import settings, get_password_hash -from models import Base -from utils import check_or_enum - - -class Teacher(Base): - """ 教师表 """ - - id = Column(Integer, primary_key=True, autoincrement=True, index=True, comment='职工号') - - name = Column(String(10), unique=True, nullable=False, comment='姓名') - - sex = check_or_enum(name='sex', enumList=['0', '1'], comment='性别: 0->男, 1->女') - - birthday = Column(Date, default=date(2012, 1, 1), comment='生日') - - education = check_or_enum(name='education', enumList=['1', '2', '3'], comment='学历: 1->学士, 2->硕士, 3->博士') - - title = check_or_enum(name='title', enumList=['1', '2', '3', '4'], comment='职称: 1->助教, 2->讲师, 3->副教授, 4->教授') - - hashed_password = Column(String(60), server_default=get_password_hash('123456'), comment='密码') - - address = Column(String(20), server_default='广东省广州市', comment='上次登录地点') - - image = Column(String(60), server_default=f'{settings.BASE_URL}/{settings.STATIC_DIR}/author.jpg', comment='头像') - - departmentId = Column(Integer, ForeignKey('department.id', ondelete='CASCADE'), nullable=False, doc='院系编号') - - taught = relationship('Taught') # 不是字段, 可以通过 teacher ORM对象引用 taught 表的类集合 diff --git a/backend/models/user.py b/backend/models/user.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccb3bf3f1b66fb75a4a4ac94f84e3f44f51de92 --- /dev/null +++ b/backend/models/user.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/1 15:19 +# @desc : 用户表 +from enum import Enum + +from tortoise.contrib.pydantic import pydantic_model_creator +from tortoise.fields import IntField, CharField, CharEnumField, SmallIntField, ReverseRelation + +from models.base import Base + + +class SexEnum(str, Enum): + """ 性别枚举 """ + man = "1" + woman = "0" + + +class User(Base): + name = CharField(max_length=20, unique=True, description="用户名") + sex: SexEnum = CharEnumField(SexEnum, default=SexEnum.man, description="性别 1: 男 0: 女") + phone = CharField(max_length=11, description="手机号") + email = CharField(max_length=50, description="邮箱") + password = CharField(max_length=60, description="密码") + is_del = SmallIntField(default=0, description="是否删除 0: 正常 1: 已删") + + user_role = ReverseRelation["models.user_role"] + + def gender(self) -> str: # 返回类型一定要带 + return SexEnum(self.sex).name + + class PydanticMeta: + computed = ["gender"] + + class Meta: + table = "user" + table_description = "用户表" + + +UserOut = pydantic_model_creator(User, name="User", exclude=tuple(["sex", ])) +UserIn = pydantic_model_creator(User, name="UserIn", exclude_readonly=True) diff --git a/backend/models/user_role.py b/backend/models/user_role.py new file mode 100644 index 0000000000000000000000000000000000000000..084e6df32ec8b9fc875580e7fffb7df35f599ee3 --- /dev/null +++ b/backend/models/user_role.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/4 22:15 +# @desc : 用户角色表 +from tortoise import Model +from tortoise.fields import ForeignKeyRelation, ForeignKeyField + +from models.role import Role +from models.user import User + + +class UserRole(Model): + user_id: ForeignKeyRelation[User] = ForeignKeyField( + "models.User", + related_name="user_role", + description="用户id" + ) + + role_id: ForeignKeyRelation[Role] = ForeignKeyField( + "models.Role", + related_name="user_role", + description="角色id" + ) + + class Meta: + table = "user_role" + table_description = "用户角色表" diff --git a/backend/register/__init__.py b/backend/register/__init__.py deleted file mode 100644 index 42780d2b7a25ee9bc5dc51a0d163f9e815eb79e5..0000000000000000000000000000000000000000 --- a/backend/register/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/8 14:53 -# @Author : zxiaosi -# @desc : 注册中心 -from .cors import register_cors -from .exception import register_exception -from .router import register_router -from .middleware import register_middleware -from .mount import register_mount diff --git a/backend/register/cors.py b/backend/register/cors.py deleted file mode 100644 index a9a6da98e4288cb3d1768f86f5c55a91a993ff7f..0000000000000000000000000000000000000000 --- a/backend/register/cors.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/9 16:48 -# @Author : zxiaosi -# @desc : 跨域请求 -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware - -from core import settings - - -def register_cors(app: FastAPI): - """ 跨域请求 -- https://fastapi.tiangolo.com/zh/tutorial/cors/ """ - - app.add_middleware( - CORSMiddleware, - allow_origins=[str(origin) for origin in settings.CORS_ORIGINS], - allow_credentials=True, - allow_methods=("GET", "POST", "PUT", "DELETE"), - allow_headers=("*", "authentication"), - ) diff --git a/backend/register/exception.py b/backend/register/exception.py deleted file mode 100644 index 07ff876c6349ebd2b4f21345a0769dea41c0425f..0000000000000000000000000000000000000000 --- a/backend/register/exception.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/8 15:48 -# @Author : zxiaosi -# @desc : 全局异常 -import traceback -from fastapi import FastAPI -from fastapi.exceptions import RequestValidationError -from pydantic import ValidationError -from sqlalchemy.exc import IntegrityError, ProgrammingError -from sqlalchemy.orm.exc import UnmappedInstanceError -from starlette.requests import Request - -from core.logger import logger -from utils.custom_exc import IpError, ErrorUser, UserNotExist, SetRedis, AccessTokenFail, IdNotExist, \ - PermissionNotEnough -from utils.resp_code import resp_400, resp_401, resp_403, resp_422, resp_500 - - -def register_exception(app: FastAPI): - """ 全局异常捕获 -- https://www.charmcode.cn/article/2020-07-19_fastapi_exception """ - - @app.exception_handler(IpError) - async def ip_error_handler(request: Request, exc: IpError): - """ ip错误(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_400(msg=exc.err_desc) - - @app.exception_handler(ErrorUser) - async def error_user_handler(request: Request, exc: ErrorUser): - """ 错误的用户名或密码(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_400(msg=exc.err_desc) - - @app.exception_handler(UserNotExist) - async def user_not_exist_handler(request: Request, exc: UserNotExist): - """ 用户不存在(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_400(msg=exc.err_desc) - - @app.exception_handler(IdNotExist) - async def id_not_exist_handler(request: Request, exc: IdNotExist): - """ 查询id不存在(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_400(msg=exc.err_desc) - - @app.exception_handler(SetRedis) - async def set_redis_handler(request: Request, exc: SetRedis): - """ Redis存储失败(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_400(msg=exc.err_desc) - - @app.exception_handler(AccessTokenFail) - async def access_token_fail_handler(request: Request, exc: AccessTokenFail): - """ 访问令牌失败(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_401(msg=exc.err_desc) - - @app.exception_handler(PermissionNotEnough) - async def permission_not_enough_handler(request: Request, exc: AccessTokenFail): - """ 权限不足,拒绝访问(自定义异常) """ - logger.warning(f"{exc.err_desc}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_403(msg=exc.err_desc) - - @app.exception_handler(IntegrityError) - async def integrity_error_handler(request: Request, exc: IntegrityError): - """ 添加/更新的数据与数据库中数据冲突 """ - text = f"添加/更新的数据与数据库中数据冲突!" - logger.warning(f"{text}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}\nerror:{exc.orig}") - return resp_400(msg=text) - - @app.exception_handler(ProgrammingError) - async def programming_error_handle(request: Request, exc: ProgrammingError): - """ 请求参数丢失 """ - logger.error(f"请求参数丢失\nURL:{request.method}-{request.url}\nHeaders:{request.headers}\nerror:{exc}") - return resp_400(msg='请求参数丢失!(实际请求参数错误)') - - @app.exception_handler(RequestValidationError) - async def request_validation_exception_handler(request: Request, exc: RequestValidationError): - """ 请求参数验证异常 """ - logger.error(f"请求参数格式错误\nURL:{request.method}-{request.url}\nHeaders:{request.headers}\nerror:{exc.errors()}") - return resp_422(msg=exc.errors()) - - @app.exception_handler(ValidationError) - async def inner_validation_exception_handler(request: Request, exc: ValidationError): - """ 内部参数验证异常 """ - logger.error(f"内部参数验证错误\nURL:{request.method}-{request.url}\nHeaders:{request.headers}\nerror:{exc.errors()}") - return resp_500(msg=exc.errors()) - - @app.exception_handler(UnmappedInstanceError) - async def un_mapped_instance_error_handler(request: Request, exc: UnmappedInstanceError): - """ 删除数据的id在数据库中不存在 """ - id = request.path_params.get("id") - text = f"不存在编号为 {id} 的数据, 删除失败!" - logger.warning(f"{text}\nURL:{request.method}-{request.url}\nHeaders:{request.headers}") - return resp_400(msg=text) - - @app.exception_handler(Exception) - async def all_exception_handler(request: Request, exc: Exception): - """ 捕获全局异常 """ - logger.error(f"全局异常\n{request.method}URL:{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}") - return resp_500(msg="服务器内部错误") diff --git a/backend/register/middleware.py b/backend/register/middleware.py deleted file mode 100644 index 0591d6cf2ff22e91c01af5202da2354975ea0e6a..0000000000000000000000000000000000000000 --- a/backend/register/middleware.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/6 14:29 -# @Author : zxiaosi -# @desc : 中间件 -from fastapi import FastAPI, Request -from sqlalchemy.exc import OperationalError -from aioredis.exceptions import ConnectionError - -from core.logger import logger -from utils import resp_500 - - -# 权限验证 https://www.cnblogs.com/mazhiyong/p/13433214.html -# 得到真实ip https://stackoverflow.com/questions/60098005/fastapi-starlette-get-client-real-ip -# nginx 解决跨域请求 https://segmentfault.com/a/1190000019227927 - -def register_middleware(app: FastAPI): - """ 请求拦截与响应拦截 -- https://fastapi.tiangolo.com/tutorial/middleware/ """ - - @app.middleware("http") - async def intercept(request: Request, call_next): - logger.info(f"访问记录:IP:{request.client.host}-method:{request.method}-url:{request.url}") - try: - await request.app.state.redis.incr('request_num') # redis 请求数量 (自增 1) - return await call_next(request) # 返回请求(跳过token) - except ConnectionError as e: - logger.error(f'redis连接失败!-- {e}') - return resp_500(msg=f'redis连接失败!') - except OperationalError as e: - logger.error(f'数据库连接失败!-- {e}') - return resp_500(msg=f'数据库连接失败!') diff --git a/backend/register/mount.py b/backend/register/mount.py deleted file mode 100644 index 616dd8ab6b564a565bdb1b76060cc6526a7db14d..0000000000000000000000000000000000000000 --- a/backend/register/mount.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/3/4 16:04 -# @Author : zxiaosi -# @desc : 挂载静态文件 -from fastapi import FastAPI -from fastapi.staticfiles import StaticFiles - -from core import settings - - -def register_mount(app: FastAPI): - """ 挂载静态文件 -- https://fastapi.tiangolo.com/zh/tutorial/static-files/ """ - - # 第一个参数为url路径参数, 第二参数为静态文件目录的路径, 第三个参数是FastAPI内部使用的名字 - app.mount(f"/{settings.STATIC_DIR}", StaticFiles(directory=settings.STATIC_DIR), name=settings.STATIC_DIR) diff --git a/backend/register/router.py b/backend/register/router.py deleted file mode 100644 index 66709a748dc0ae1f52293360d9e024690b5e3d53..0000000000000000000000000000000000000000 --- a/backend/register/router.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/8 23:06 -# @Author : zxiaosi -# @desc : 注册路由 -from fastapi import FastAPI, Security - -from core import settings -from apis import app_router -from apis.deps import get_current_user -from apis.common import redis_check, login, dashboard - - -def register_router(app: FastAPI): - """ 注册路由 """ - - app.include_router(redis_check.router, prefix=settings.API_PREFIX, tags=["Redis"]) # Redis(不需要权限) - - app.include_router(login.router, prefix=settings.API_PREFIX, tags=["Login"]) # Login(权限在每个接口上) - - app.include_router(dashboard.router, prefix=settings.API_PREFIX, tags=["Dashboard"], - dependencies=[Security(get_current_user, scopes=[])]) # Dashboard(不需要权限,但需要登录) - - # 权限(权限在每个接口上) - app.include_router(app_router, prefix=settings.API_PREFIX) diff --git a/backend/requirements.txt b/backend/requirements.txt index 8b6beb2e17036284a4d8f30fe41194a3abd90ba8..10d231367ab2f3b2653e743e457e93828202a7b3 100644 Binary files a/backend/requirements.txt and b/backend/requirements.txt differ diff --git a/backend/schemas/__init__.py b/backend/schemas/__init__.py index da3f10431a32ff5bae75d6c1412e3b080895fd1f..44bf1b4b9dc8752f9828d4df6ebf1b3b7c38b392 100644 --- a/backend/schemas/__init__.py +++ b/backend/schemas/__init__.py @@ -1,18 +1,5 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 9:40 # @Author : zxiaosi -# @desc : 数据模型(类似于TS中的interface) -from .common import GMT -from .department import DepartmentCreate, DepartmentUpdate, DepartmentOut -from .major import MajorCreate, MajorUpdate, MajorOut -from .teacher import TeacherCreate, TeacherUpdate, TeacherOut -from .student import StudentCreate, StudentUpdate, StudentOut -from .course import CourseCreate, CourseUpdate, CourseOut -from .taught import TaughtCreate, TaughtUpdate, TaughtOut -from .elective import ElectiveCreate, ElectiveUpdate, ElectiveOut -from .admin import AdminCreate, AdminUpdate, AdminOut -from .token import Token, TokenData -from .result import SchemasType, Result, ResultPlus -from .todo import TodoId, Todo -from .login import Login +# @Time : 2022/7/1 14:43 +# @desc : diff --git a/backend/schemas/admin.py b/backend/schemas/admin.py deleted file mode 100644 index 05effc020c87d1a456db4d31f2d4d9a1aa6e1844..0000000000000000000000000000000000000000 --- a/backend/schemas/admin.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:01 -# @Author : zxiaosi -# @desc : 管理员表的模型 -from typing import Optional - -from pydantic import BaseModel, Field - -from schemas import GMT - - -class AdminIn(BaseModel): - """ 共享模型字段 """ - name: str = Field(..., max_length=10, example='姓名') - image: str = Field(..., example='头像') - address: str = Field(..., example='地址') - - -class AdminUpdate(AdminIn): - """ 更新数据的字段验证 """ - password: Optional[str] = Field(max_length=60, example='管理员密码') # 前端返回可不带该字段 - - -class AdminCreate(AdminUpdate): - """ 添加数据时的字段验证 """ - id: int = Field(..., example='自增编号') - - -class AdminOut(AdminIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='自增编号') - - class Config: - orm_mode = True # 是否使用orm模型(个人理解: 放行,不验证) diff --git a/backend/schemas/common.py b/backend/schemas/common.py deleted file mode 100644 index a35c86fb0fceb4a049b6dbee2fab3329b7ccdaf9..0000000000000000000000000000000000000000 --- a/backend/schemas/common.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/2/22 20:06 -# @Author : zxiaosi -# @desc : 常用模型 -from datetime import datetime - -from pydantic import BaseModel, validator - - -class GMT(BaseModel): - """ 时间字段处理 """ - create_time: datetime - update_time: datetime - - @validator("create_time", "update_time") - def format_time(cls, value: datetime) -> str: - return value.strftime('%Y-%m-%d %H:%M:%S') # 格式化时间 diff --git a/backend/schemas/course.py b/backend/schemas/course.py deleted file mode 100644 index 4609833904a80b53e8fc2cdf97b2daad57ff3268..0000000000000000000000000000000000000000 --- a/backend/schemas/course.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:01 -# @Author : zxiaosi -# @desc : 课程表模型 -from pydantic import BaseModel, Field -from schemas import GMT - - -class CourseIn(BaseModel): - """ 共享模型字段 """ - name: str = Field(min_length=1, max_length=20, example='课程名字') - credit: float = Field(..., example='学分') - period: int = Field(..., example='课时') - - -class CourseCreate(CourseIn): - """ 添加数据时的字段验证 """ - id: str = Field(regex=r'^[1-9][0-9]{3}$', min_length=4, max_length=4, example='课程编号:1101') - - -class CourseUpdate(CourseIn): - """ 更新数据的字段验证 """ - pass - - -class CourseOut(CourseCreate, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='编号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/department.py b/backend/schemas/department.py deleted file mode 100644 index 15bcff4cd7f3a52c6be41f3e978cd1995c9545f2..0000000000000000000000000000000000000000 --- a/backend/schemas/department.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 9:59 -# @Author : zxiaosi -# @desc : 院系表模型 -from typing import Optional -from pydantic import BaseModel, Field - -from schemas import GMT - - -class DepartmentIn(BaseModel): - """ 共享模型字段 """ - name: str = Field(min_length=1, max_length=20, example='名字') - chairman: str = Field(min_length=1, max_length=10, example='主任名') - phone: Optional[str] = Field(regex=r'(^\s{0}$)|^(0\d{2,3}-\d{7,8})|(1[34578]\d{9})$', example='主任手机号') - - -class DepartmentCreate(DepartmentIn): - """ 添加数据时的字段验证 """ - id: str = Field(regex=r'^[1-9][0-9]{3}$', example='编号') - - -class DepartmentUpdate(DepartmentIn): - """ 更新数据的字段验证 """ - pass - - -class DepartmentOut(DepartmentIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='编号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/elective.py b/backend/schemas/elective.py deleted file mode 100644 index f59914641820ef11c4ae2064b72e125759a7ebc9..0000000000000000000000000000000000000000 --- a/backend/schemas/elective.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:01 -# @Author : zxiaosi -# @desc : 选课表模型 -from typing import Optional - -from pydantic import BaseModel, Field - -from schemas import GMT - - -class ElectiveIn(BaseModel): - """ 共享模型字段 """ - grade: Optional[str] = Field(regex=r'(^\s{0}$)|(^100)|(^([0]{0})([1-9]?)([0-9]))$', max_length=3, example='成绩') - studentId: str = Field(regex=r'^[1-9][0-9]{9}$', min_length=10, max_length=10, example='学号:1810020401') - courseId: str = Field(regex=r'^[1-9][0-9]{3}$', min_length=4, max_length=4, example='课程编号:1101') - - -class ElectiveCreate(ElectiveIn): - """ 添加数据时的字段验证 """ - pass - - -class ElectiveUpdate(ElectiveIn): - """ 更新数据的字段验证 """ - pass - - -class ElectiveOut(ElectiveIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='自增编号') - studentId: int = Field(..., example='学号') - courseId: int = Field(..., example='课程号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/login.py b/backend/schemas/login.py deleted file mode 100644 index 42e192f4cc33bdc9d6eb6ea50eaf31ee3f58abe4..0000000000000000000000000000000000000000 --- a/backend/schemas/login.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/2/25 11:23 -# @Author : zxiaosi -# @desc : 登录模型 -from pydantic import BaseModel - - -class Login(BaseModel): - """ 登录模型 """ - username: str - password: str diff --git a/backend/schemas/major.py b/backend/schemas/major.py deleted file mode 100644 index 691377d4608f781676508302e84022e9ea48c3e1..0000000000000000000000000000000000000000 --- a/backend/schemas/major.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:00 -# @Author : zxiaosi -# @desc : 专业表模型 -from typing import Optional -from pydantic import BaseModel, Field - -from schemas import GMT - - -class MajorIn(BaseModel): - """ 共享模型字段 """ - name: str = Field(max_length=20, example='名字') - assistant: str = Field(min_length=1, max_length=10, example='辅导员名') - phone: Optional[str] = Field(regex=r'(^\s{0}$)|^(0\d{2,3}-\d{7,8})|(1[34578]\d{9})$', example='辅导员手机号') - departmentId: str = Field(regex=r'^10[0-9]{2}$', min_length=4, max_length=4, example='院系编号') - - -class MajorCreate(MajorIn): - """ 添加数据时的字段验证 """ - id: str = Field(regex=r'^10[0-9]{4}$', min_length=6, max_length=6, example='编号') - - -class MajorUpdate(MajorIn): - """ 更新数据的字段验证 """ - pass - - -class MajorOut(MajorIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='编号') - departmentId: int = Field(..., example='院系编号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/result.py b/backend/schemas/result.py index 1a6169a2cfb7d59053223d43c2f34ea5984cce33..35b4fbea95e8cdb3438041bedfd6e6d1964202cd 100644 --- a/backend/schemas/result.py +++ b/backend/schemas/result.py @@ -1,30 +1,10 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2022/1/7 11:24 # @Author : zxiaosi -# @desc : 返回数据验证 -from typing import TypeVar, Generic, Optional, Union, List, Dict, Any +# @Time : 2022/7/3 17:58 +# @desc : 返回消息模型 from pydantic import BaseModel -from pydantic.generics import GenericModel -SchemasType = TypeVar("SchemasType", bound=BaseModel) - -class Result(GenericModel, Generic[SchemasType]): - """ 普通结果验证 """ - code: int - data: Union[SchemasType, str, list, dict, bool] - msg: Optional[str] - - -class ListModel(GenericModel, Generic[SchemasType]): - """ 自定义结果 --> data 数据 """ - list: List[Union[SchemasType, Dict[str, Any]]] - count: int - - -class ResultPlus(GenericModel, Generic[SchemasType]): - """ 列表结果验证 """ - code: int - data: ListModel[SchemasType] - msg: Optional[str] +class Status(BaseModel): + message: str diff --git a/backend/schemas/student.py b/backend/schemas/student.py deleted file mode 100644 index a98990eb40cee673801f7758e9d0b2ade98b2e18..0000000000000000000000000000000000000000 --- a/backend/schemas/student.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:00 -# @Author : zxiaosi -# @desc : 学生表模型 -from datetime import date -from typing import Optional, Literal -from pydantic import BaseModel, Field - -from schemas import GMT - - -class StudentIn(BaseModel): - """ 共享模型字段 """ - name: str = Field(min_length=1, max_length=10, example='姓名') - sex: Literal['0', '1'] = Field(..., example='性别:0->男, 1->女') - birthday: date = Field(..., example='生日: 1998-7-2') - image: str = Field(..., example='头像') - address: str = Field(..., example='地址') - majorId: str = Field(regex=r'^10\d{4}$', min_length=6, max_length=6, example='专业编号') - - -class StudentUpdate(StudentIn): - """ 更新数据的字段验证 """ - password: Optional[str] = Field(max_length=60, example='密码') # 前端返回可不带该字段 - - -class StudentCreate(StudentUpdate): - """ 添加数据时的字段验证 """ - id: str = Field(regex=r'^[1-9][0-9]{9}$', min_length=10, max_length=10, example='学号:1810020401') - - -class StudentOut(StudentIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='学号:1810020401') - majorId: int = Field(..., example='专业编号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/taught.py b/backend/schemas/taught.py deleted file mode 100644 index 6e4160d76a886cbaea59de050721ceb0ad60486b..0000000000000000000000000000000000000000 --- a/backend/schemas/taught.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:01 -# @Author : zxiaosi -# @desc : 讲授表模型 -from pydantic import BaseModel, Field - -from schemas import GMT - - -class TaughtIn(BaseModel): - """ 共享模型字段 """ - teacherId: str = Field(regex=r'^[1-9][0-9]{5}$', min_length=6, max_length=6, example='职工号:180404') - courseId: str = Field(regex=r'^[1-9][0-9]{3}$', min_length=4, max_length=4, example='课程编号:1101') - - -class TaughtCreate(TaughtIn): - """ 添加数据时的字段验证 """ - pass - - -class TaughtUpdate(TaughtIn): - """ 更新数据的字段验证 """ - pass - - -class TaughtOut(TaughtIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='自增编号') - teacherId: int = Field(..., example='职工号') - courseId: int = Field(..., example='课程号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/teacher.py b/backend/schemas/teacher.py deleted file mode 100644 index 6d0f6c122a746a6f61364bcdb982f1783d978bb9..0000000000000000000000000000000000000000 --- a/backend/schemas/teacher.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/9/22 10:01 -# @Author : zxiaosi -# @desc : 教师表模型 -from datetime import date -from typing import Optional, Literal -from pydantic import BaseModel, Field - -from schemas import GMT - - -class TeacherIn(BaseModel): - """ 共享模型字段 """ - name: str = Field(min_length=1, max_length=10, example='姓名') - sex: Literal['0', '1'] = Field(..., example='性别: 0->男, 1->女') - birthday: date = Field(..., example='生日: 1998-7-2') - education: Literal['1', '2', '3'] = Field(..., example='学历:1->学士, 2->硕士, 3->博士') - title: Literal['1', '2', '3', '4'] = Field(..., example='职称:1->助教, 2->讲师, 3->副教授, 4->教授') - image: str = Field(example='头像') - address: str = Field(example='地址') - departmentId: str = Field(regex=r'^10[0-9]{2}$', min_length=4, max_length=4, example='院系编号') - - -class TeacherUpdate(TeacherIn): - """ 更新数据的字段验证 """ - password: Optional[str] = Field(max_length=60, example='密码') # 前端返回可不带该字段 - - -class TeacherCreate(TeacherUpdate): - """ 添加数据时的字段验证 """ - id: str = Field(regex=r'^[1-9][0-9]{5}$', min_length=6, max_length=6, example='职工号:180404') - - -class TeacherOut(TeacherIn, GMT): - """ 查询数据的字段验证 """ - id: int = Field(..., example='职工号:180404') - departmentId: int = Field(..., example='院系编号') - - class Config: - orm_mode = True # 是否使用orm模型(结果为字典类型) diff --git a/backend/schemas/todo.py b/backend/schemas/todo.py deleted file mode 100644 index f6c8768586146fad416064ffe11aa404dbf41244..0000000000000000000000000000000000000000 --- a/backend/schemas/todo.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/2/24 11:20 -# @Author : zxiaosi -# @desc : 待办模型 -from typing import Optional - -from pydantic import BaseModel - - -class TodoId(BaseModel): - id: Optional[int] - - -class Todo(BaseModel): - title: Optional[str] - status: Optional[bool] = False diff --git a/backend/schemas/token.py b/backend/schemas/token.py deleted file mode 100644 index 060952ee296d48fb88660c228d040989d74ce426..0000000000000000000000000000000000000000 --- a/backend/schemas/token.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/12/24 14:48 -# @Author : zxiaosi -# @desc : token -from typing import Optional, List - -from pydantic import BaseModel - - -class Token(BaseModel): - """ token """ - access_token: str - token_type: str - - -class TokenData(BaseModel): - sub: Optional[str] = None - scopes: List[str] = [] diff --git a/backend/sql_app.db b/backend/sql_app.db deleted file mode 100644 index bb03a95a97f3e080f4dab8572f413ba76c7fc2eb..0000000000000000000000000000000000000000 Binary files a/backend/sql_app.db and /dev/null differ diff --git a/backend/static/author.jpg b/backend/static/author.jpg deleted file mode 100644 index b5901c1f7ad0d5e58f2a1953ae7a462dde7ff4df..0000000000000000000000000000000000000000 Binary files a/backend/static/author.jpg and /dev/null differ diff --git a/backend/test_main.http b/backend/test_main.http new file mode 100644 index 0000000000000000000000000000000000000000..a2d81a92c9122ae3e6b5b657c5723033b2f26895 --- /dev/null +++ b/backend/test_main.http @@ -0,0 +1,11 @@ +# Test your FastAPI endpoints + +GET http://127.0.0.1:8000/ +Accept: application/json + +### + +GET http://127.0.0.1:8000/hello/User +Accept: application/json + +### diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py index ebc1f252166db309906178c81f63603fb8d95c56..9803b2080c668ce67eb4b101c6fc2a728ddf4d88 100644 --- a/backend/utils/__init__.py +++ b/backend/utils/__init__.py @@ -1,11 +1,5 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2021/11/11 15:42 # @Author : zxiaosi -# @desc : 工具类 -from .create_dir import create_dir -from .check_enum import check_or_enum -from .ip_address import by_ip_get_address -from .obj_dict import obj_as_dict, list_obj_as_dict -from .resp_code import resp_200, resp_400, resp_401, resp_403, resp_404, resp_422, resp_500 -from .custom_exc import IdNotExist, SetRedis, UserNotExist, AccessTokenFail, ErrorUser, IpError, PermissionNotEnough +# @Time : 2022/7/1 14:41 +# @desc : diff --git a/backend/utils/check_enum.py b/backend/utils/check_enum.py index e81e858acd9d5638b801f92e337026b85f8ab6a7..27728cf375289c45be8f6c1c52a0dc3aa855ed33 100644 --- a/backend/utils/check_enum.py +++ b/backend/utils/check_enum.py @@ -1,23 +1,47 @@ #!/usr/bin/env python3 # _*_ coding: utf-8 _*_ -# @Time : 2022/4/28 18:54 # @Author : zxiaosi -# @desc : 创建枚举属性 -from sqlalchemy import Enum, CheckConstraint, String, Column +# @Time : 2022/7/2 21:04 +# @desc : 仿照tortoise-orm写的枚举 +""" 本人太菜, 还待完善 """ +from enum import Enum +from typing import Any, List, Union -from core import settings +from tortoise.fields import Field -# sex = Column(String(1), CheckConstraint("sex in ('1', '2', '3')")) # sqlite -# sex = Column(Enum('0', '1')) # mysql -# sex = Column(Enum('0', '1', name='sex_enum')) # postgresql +class MyEnumField(Field[str], str): + def __init__(self, enum_list: List, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.enum_list = enum_list + print(f"字段名 {self.source_field}") -def check_or_enum(name: str, enumList: [], comment: str = ''): - """ - - sqlite不支持枚举, 用check约束 - - mysql和postgresql用check没效果, 用枚举限制 - """ - if settings.DATABASE_URI.startswith('sqlite'): - return Column(String(1), CheckConstraint(f"{name} in {tuple(enumList)}"), nullable=False, comment=comment) - else: - return Column(Enum(*enumList, name=name), nullable=False, comment=comment) + @property + def constraints(self) -> dict: + return {"enum_list": self.enum_list} + + @property # mysql, postgresql 的 enum 类型 + def SQL_TYPE(self) -> str: # type: ignore + return f"ENUM{tuple(self.enum_list)}" + + # class _db_oracle + # class _db_mssql + # class _db_mysql + class _db_sqlite: # 解决sqlite的check类型, 没生效。。。 + def __init__(self, field: "MyEnumField") -> None: + self.field = field + + @property + def SQL_TYPE(self) -> str: + print(self.field.enum_list) + return f"char(1) CHECK ({self.field.source_field} IN {tuple(self.field.enum_list)})" + + def to_python_value(self, value: Union[str, None]) -> Union[Enum, None]: + self.validate(value) + return value if value is not None else None + + def to_db_value(self, value: Union[Enum, None, str], instance: "Union[Type[Model], Model]") -> Union[str, None]: + self.validate(value) + if isinstance(value, str): + return str(value) + return value diff --git a/backend/utils/create_dir.py b/backend/utils/create_dir.py deleted file mode 100644 index 5405bb131aad6d8da52b0a3134331510d39520fd..0000000000000000000000000000000000000000 --- a/backend/utils/create_dir.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/3/4 15:48 -# @Author : zxiaosi -# @desc : 创建文件夹 -import os - - -# 请不要随意移动该文件,创建文件夹是根据当前文件位置来创建 -def create_dir(file_name: str) -> str: - """ 创建文件夹 """ - current_path = os.path.dirname(__file__) # 获取当前文件夹 - - base_path = os.path.abspath(os.path.join(current_path, "..")) # 获取当前文件夹的上一层文件 - - path = base_path + os.sep + file_name + os.sep # 拼接日志文件夹的路径 - - os.makedirs(path, exist_ok=True) # 如果文件夹不存在就创建 - - return path diff --git a/backend/utils/custom_exc.py b/backend/utils/custom_exc.py deleted file mode 100644 index c40553ed2e590397e83d6f17a081dc00657be12e..0000000000000000000000000000000000000000 --- a/backend/utils/custom_exc.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/1/8 13:48 -# @Author : zxiaosi -# @desc : 自定义异常 -class IpError(Exception): - """ ip错误 """ - - def __init__(self, err_desc: str = "ip错误"): - self.err_desc = err_desc - - -class SetRedis(Exception): - """ Redis存储失败 """ - - def __init__(self, err_desc: str = "Redis存储失败"): - self.err_desc = err_desc - - -class IdNotExist(Exception): - """ 查询id不存在 """ - - def __init__(self, err_desc: str = "查询id不存在"): - self.err_desc = err_desc - - -class UserNotExist(Exception): - """ 用户不存在 """ - - def __init__(self, err_desc: str = "用户不存在"): - self.err_desc = err_desc - - -class AccessTokenFail(Exception): - """ 访问令牌失败 """ - - def __init__(self, err_desc: str = "访问令牌失败"): - self.err_desc = err_desc - - -class ErrorUser(Exception): - """ 错误的用户名或密码 """ - - def __init__(self, err_desc: str = "错误的用户名或密码"): - self.err_desc = err_desc - - -class PermissionNotEnough(Exception): - """ 权限不足,拒绝访问 """ - - def __init__(self, err_desc: str = "权限不足,拒绝访问"): - self.err_desc = err_desc diff --git a/backend/utils/date_util.py b/backend/utils/date_util.py new file mode 100644 index 0000000000000000000000000000000000000000..d527818db45546122e976b0e820f088262155a50 --- /dev/null +++ b/backend/utils/date_util.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/3 15:00 +# @desc : 时间工具类 +from datetime import datetime + + +def handle_datetime(time: datetime): + """ + 处理时间格式 + :param time: 带时区的时间 + :return: datetime类型 + """ + # 方法一 + # tz_time = datetime.strptime(str(time), "%Y-%m-%d %H:%M:%S.%f+08:00") + # tz_time_str = tz_time.strftime("%Y-%m-%d %H:%M:%S") + + # 方法二 + # isoformat用法: https://vimsky.com/examples/usage/python-datetime-isoformat-method-with-example-02.html + tz_time = time.replace(tzinfo=None) + tz_time_str = tz_time.isoformat(" ", timespec='seconds') + + return tz_time_str diff --git a/backend/utils/file_util.py b/backend/utils/file_util.py new file mode 100644 index 0000000000000000000000000000000000000000..14f8087111f8b63182f16934ba19ba98c8f75337 --- /dev/null +++ b/backend/utils/file_util.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# _*_ coding: utf-8 _*_ +# @Author : zxiaosi +# @Time : 2022/7/3 16:28 +# @desc : 文件工具类 +import os +from typing import List + +from loguru import logger + + +def get_models_file(dir_name: str, exclude: List = []) -> List: + """ + 获取models目录下的 .py 文件 + :return: 文件列表 eg: [models.user.py, models.user_in.py] + """ + current_path = os.path.dirname(__file__) # 获取当前文件夹 + models_path = os.path.abspath(os.path.join(current_path, "..", dir_name)) # 获取当前文件夹的上一层文件 + + try: + file_list = [] + for file in os.listdir(models_path): + # 下面可简写 + if file == '__pycache__' or file == '__init__.py': + continue + if file in exclude: + continue + if file.endswith('.py'): + file_list.append(f"{dir_name}.{file[:-3]}") + return file_list + except Exception as e: + logger.error(f"获取{dir_name}目录失败 -- {e}") + return [] diff --git a/backend/utils/ip_address.py b/backend/utils/ip_address.py deleted file mode 100644 index 4d944f8af7f04b5fef5b7b349ef8a3ff424c0aac..0000000000000000000000000000000000000000 --- a/backend/utils/ip_address.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/3/4 15:21 -# @Author : zxiaosi -# @desc : 根据ip获取地址 -import re -from urllib import request -from ipaddress import ip_address - -from utils.custom_exc import IpError - - -def verify_ip(ip): - """ 验证ip格式是否正确 """ - try: - return str(ip_address(ip)) - except Exception as e: - raise IpError(f'错误的IP格式! -- {e}') - - -def by_ip_get_address(ip) -> str: - """ 根据ip获取地址 """ - verify_ip(ip) - - req = request.Request(f"http://ip.ws.126.net/ipquery?ip={ip}") - response = request.urlopen(req).read().decode('gbk') # 获取响应 - - handle_address = re.findall(r'"([^"]*)"', response) - if handle_address: - return handle_address[0] + handle_address[1] - else: - return '广东省广州市' diff --git a/backend/utils/obj_dict.py b/backend/utils/obj_dict.py deleted file mode 100644 index 1a1b7b4f54b97867e4b51867ed2a173dfc11fe31..0000000000000000000000000000000000000000 --- a/backend/utils/obj_dict.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/4/11 11:05 -# @Author : zxiaosi -# @desc : 对象转字典 -from sqlalchemy import inspect - - -def obj_as_dict(obj): - """ ORM对象转字典 """ - return {c.key: getattr(obj, c.key) for c in inspect(obj).mapper.column_attrs} if obj else None - - -def list_obj_as_dict(list_obj): - """ ORM列表对象转字典 """ - return [obj_as_dict(obj) for obj in list_obj] diff --git a/backend/utils/permission_assign.py b/backend/utils/permission_assign.py deleted file mode 100644 index 3215384f3cb4d3b023c48d5370124152084fe0a8..0000000000000000000000000000000000000000 --- a/backend/utils/permission_assign.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2022/3/9 20:44 -# @Author : zxiaosi -# @desc : 权限分配 -from typing import List, Union - -import crud -from core import settings -from crud.admin import CRUDAdmin -from crud.teacher import CRUDTeacher -from crud.student import CRUDStudent -from utils import PermissionNotEnough - - -def by_scopes_get_crud(scopes: List[Union[str]]) -> Union[CRUDAdmin, CRUDTeacher, CRUDStudent]: - """ 根据scopes(权限)分配不同的权限 """ - if 'admin' in scopes: - return crud.admin - elif 'teacher' in scopes: - return crud.teacher - elif 'student' in scopes: - return crud.student - else: - raise PermissionNotEnough - - -def handle_oauth2_scopes(): - """ 配置 OAuth2PasswordBearer 的 scopes """ - join_dict = {} - for item in settings.PERMISSION_DATA: - join_dict.update(item) - return join_dict - - -def generate_permission_data(): - """ 生成 permission 表数据 """ - data = [] - for item in settings.PERMISSION_DATA: - (key, value), = item.items() # , 一定要存在 - data.append({'name': key, 'desc': value}) - return data diff --git a/backend/utils/resp_code.py b/backend/utils/resp_code.py deleted file mode 100644 index 4291419728956b4d1eb851923ac2030775ddcb82..0000000000000000000000000000000000000000 --- a/backend/utils/resp_code.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# _*_ coding: utf-8 _*_ -# @Time : 2021/11/13 13:41 -# @Author : zxiaosi -# @desc : 响应状态码 -from typing import Union, Any, Optional -from starlette import status -from starlette.responses import Response -from fastapi.responses import ORJSONResponse - -from core.logger import logger - - -def resp_200(*, data: Any = '', msg: str = "Success") -> dict: - logger.info(msg) - return {'code': 200, 'data': data, 'msg': msg} - - -def resp_400(code: int = 400, data: str = None, msg: str = "请求错误(400)") -> Response: - return ORJSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content={'code': code, 'msg': msg, 'data': data}) - - -def resp_401(*, data: str = None, msg: str = "未授权,请重新登录(401)") -> Response: - return ORJSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content={'code': 401, 'msg': msg, 'data': data}) - - -def resp_403(*, data: str = None, msg: str = "拒绝访问(403)") -> Response: - return ORJSONResponse(status_code=status.HTTP_403_FORBIDDEN, content={'code': 403, 'msg': msg, 'data': data}) - - -def resp_404(*, data: str = None, msg: str = "请求出错(404)") -> Response: - return ORJSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={'code': 404, 'msg': msg, 'data': data}) - - -def resp_422(*, data: str = None, msg: Union[list, dict, str] = "不可处理的实体") -> Response: - return ORJSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - content={'code': 422, 'msg': msg, 'data': data}) - - -def resp_500(*, data: str = None, msg: Union[list, dict, str] = "服务器错误(500)") -> Response: - return ORJSONResponse(headers={'Access-Control-Allow-Origin': '*'}, - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - content={'code': 500, 'msg': msg, 'data': data}) - - -def resp_502(*, data: str = None, msg: str = "网络错误(502)") -> Response: - return ORJSONResponse(status_code=status.HTTP_502_BAD_GATEWAY, content={'code': 502, 'msg': msg, 'data': data}) - - -# ------------------------------------------- 以下不常用 ------------------------------------------- - -def resp_406(*, data: str = None, msg: str = "请求的格式不可得(406)") -> Response: - return ORJSONResponse(status_code=status.HTTP_406_NOT_ACCEPTABLE, content={'code': 406, 'msg': msg, 'data': data}) - - -def resp_408(*, data: str = None, msg: str = "请求超时(408)") -> Response: - return ORJSONResponse(status_code=status.HTTP_408_REQUEST_TIMEOUT, content={'code': 408, 'msg': msg, 'data': data}) - - -def resp_410(*, data: str = None, msg: str = "请求的资源被永久删除,且不会再得到的(410)") -> Response: - return ORJSONResponse(status_code=status.HTTP_410_GONE, content={'code': 410, 'msg': msg, 'data': data}) - - -def resp_501(*, data: str = None, msg: str = "服务未实现(501)") -> Response: - return ORJSONResponse(status_code=status.HTTP_501_NOT_IMPLEMENTED, content={'code': 501, 'msg': msg, 'data': data}) - - -def resp_503(*, data: str = None, msg: str = "服务不可用(503)") -> Response: - return ORJSONResponse(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - content={'code': 503, 'msg': msg, 'data': data}) - - -def resp_504(*, data: str = None, msg: str = "网络超时(504)") -> Response: - return ORJSONResponse(status_code=status.HTTP_504_GATEWAY_TIMEOUT, content={'code': 504, 'msg': msg, 'data': data}) - - -def resp_505(*, data: str = None, msg: str = "HTTP版本不受支持(505)") -> Response: - return ORJSONResponse(status_code=status.HTTP_505_HTTP_VERSION_NOT_SUPPORTED, - content={'code': 505, 'msg': msg, 'data': data}) diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index 38adffa64e8300a31b749218081149e1fe3deaaa..0000000000000000000000000000000000000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -.DS_Store -dist -dist-ssr -coverage -*.local - -/cypress/videos/ -/cypress/screenshots/ - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json deleted file mode 100644 index 806eacda6197736736541ff3f0da725f96c08528..0000000000000000000000000000000000000000 --- a/frontend/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"] -} diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index f311dc8afc8959a883b5d61e4597f817283a9650..0000000000000000000000000000000000000000 --- a/frontend/README.md +++ /dev/null @@ -1,133 +0,0 @@ -## Vue3+ElementPlus+Vite - -## 版本 - -+ `V1.0` 初始化项目 -+ `V1.1` 调试了接口(测试页面)&&引入阿里的图标 -+ `V1.2` 测试数据的增删改查已完成 -+ `V1.3` 添加了院系表的增删改查,以及其相关字段的验证规则,添加了额外功能排序 -+ `V1.4` 首页数据更改,待办事项可以添加(临时数据) -+ `V1.5` 添加了专业表的增删改查 -+ `V1.6` 调试了专业表中下拉框 -+ `V1.6` 添加了教师表 -+ `V1.7` 添加了学生表、课程表、选课表 -+ `V1.8` 重构代码 -+ `V1.9` 封装了部分组件 -+ `V2.0` 重构表格代码 -+ `V2.1` 封装了部分方法,重构表格组件 -+ `V2.1` 修改了校验规则 -+ `v2.2` 修复打包出现的小问题 -+ `v2.3` 部署成功 -+ `v2.4` 添加加载效果 -+ `v2.5` 通过vuex优化取数据的方式 -+ `v2.6` 优化请求方式&&部分数据添加到redis中 -+ `v2.7` 首页数据添加到redis中 -+ `v2.8` 加入TS -+ `v2.9` 实现图片上传 -+ `v3.0` 分离文件(vue与ts分离) -+ `v3.1` 简单实现权限管理 -+ `v3.2` 整理代码 -+ `v3.3` 语言详情更换为Github公共API -+ `v3.4` 简单实现学生选课 -+ `v3.5` 简单实现教师讲授课程 - -## 安装 - -1. 进到 `frontend` 目录下 - -2. `npm install` 安装所需的包 - -3. 启动服务 - - + `npm run dev` 运行项目 - + 报错见下面 - - >服务接口:http://localhost:3000/ - > - >用户名:admin - > - >密码:123 - -## 项目截图 - -+ 登录界面(待定) - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/frontend-login.png) - -+ 后台界面(待定) - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/frontend-%E5%90%8E%E5%8F%B0.png) - -## 项目目录 - -```sh -|-- frontend - |-- dist # 输出文件 - |-- src # 核心文件 - |-- api # 接口 - |-- assets # 静态文件 - |-- commponents # 组件 - |-- request # axios请求 - |-- router # 路由 - |-- stores # 状态管理 - |-- types # 公用接口 - |-- utils # 工具类 - |-- views # 页面 - |-- App.vue # app - |-- main.ts # 主文件 - |-- index.html # 首页 - |-- env.d.ts # - |-- package-lock.json # 包版本锁 - |-- package.json # 安装的包 - |-- README.md # 文档说明 - |-- tsconfig.json # ts配置 - |-- tsconfig.vite-config.json # 兼用node与vite - |-- vite.config.js # vite配置 - -``` - - - -## 常见错误 - -### 1. `npm run dev` 报错 - -+ 内容如下 - - ```sh - > vue-manage-system@5.1.0 dev - > vite - - events.js:377 - throw er; // Unhandled 'error' event - ^ - - Error: spawn D:\Vscode\Vue-Manage-System\node_modules\esbuild\esbuild.exe ENOENT - at Process.ChildProcess._handle.onexit (internal/child_process.js:274:19) - at onErrorNT (internal/child_process.js:469:16) - at processTicksAndRejections (internal/process/task_queues.js:82:21) - Emitted 'error' event on ChildProcess instance at: - at Process.ChildProcess._handle.onexit (internal/child_process.js:280:12) - at onErrorNT (internal/child_process.js:469:16) - at processTicksAndRejections (internal/process/task_queues.js:82:21) { - errno: -4058, - code: 'ENOENT', - syscall: 'spawn D:\\Vscode\\Vue-Manage-System\\node_modules\\esbuild\\esbuild.exe', - path: 'D:\\Vscode\\Vue-Manage-System\\node_modules\\esbuild\\esbuild.exe', - spawnargs: [ '--service=0.12.9', '--ping' ] - } - ``` - -+ 错误原因:`vite` 安装失败 - -+ 解决措施:输入下面命令 `node .\node_modules\esbuild\install.js`,重新运行 `npm run dev` - -### 2. `npm run dev` 报错 - -+ 内容如下 - - ![](https://gitee.com/zxiaosi/image/raw/master/Project/Vue+FastAPI/%E6%8A%A5%E9%94%992.png) - -+ 错误原因:`端口号被占用` -+ 解决措施:关闭其他占用 `3000`端口号的应用,重新运行 `npm run dev` - diff --git a/frontend/env.d.ts b/frontend/env.d.ts deleted file mode 100644 index 11f02fe2a0061d6e6e1f271b21da95423b448b32..0000000000000000000000000000000000000000 --- a/frontend/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/frontend/index.html b/frontend/index.html deleted file mode 100644 index b86e8f18291b0749042afe30bfbbb6e6e1425d0c..0000000000000000000000000000000000000000 --- a/frontend/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - 学生选课系统 - - - - -
- - - diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index ba8f6e2060949d51d8be278a589be10244d38093..0000000000000000000000000000000000000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,3256 +0,0 @@ -{ - "name": "frontend", - "version": "3.1.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "frontend", - "version": "3.1.0", - "dependencies": { - "axios": "^0.27.2", - "echarts": "^5.3.2", - "element-plus": "^2.1.11", - "pinia": "^2.0.13", - "vue": "^3.2.33", - "vue-router": "^4.0.14" - }, - "devDependencies": { - "@types/node": "^16.11.27", - "@vitejs/plugin-vue": "^2.3.1", - "@vitejs/plugin-vue-jsx": "^1.3.10", - "@vue/tsconfig": "^0.1.3", - "typescript": "~4.6.3", - "vite": "^2.9.5", - "vue-tsc": "^0.34.7" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.17.10.tgz", - "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.17.10.tgz", - "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-compilation-targets": "^7.17.10", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.10", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.10", - "@babel/types": "^7.17.10", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", - "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.10", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", - "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-member-expression-to-functions": "^7.17.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.17.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", - "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", - "dev": true, - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", - "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz", - "integrity": "sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.16.8", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz", - "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-typescript": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.17.9.tgz", - "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", - "deprecated": "[WARNING] Use 7.17.9 instead of 7.17.10, reason: https://github.com/babel/babel/issues/14525", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.9", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.9", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ctrl/tinycolor": { - "version": "3.4.1", - "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", - "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@element-plus/icons-vue": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-1.1.4.tgz", - "integrity": "sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==", - "peerDependencies": { - "vue": "^3.2.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "0.6.2", - "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-0.6.2.tgz", - "integrity": "sha512-jktYRmZwmau63adUG3GKOAVCofBXkk55S/zQ94XOorAHhwqFIOFAy1rSp2N0Wp6/tGbe9V3u/ExlGZypyY17rg==" - }, - "node_modules/@floating-ui/dom": { - "version": "0.4.5", - "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-0.4.5.tgz", - "integrity": "sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw==", - "dependencies": { - "@floating-ui/core": "^0.6.2" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.11", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.11.tgz", - "integrity": "sha512-RllI476aSMsxzeI9TtlSMoNTgHDxEmnl6GkkHwhr0vdL8W+0WuesyI8Vd3rBOfrwtPXbPxdT9ADJdiOKgzxPQA==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@popperjs/core": { - "name": "@sxzz/popperjs-es", - "version": "2.11.7", - "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", - "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==" - }, - "node_modules/@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", - "dev": true, - "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.6", - "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.6.tgz", - "integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==", - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/node": { - "version": "16.11.33", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.33.tgz", - "integrity": "sha512-0PJ0vg+JyU0MIan58IOIFRtSvsb7Ri+7Wltx2qAg94eMOrpg4+uuP3aUHCpxXc1i0jCXiC+zIamSZh3l9AbcQA==", - "dev": true - }, - "node_modules/@vitejs/plugin-vue": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.2.tgz", - "integrity": "sha512-umyypfSHS4kQLdYAnJHhaASq7FRzNCdvcRoQ3uYGNk1/M4a+hXUd7ysN7BLhCrWH6uBokyCkFeUAaFDzSaaSrQ==", - "dev": true, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "vite": "^2.5.10", - "vue": "^3.2.25" - } - }, - "node_modules/@vitejs/plugin-vue-jsx": { - "version": "1.3.10", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-1.3.10.tgz", - "integrity": "sha512-Cf5zznh4yNMiEMBfTOztaDVDmK1XXfgxClzOSUVUc8WAmHzogrCUeM8B05ABzuGtg0D1amfng+mUmSIOFGP3Pw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.17.9", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-transform-typescript": "^7.16.8", - "@rollup/pluginutils": "^4.2.0", - "@vue/babel-plugin-jsx": "^1.1.1", - "hash-sum": "^2.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@volar/code-gen": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/code-gen/-/code-gen-0.34.12.tgz", - "integrity": "sha512-5GAPsSjScnfMmMoh9qLW7CWQjjnT0fTUsPWnDMMjKIOqQF9J5mOyo7rprt1VzX63zwayqFfx7V8W3EVNhUCE3w==", - "dev": true, - "dependencies": { - "@volar/source-map": "0.34.12" - } - }, - "node_modules/@volar/source-map": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-0.34.12.tgz", - "integrity": "sha512-07imKws1cz9g3eo0VWXdioNfc1eCjqwK7GsxVuYSc7OCzKASt9PywUW+F39QGB9g2Kewof+PjCVIPeGqGRECTA==", - "dev": true - }, - "node_modules/@volar/vue-code-gen": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/vue-code-gen/-/vue-code-gen-0.34.12.tgz", - "integrity": "sha512-PFcft62eIvQvcB6H2Z88fouTu2JmYwimORziFGr3LlGriQUEVmyDtqddtb+E+j2wGChtLkh6hf1py94C5VpI/Q==", - "dev": true, - "dependencies": { - "@volar/code-gen": "0.34.12", - "@volar/source-map": "0.34.12", - "@vue/compiler-core": "^3.2.31", - "@vue/compiler-dom": "^3.2.31", - "@vue/shared": "^3.2.31" - } - }, - "node_modules/@volar/vue-typescript": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-0.34.12.tgz", - "integrity": "sha512-mY5cZ2OFOKt1HcCuoX1ViEsccltX3mdACk/FAjrSZTrilTdVHI1zkmQlrpCSnjmE1qowd8I6YoVt7THCaVrHdg==", - "dev": true, - "dependencies": { - "@volar/code-gen": "0.34.12", - "@volar/source-map": "0.34.12", - "@volar/vue-code-gen": "0.34.12", - "@vue/compiler-sfc": "^3.2.31", - "@vue/reactivity": "^3.2.31" - } - }, - "node_modules/@vue/babel-helper-vue-transform-on": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", - "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==", - "dev": true - }, - "node_modules/@vue/babel-plugin-jsx": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz", - "integrity": "sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "@vue/babel-helper-vue-transform-on": "^1.0.2", - "camelcase": "^6.0.0", - "html-tags": "^3.1.0", - "svg-tags": "^1.0.0" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.33.tgz", - "integrity": "sha512-AAmr52ji3Zhk7IKIuigX2osWWsb2nQE5xsdFYjdnmtQ4gymmqXbjLvkSE174+fF3A3kstYrTgGkqgOEbsdLDpw==", - "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.33", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.33.tgz", - "integrity": "sha512-GhiG1C8X98Xz9QUX/RlA6/kgPBWJkjq0Rq6//5XTAGSYrTMBgcLpP9+CnlUg1TFxnnCVughAG+KZl28XJqw8uQ==", - "dependencies": { - "@vue/compiler-core": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.33.tgz", - "integrity": "sha512-H8D0WqagCr295pQjUYyO8P3IejM3vEzeCO1apzByAEaAR/WimhMYczHfZVvlCE/9yBaEu/eu9RdiWr0kF8b71Q==", - "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.33", - "@vue/compiler-dom": "3.2.33", - "@vue/compiler-ssr": "3.2.33", - "@vue/reactivity-transform": "3.2.33", - "@vue/shared": "3.2.33", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.33.tgz", - "integrity": "sha512-XQh1Xdk3VquDpXsnoCd7JnMoWec9CfAzQDQsaMcSU79OrrO2PNR0ErlIjm/mGq3GmBfkQjzZACV+7GhfRB8xMQ==", - "dependencies": { - "@vue/compiler-dom": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "node_modules/@vue/devtools-api": { - "version": "6.1.4", - "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz", - "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==" - }, - "node_modules/@vue/reactivity": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.33.tgz", - "integrity": "sha512-62Sq0mp9/0bLmDuxuLD5CIaMG2susFAGARLuZ/5jkU1FCf9EDbwUuF+BO8Ub3Rbodx0ziIecM/NsmyjardBxfQ==", - "dependencies": { - "@vue/shared": "3.2.33" - } - }, - "node_modules/@vue/reactivity-transform": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.33.tgz", - "integrity": "sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==", - "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.33", - "@vue/shared": "3.2.33", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.33.tgz", - "integrity": "sha512-N2D2vfaXsBPhzCV3JsXQa2NECjxP3eXgZlFqKh4tgakp3iX6LCGv76DLlc+IfFZq+TW10Y8QUfeihXOupJ1dGw==", - "dependencies": { - "@vue/reactivity": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz", - "integrity": "sha512-LSrJ6W7CZTSUygX5s8aFkraDWlO6K4geOwA3quFF2O+hC3QuAMZt/0Xb7JKE3C4JD4pFwCSO7oCrZmZ0BIJUnw==", - "dependencies": { - "@vue/runtime-core": "3.2.33", - "@vue/shared": "3.2.33", - "csstype": "^2.6.8" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.33.tgz", - "integrity": "sha512-4jpJHRD4ORv8PlbYi+/MfP8ec1okz6rybe36MdpkDrGIdEItHEUyaHSKvz+ptNEyQpALmmVfRteHkU9F8vxOew==", - "dependencies": { - "@vue/compiler-ssr": "3.2.33", - "@vue/shared": "3.2.33" - }, - "peerDependencies": { - "vue": "3.2.33" - } - }, - "node_modules/@vue/shared": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.33.tgz", - "integrity": "sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==" - }, - "node_modules/@vue/tsconfig": { - "version": "0.1.3", - "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.1.3.tgz", - "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==", - "dev": true, - "peerDependencies": { - "@types/node": "*" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@vueuse/core": { - "version": "8.4.2", - "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-8.4.2.tgz", - "integrity": "sha512-dUVU96lii1ZdWoNJXauQNt+4QrHz1DKbuW+y6pDR2N10q7rGZJMDU7pQeMcC2XeosX7kMODfaBuqsF03NozzLg==", - "dependencies": { - "@vueuse/metadata": "8.4.2", - "@vueuse/shared": "8.4.2", - "vue-demi": "*" - }, - "peerDependencies": { - "@vue/composition-api": "^1.1.0", - "vue": "^2.6.0 || ^3.2.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, - "vue": { - "optional": true - } - } - }, - "node_modules/@vueuse/core/node_modules/@vueuse/shared": { - "version": "8.4.2", - "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-8.4.2.tgz", - "integrity": "sha512-hILXMEjL8YQhj1LHN/HZ49UThyfk8irTjhele2nW+L3N55ElFUBGB/f4w0rg8EW+/suhqv7kJJPTZzvHCqxlIw==", - "dependencies": { - "vue-demi": "*" - }, - "peerDependencies": { - "@vue/composition-api": "^1.1.0", - "vue": "^2.6.0 || ^3.2.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, - "vue": { - "optional": true - } - } - }, - "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.12.5", - "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz", - "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/@vueuse/metadata": { - "version": "8.4.2", - "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-8.4.2.tgz", - "integrity": "sha512-2BIj++7P0/I5dfMsEe8q7Kw0HqVAjVcyNOd9+G22/ILUC9TVLTeYOuJ1kwa1Gpr+0LWKHc6GqHiLWNL33+exoQ==" - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/async-validator": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.1.1.tgz", - "integrity": "sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001339", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001339.tgz", - "integrity": "sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ==", - "dev": true - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" - }, - "node_modules/dayjs": { - "version": "1.11.2", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.2.tgz", - "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/echarts": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.3.2.tgz", - "integrity": "sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==", - "dependencies": { - "tslib": "2.3.0", - "zrender": "5.3.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.137", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", - "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", - "dev": true - }, - "node_modules/element-plus": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.0.tgz", - "integrity": "sha512-zxmAFEAa1T/n09rR+NozXcWl5CjaFtqoaxhFSafag0dgc90tgEHitDXfegdFAl4ahugdNTqu9aLzngx3VhDAtA==", - "dependencies": { - "@ctrl/tinycolor": "^3.4.1", - "@element-plus/icons-vue": "^1.1.4", - "@floating-ui/dom": "^0.4.5", - "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.6", - "@types/lodash": "^4.14.182", - "@types/lodash-es": "^4.17.6", - "@vueuse/core": "^8.2.6", - "async-validator": "^4.0.7", - "dayjs": "^1.11.1", - "escape-html": "^1.0.3", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "lodash-unified": "^1.0.2", - "memoize-one": "^6.0.0", - "normalize-wheel-es": "^1.1.2" - }, - "peerDependencies": { - "vue": "^3.2.0" - } - }, - "node_modules/esbuild": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.38.tgz", - "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "esbuild-android-64": "0.14.38", - "esbuild-android-arm64": "0.14.38", - "esbuild-darwin-64": "0.14.38", - "esbuild-darwin-arm64": "0.14.38", - "esbuild-freebsd-64": "0.14.38", - "esbuild-freebsd-arm64": "0.14.38", - "esbuild-linux-32": "0.14.38", - "esbuild-linux-64": "0.14.38", - "esbuild-linux-arm": "0.14.38", - "esbuild-linux-arm64": "0.14.38", - "esbuild-linux-mips64le": "0.14.38", - "esbuild-linux-ppc64le": "0.14.38", - "esbuild-linux-riscv64": "0.14.38", - "esbuild-linux-s390x": "0.14.38", - "esbuild-netbsd-64": "0.14.38", - "esbuild-openbsd-64": "0.14.38", - "esbuild-sunos-64": "0.14.38", - "esbuild-windows-32": "0.14.38", - "esbuild-windows-64": "0.14.38", - "esbuild-windows-arm64": "0.14.38" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", - "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", - "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", - "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", - "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", - "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", - "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", - "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", - "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", - "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", - "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", - "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", - "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", - "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", - "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", - "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", - "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", - "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", - "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", - "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", - "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "node_modules/follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true - }, - "node_modules/html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "node_modules/lodash-unified": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.2.tgz", - "integrity": "sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==", - "peerDependencies": { - "@types/lodash-es": "*", - "lodash": "*", - "lodash-es": "*" - } - }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", - "dev": true - }, - "node_modules/normalize-wheel-es": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.1.2.tgz", - "integrity": "sha512-scX83plWJXYH1J4+BhAuIHadROzxX0UBF3+HuZNY2Ks8BciE7tSTQ+5JhTsvzjaO0/EJdm4JBGrfObKxFf3Png==" - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/pinia": { - "version": "2.0.14", - "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.0.14.tgz", - "integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==", - "dependencies": { - "@vue/devtools-api": "^6.1.4", - "vue-demi": "*" - }, - "peerDependencies": { - "@vue/composition-api": "^1.4.0", - "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.2.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/pinia/node_modules/vue-demi": { - "version": "0.12.5", - "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz", - "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/postcss": { - "version": "8.4.13", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.13.tgz", - "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", - "dependencies": { - "nanoid": "^3.3.3", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - } - }, - "node_modules/rollup": { - "version": "2.72.1", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-2.72.1.tgz", - "integrity": "sha512-NTc5UGy/NWFGpSqF1lFY8z9Adri6uhyMLI6LvPAXdBKoPRFhIIiBUpt+Qg2awixqO3xvzSijjhnb4+QEZwJmxA==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - }, - "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "devOptional": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/vite": { - "version": "2.9.8", - "resolved": "https://registry.npmmirror.com/vite/-/vite-2.9.8.tgz", - "integrity": "sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.27", - "postcss": "^8.4.13", - "resolve": "^1.22.0", - "rollup": "^2.59.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": ">=12.2.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.33.tgz", - "integrity": "sha512-si1ExAlDUrLSIg/V7D/GgA4twJwfsfgG+t9w10z38HhL/HA07132pUQ2KuwAo8qbCyMJ9e6OqrmWrOCr+jW7ZQ==", - "dependencies": { - "@vue/compiler-dom": "3.2.33", - "@vue/compiler-sfc": "3.2.33", - "@vue/runtime-dom": "3.2.33", - "@vue/server-renderer": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "node_modules/vue-router": { - "version": "4.0.15", - "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.15.tgz", - "integrity": "sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==", - "dependencies": { - "@vue/devtools-api": "^6.0.0" - }, - "peerDependencies": { - "vue": "^3.2.0" - } - }, - "node_modules/vue-tsc": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-0.34.12.tgz", - "integrity": "sha512-CmuqLXHEW5UvS8UpT2RYom5MzOWBD142PLXxDX0ARdZ/u1oLobA3od4XY2XZACQYCFCzjTvfD1H5wrWwiGwoUA==", - "dev": true, - "dependencies": { - "@volar/vue-typescript": "0.34.12" - }, - "bin": { - "vue-tsc": "bin/vue-tsc.js" - }, - "peerDependencies": { - "typescript": "*" - } - }, - "node_modules/zrender": { - "version": "5.3.1", - "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.3.1.tgz", - "integrity": "sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==", - "dependencies": { - "tslib": "2.3.0" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/compat-data": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.17.10.tgz", - "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", - "dev": true - }, - "@babel/core": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.17.10.tgz", - "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-compilation-targets": "^7.17.10", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.10", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.10", - "@babel/types": "^7.17.10", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", - "dev": true, - "requires": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", - "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.10", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.20.2", - "semver": "^6.3.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", - "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-member-expression-to-functions": "^7.17.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.17.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", - "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" - } - }, - "@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==" - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", - "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz", - "integrity": "sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.16.8", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz", - "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-typescript": "^7.16.7" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.17.9.tgz", - "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.9", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.9", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@ctrl/tinycolor": { - "version": "3.4.1", - "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", - "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==" - }, - "@element-plus/icons-vue": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-1.1.4.tgz", - "integrity": "sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==", - "requires": {} - }, - "@floating-ui/core": { - "version": "0.6.2", - "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-0.6.2.tgz", - "integrity": "sha512-jktYRmZwmau63adUG3GKOAVCofBXkk55S/zQ94XOorAHhwqFIOFAy1rSp2N0Wp6/tGbe9V3u/ExlGZypyY17rg==" - }, - "@floating-ui/dom": { - "version": "0.4.5", - "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-0.4.5.tgz", - "integrity": "sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw==", - "requires": { - "@floating-ui/core": "^0.6.2" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.11", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.11.tgz", - "integrity": "sha512-RllI476aSMsxzeI9TtlSMoNTgHDxEmnl6GkkHwhr0vdL8W+0WuesyI8Vd3rBOfrwtPXbPxdT9ADJdiOKgzxPQA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@popperjs/core": { - "version": "npm:@sxzz/popperjs-es@2.11.7", - "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", - "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==" - }, - "@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", - "dev": true, - "requires": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" - } - }, - "@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==" - }, - "@types/lodash-es": { - "version": "4.17.6", - "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.6.tgz", - "integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==", - "requires": { - "@types/lodash": "*" - } - }, - "@types/node": { - "version": "16.11.33", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.33.tgz", - "integrity": "sha512-0PJ0vg+JyU0MIan58IOIFRtSvsb7Ri+7Wltx2qAg94eMOrpg4+uuP3aUHCpxXc1i0jCXiC+zIamSZh3l9AbcQA==", - "dev": true - }, - "@vitejs/plugin-vue": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.2.tgz", - "integrity": "sha512-umyypfSHS4kQLdYAnJHhaASq7FRzNCdvcRoQ3uYGNk1/M4a+hXUd7ysN7BLhCrWH6uBokyCkFeUAaFDzSaaSrQ==", - "dev": true, - "requires": {} - }, - "@vitejs/plugin-vue-jsx": { - "version": "1.3.10", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-1.3.10.tgz", - "integrity": "sha512-Cf5zznh4yNMiEMBfTOztaDVDmK1XXfgxClzOSUVUc8WAmHzogrCUeM8B05ABzuGtg0D1amfng+mUmSIOFGP3Pw==", - "dev": true, - "requires": { - "@babel/core": "^7.17.9", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-transform-typescript": "^7.16.8", - "@rollup/pluginutils": "^4.2.0", - "@vue/babel-plugin-jsx": "^1.1.1", - "hash-sum": "^2.0.0" - } - }, - "@volar/code-gen": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/code-gen/-/code-gen-0.34.12.tgz", - "integrity": "sha512-5GAPsSjScnfMmMoh9qLW7CWQjjnT0fTUsPWnDMMjKIOqQF9J5mOyo7rprt1VzX63zwayqFfx7V8W3EVNhUCE3w==", - "dev": true, - "requires": { - "@volar/source-map": "0.34.12" - } - }, - "@volar/source-map": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-0.34.12.tgz", - "integrity": "sha512-07imKws1cz9g3eo0VWXdioNfc1eCjqwK7GsxVuYSc7OCzKASt9PywUW+F39QGB9g2Kewof+PjCVIPeGqGRECTA==", - "dev": true - }, - "@volar/vue-code-gen": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/vue-code-gen/-/vue-code-gen-0.34.12.tgz", - "integrity": "sha512-PFcft62eIvQvcB6H2Z88fouTu2JmYwimORziFGr3LlGriQUEVmyDtqddtb+E+j2wGChtLkh6hf1py94C5VpI/Q==", - "dev": true, - "requires": { - "@volar/code-gen": "0.34.12", - "@volar/source-map": "0.34.12", - "@vue/compiler-core": "^3.2.31", - "@vue/compiler-dom": "^3.2.31", - "@vue/shared": "^3.2.31" - } - }, - "@volar/vue-typescript": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-0.34.12.tgz", - "integrity": "sha512-mY5cZ2OFOKt1HcCuoX1ViEsccltX3mdACk/FAjrSZTrilTdVHI1zkmQlrpCSnjmE1qowd8I6YoVt7THCaVrHdg==", - "dev": true, - "requires": { - "@volar/code-gen": "0.34.12", - "@volar/source-map": "0.34.12", - "@volar/vue-code-gen": "0.34.12", - "@vue/compiler-sfc": "^3.2.31", - "@vue/reactivity": "^3.2.31" - } - }, - "@vue/babel-helper-vue-transform-on": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", - "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==", - "dev": true - }, - "@vue/babel-plugin-jsx": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz", - "integrity": "sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "@vue/babel-helper-vue-transform-on": "^1.0.2", - "camelcase": "^6.0.0", - "html-tags": "^3.1.0", - "svg-tags": "^1.0.0" - } - }, - "@vue/compiler-core": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.33.tgz", - "integrity": "sha512-AAmr52ji3Zhk7IKIuigX2osWWsb2nQE5xsdFYjdnmtQ4gymmqXbjLvkSE174+fF3A3kstYrTgGkqgOEbsdLDpw==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.33", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-dom": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.33.tgz", - "integrity": "sha512-GhiG1C8X98Xz9QUX/RlA6/kgPBWJkjq0Rq6//5XTAGSYrTMBgcLpP9+CnlUg1TFxnnCVughAG+KZl28XJqw8uQ==", - "requires": { - "@vue/compiler-core": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "@vue/compiler-sfc": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.33.tgz", - "integrity": "sha512-H8D0WqagCr295pQjUYyO8P3IejM3vEzeCO1apzByAEaAR/WimhMYczHfZVvlCE/9yBaEu/eu9RdiWr0kF8b71Q==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.33", - "@vue/compiler-dom": "3.2.33", - "@vue/compiler-ssr": "3.2.33", - "@vue/reactivity-transform": "3.2.33", - "@vue/shared": "3.2.33", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-ssr": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.33.tgz", - "integrity": "sha512-XQh1Xdk3VquDpXsnoCd7JnMoWec9CfAzQDQsaMcSU79OrrO2PNR0ErlIjm/mGq3GmBfkQjzZACV+7GhfRB8xMQ==", - "requires": { - "@vue/compiler-dom": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "@vue/devtools-api": { - "version": "6.1.4", - "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz", - "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==" - }, - "@vue/reactivity": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.33.tgz", - "integrity": "sha512-62Sq0mp9/0bLmDuxuLD5CIaMG2susFAGARLuZ/5jkU1FCf9EDbwUuF+BO8Ub3Rbodx0ziIecM/NsmyjardBxfQ==", - "requires": { - "@vue/shared": "3.2.33" - } - }, - "@vue/reactivity-transform": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.33.tgz", - "integrity": "sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.33", - "@vue/shared": "3.2.33", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - } - }, - "@vue/runtime-core": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.33.tgz", - "integrity": "sha512-N2D2vfaXsBPhzCV3JsXQa2NECjxP3eXgZlFqKh4tgakp3iX6LCGv76DLlc+IfFZq+TW10Y8QUfeihXOupJ1dGw==", - "requires": { - "@vue/reactivity": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "@vue/runtime-dom": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz", - "integrity": "sha512-LSrJ6W7CZTSUygX5s8aFkraDWlO6K4geOwA3quFF2O+hC3QuAMZt/0Xb7JKE3C4JD4pFwCSO7oCrZmZ0BIJUnw==", - "requires": { - "@vue/runtime-core": "3.2.33", - "@vue/shared": "3.2.33", - "csstype": "^2.6.8" - } - }, - "@vue/server-renderer": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.33.tgz", - "integrity": "sha512-4jpJHRD4ORv8PlbYi+/MfP8ec1okz6rybe36MdpkDrGIdEItHEUyaHSKvz+ptNEyQpALmmVfRteHkU9F8vxOew==", - "requires": { - "@vue/compiler-ssr": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "@vue/shared": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.33.tgz", - "integrity": "sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==" - }, - "@vue/tsconfig": { - "version": "0.1.3", - "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.1.3.tgz", - "integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==", - "dev": true, - "requires": {} - }, - "@vueuse/core": { - "version": "8.4.2", - "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-8.4.2.tgz", - "integrity": "sha512-dUVU96lii1ZdWoNJXauQNt+4QrHz1DKbuW+y6pDR2N10q7rGZJMDU7pQeMcC2XeosX7kMODfaBuqsF03NozzLg==", - "requires": { - "@vueuse/metadata": "8.4.2", - "@vueuse/shared": "8.4.2", - "vue-demi": "*" - }, - "dependencies": { - "@vueuse/shared": { - "version": "8.4.2", - "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-8.4.2.tgz", - "integrity": "sha512-hILXMEjL8YQhj1LHN/HZ49UThyfk8irTjhele2nW+L3N55ElFUBGB/f4w0rg8EW+/suhqv7kJJPTZzvHCqxlIw==", - "requires": { - "vue-demi": "*" - } - }, - "vue-demi": { - "version": "0.12.5", - "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz", - "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", - "requires": {} - } - } - }, - "@vueuse/metadata": { - "version": "8.4.2", - "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-8.4.2.tgz", - "integrity": "sha512-2BIj++7P0/I5dfMsEe8q7Kw0HqVAjVcyNOd9+G22/ILUC9TVLTeYOuJ1kwa1Gpr+0LWKHc6GqHiLWNL33+exoQ==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "async-validator": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.1.1.tgz", - "integrity": "sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001339", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001339.tgz", - "integrity": "sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" - }, - "dayjs": { - "version": "1.11.2", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.2.tgz", - "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "echarts": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.3.2.tgz", - "integrity": "sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==", - "requires": { - "tslib": "2.3.0", - "zrender": "5.3.1" - } - }, - "electron-to-chromium": { - "version": "1.4.137", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", - "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", - "dev": true - }, - "element-plus": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.0.tgz", - "integrity": "sha512-zxmAFEAa1T/n09rR+NozXcWl5CjaFtqoaxhFSafag0dgc90tgEHitDXfegdFAl4ahugdNTqu9aLzngx3VhDAtA==", - "requires": { - "@ctrl/tinycolor": "^3.4.1", - "@element-plus/icons-vue": "^1.1.4", - "@floating-ui/dom": "^0.4.5", - "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.6", - "@types/lodash": "^4.14.182", - "@types/lodash-es": "^4.17.6", - "@vueuse/core": "^8.2.6", - "async-validator": "^4.0.7", - "dayjs": "^1.11.1", - "escape-html": "^1.0.3", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "lodash-unified": "^1.0.2", - "memoize-one": "^6.0.0", - "normalize-wheel-es": "^1.1.2" - } - }, - "esbuild": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.38.tgz", - "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", - "dev": true, - "requires": { - "esbuild-android-64": "0.14.38", - "esbuild-android-arm64": "0.14.38", - "esbuild-darwin-64": "0.14.38", - "esbuild-darwin-arm64": "0.14.38", - "esbuild-freebsd-64": "0.14.38", - "esbuild-freebsd-arm64": "0.14.38", - "esbuild-linux-32": "0.14.38", - "esbuild-linux-64": "0.14.38", - "esbuild-linux-arm": "0.14.38", - "esbuild-linux-arm64": "0.14.38", - "esbuild-linux-mips64le": "0.14.38", - "esbuild-linux-ppc64le": "0.14.38", - "esbuild-linux-riscv64": "0.14.38", - "esbuild-linux-s390x": "0.14.38", - "esbuild-netbsd-64": "0.14.38", - "esbuild-openbsd-64": "0.14.38", - "esbuild-sunos-64": "0.14.38", - "esbuild-windows-32": "0.14.38", - "esbuild-windows-64": "0.14.38", - "esbuild-windows-arm64": "0.14.38" - } - }, - "esbuild-android-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", - "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", - "dev": true, - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", - "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", - "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", - "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", - "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", - "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", - "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", - "dev": true, - "optional": true - }, - "esbuild-linux-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", - "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", - "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", - "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", - "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", - "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", - "dev": true, - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", - "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", - "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", - "dev": true, - "optional": true - }, - "esbuild-netbsd-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", - "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", - "dev": true, - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", - "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", - "dev": true, - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", - "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", - "dev": true, - "optional": true - }, - "esbuild-windows-32": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", - "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", - "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", - "dev": true, - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.14.38", - "resolved": "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", - "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", - "dev": true, - "optional": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==" - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true - }, - "html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", - "dev": true - }, - "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "dev": true - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "lodash-unified": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.2.tgz", - "integrity": "sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==", - "requires": {} - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", - "dev": true - }, - "normalize-wheel-es": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.1.2.tgz", - "integrity": "sha512-scX83plWJXYH1J4+BhAuIHadROzxX0UBF3+HuZNY2Ks8BciE7tSTQ+5JhTsvzjaO0/EJdm4JBGrfObKxFf3Png==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pinia": { - "version": "2.0.14", - "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.0.14.tgz", - "integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==", - "requires": { - "@vue/devtools-api": "^6.1.4", - "vue-demi": "*" - }, - "dependencies": { - "vue-demi": { - "version": "0.12.5", - "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz", - "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", - "requires": {} - } - } - }, - "postcss": { - "version": "8.4.13", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.13.tgz", - "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", - "requires": { - "nanoid": "^3.3.3", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "rollup": { - "version": "2.72.1", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-2.72.1.tgz", - "integrity": "sha512-NTc5UGy/NWFGpSqF1lFY8z9Adri6uhyMLI6LvPAXdBKoPRFhIIiBUpt+Qg2awixqO3xvzSijjhnb4+QEZwJmxA==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - }, - "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "devOptional": true - }, - "vite": { - "version": "2.9.8", - "resolved": "https://registry.npmmirror.com/vite/-/vite-2.9.8.tgz", - "integrity": "sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==", - "dev": true, - "requires": { - "esbuild": "^0.14.27", - "fsevents": "~2.3.2", - "postcss": "^8.4.13", - "resolve": "^1.22.0", - "rollup": "^2.59.0" - } - }, - "vue": { - "version": "3.2.33", - "resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.33.tgz", - "integrity": "sha512-si1ExAlDUrLSIg/V7D/GgA4twJwfsfgG+t9w10z38HhL/HA07132pUQ2KuwAo8qbCyMJ9e6OqrmWrOCr+jW7ZQ==", - "requires": { - "@vue/compiler-dom": "3.2.33", - "@vue/compiler-sfc": "3.2.33", - "@vue/runtime-dom": "3.2.33", - "@vue/server-renderer": "3.2.33", - "@vue/shared": "3.2.33" - } - }, - "vue-router": { - "version": "4.0.15", - "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.15.tgz", - "integrity": "sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==", - "requires": { - "@vue/devtools-api": "^6.0.0" - } - }, - "vue-tsc": { - "version": "0.34.12", - "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-0.34.12.tgz", - "integrity": "sha512-CmuqLXHEW5UvS8UpT2RYom5MzOWBD142PLXxDX0ARdZ/u1oLobA3od4XY2XZACQYCFCzjTvfD1H5wrWwiGwoUA==", - "dev": true, - "requires": { - "@volar/vue-typescript": "0.34.12" - } - }, - "zrender": { - "version": "5.3.1", - "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.3.1.tgz", - "integrity": "sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==", - "requires": { - "tslib": "2.3.0" - } - } - } -} diff --git a/frontend/package.json b/frontend/package.json deleted file mode 100644 index ddceed39c74ae6d01bc8d2fd2622726c70540bf4..0000000000000000000000000000000000000000 --- a/frontend/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "frontend", - "version": "3.1.0", - "scripts": { - "dev": "vite", - "build": "vue-tsc --noEmit && vite build", - "preview": "vite preview --port 5050", - "typecheck": "vue-tsc --noEmit" - }, - "dependencies": { - "axios": "^0.27.2", - "echarts": "^5.3.2", - "element-plus": "^2.1.11", - "pinia": "^2.0.13", - "vue": "^3.2.33", - "vue-router": "^4.0.14" - }, - "devDependencies": { - "@types/node": "^16.11.27", - "@vitejs/plugin-vue": "^2.3.1", - "@vitejs/plugin-vue-jsx": "^1.3.10", - "@vue/tsconfig": "^0.1.3", - "typescript": "~4.6.3", - "vite": "^2.9.5", - "vue-tsc": "^0.34.7" - } -} diff --git a/frontend/src/App.vue b/frontend/src/App.vue deleted file mode 100644 index 26d8165fdf977c5eabf50b27c9f5ad828269d631..0000000000000000000000000000000000000000 --- a/frontend/src/App.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts deleted file mode 100644 index c44c2e23221b2895761111baecf6a2efec4e6c7b..0000000000000000000000000000000000000000 --- a/frontend/src/api/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -import axios from "axios"; -import { get, post, put, del } from "@/request"; -import type { GetUserInfo, Todo, TableDataList, TableData, TableObject, DelDataList, CourseId, Details } from "./model"; - -/** - * 退出登录 - */ -export const logout = (): Promise => post("/logout"); - -/** - * 获取用户信息 - */ -export const getUserInfo = (data: GetUserInfo): Promise => get(`${data.roles}/index`); - -/** - * 查询首页数据(访问量 && 待办事项 && 请求数 && 待办事项) - */ -export const getDashboard = (): Promise => get("/dashboard"); - -/** - * 添加待办 - */ -export const addTodo = (data: Todo): Promise => post(`/todo/add`, { ...data }); - -/** - * 根据索引更新待办 - */ -export const updateTodo = (data: Todo): Promise => post(`/todo/update`, { ...data }); - -/** - * 获取表的数据 - */ -export const readDatas = (data: TableDataList): Promise => { - const { path, ...rest } = data; - return get(`${path}/`, { ...rest }); -}; - -/** - * 根据 id 查询信息 - */ -export const readData = (data: TableData): Promise => get(`${data.path}/${data.id}`); - -/** - * 添加表格信息 - */ -export const createData = (data: TableObject): Promise => { - let { path, ...rest } = data; - return post(`${path}/`, { ...rest }); -}; - -/** - * 更新表格信息 - */ -export const updateData = (data: TableObject): Promise => { - let { path, id, ...rest } = data; - return put(`${path}/${id}`, { ...rest }); -}; - -/** - * 根据 id 删除表格信息 - * @param {*} data id - */ -export const deleteData = (data: TableData): Promise => del(`${data.path}/${data.id}`); - -/** - * 同时删除多个表格信息 - * @param {*} data id列表 - */ -export const deleteDatas = (data: DelDataList): Promise => post(`${data.path}/del/`, data.idList); - -/** - * 得到讲授详情 - */ -export const getTughtDetail = (data: Details): Promise => get(`${data.path}/detail/`); - -/** - * 根据 课程id 添加选课信息 - */ -export const addByCourseId = (data: CourseId): Promise => post(`${data.path}/add/${data.courseId}`); - -/** - * 根据 课程id 删除选课信息 - */ -export const delByCourseId = (data: CourseId): Promise => post(`${data.path}/del/${data.courseId}`); - -/** - * 得到选课详情 - */ -export const getElectiveDetail = (data: Details): Promise => get(`${data.path}/detail/`); - -/** - * 获取语言详情 - */ -export const getLangList = async (): Promise => { - return await axios.get("https://api.github.com/repos/zxiaosi/Vue3-FastAPI/languages"); -}; diff --git a/frontend/src/api/login.ts b/frontend/src/api/login.ts deleted file mode 100644 index eee4b1b6afc3dd18c11fdb8dcc3cf49d0a4d9d61..0000000000000000000000000000000000000000 --- a/frontend/src/api/login.ts +++ /dev/null @@ -1,30 +0,0 @@ -import http from "@/request/http"; -import { API_URL } from "@/assets/js/global"; -import type { Login } from "./model"; - -/** - * 登录(发送表单请求) - */ -export const login = async (data: Login): Promise => - http.request({ - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - url: `${API_URL}/login`, - method: "POST", - data: data, - transformRequest: [ - // 请求时, 将{username:111,password:111} 转成 username=111&password=111 - function (data) { - var ret = ""; - for (var it in data) { - // 判断是否是数组 - if (Array.isArray(data[it])) { - let tmp = data[it].join(" "); // 将 ['admin','teacher','student'] 转为 ['admin' 'teacher' 'student'] - ret += encodeURIComponent(it) + "=" + encodeURIComponent(tmp) + "&"; - } else { - ret += encodeURIComponent(it) + "=" + encodeURIComponent(data[it]) + "&"; - } - } - return ret.substring(0, ret.length - 1); - }, - ], - }); diff --git a/frontend/src/api/model.ts b/frontend/src/api/model.ts deleted file mode 100644 index 7f48b542df6482f29ecb8228ecb7a7667a002ba9..0000000000000000000000000000000000000000 --- a/frontend/src/api/model.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { FormData, PathEnum } from "@/types/table"; - -/** - * 登录 - */ -export interface Login { - username: string; - password: string; - scope: any; -} - -/** - * 获取用户信息 - */ -export interface GetUserInfo { - roles: "admin" | "student" | "teacher"; -} - -/** - * 添加待办/根据索引更新待办 - */ -export interface Todo { - id?: number; // 待办索引(临时数据) - title?: string; // 待办文案 - status?: boolean; // 是否选中 -} - -/** - * 获取所有数据(表格数据) - */ -export interface TableDataList { - path: PathEnum; // 路径参数 - pageIndex?: number; // 页码 - pageSize?: number; // 每页个数 -} - -/** - * 根据id查询/删除(多条)信息 - */ -export interface TableData { - path: PathEnum; // 路径参数 - id: number | string; // id -} - -/** - * 添加/更新信息 - */ -export interface TableObject extends FormData { - path: PathEnum; // 路径参数 -} - -/** - * 同时删除多个信息 - */ -export interface DelDataList { - path: PathEnum; // 路径参数 - idList: number[] | string[]; // id列表 -} - -/** - * 根据课程id添加/删除选课信息 - */ -export interface CourseId { - path: PathEnum; // 路径参数 - courseId: number | string; // 课程id -} -/** - * 得到课程详情 - */ -export interface Details { - path: PathEnum; // 路径参数 -} diff --git a/frontend/src/assets/css/color-dark.css b/frontend/src/assets/css/color-dark.css deleted file mode 100644 index ae3f22ab7d71c7328b75afd3c3d76c27abd1cc09..0000000000000000000000000000000000000000 --- a/frontend/src/assets/css/color-dark.css +++ /dev/null @@ -1,28 +0,0 @@ -.header { - background-color: #242f42; -} -.login-wrap { - background: #324157; -} -.plugins-tips { - background: #eef1f6; -} -.plugins-tips a { - color: #20a0ff; -} -.el-upload--text em { - color: #20a0ff; -} -.pure-button { - background: #20a0ff; -} -.tags-li.active { - border: 1px solid #409eff; - background-color: #409eff; -} -.message-title { - color: #20a0ff; -} -.collapse-btn:hover { - background: rgb(40, 52, 70); -} diff --git a/frontend/src/assets/css/icon.css b/frontend/src/assets/css/icon.css deleted file mode 100644 index 13713e4ad38e1e9330310de7f85d4da3fc73d4cb..0000000000000000000000000000000000000000 --- a/frontend/src/assets/css/icon.css +++ /dev/null @@ -1,9 +0,0 @@ -/* 修改图标见 https://www.jianshu.com/p/59dd28f0b9c9 */ -[class^="el-icon-ali"], -[class*=" el-icon-ali"] { - font-family: "iconfont" !important; - font-size: 1rem; - font-style: normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/frontend/src/assets/css/main.css b/frontend/src/assets/css/main.css deleted file mode 100644 index e8d67360074ab98cff6b399b74889f0674ef9395..0000000000000000000000000000000000000000 --- a/frontend/src/assets/css/main.css +++ /dev/null @@ -1,116 +0,0 @@ -* { - margin: 0; - padding: 0; -} - -html, -body, -#app, -.wrapper { - width: 100%; - height: 100%; - overflow: hidden; -} - -body { - font-family: "PingFang SC", "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif; -} - -a { - text-decoration: none; -} - -.content-box { - position: absolute; - left: 220px; - right: 0; - top: 70px; - bottom: 0; - padding-bottom: 30px; - -webkit-transition: left 0.3s ease-in-out; - transition: left 0.3s ease-in-out; - background: #f0f0f0; -} - -.content { - width: auto; - height: 100%; - padding: 10px; - overflow-y: scroll; - box-sizing: border-box; -} - -.content-collapse { - left: 65px; -} - -.container { - padding: 30px; - background: #fff; - border: 1px solid #ddd; - border-radius: 5px; -} - -.crumbs { - margin: 10px 0; -} - -.el-table th { - background-color: #f5f7fa !important; -} - -.pagination { - margin: 20px 0; - text-align: right; -} - -.plugins-tips { - padding: 20px 10px; - margin-bottom: 20px; -} - -.el-button + .el-tooltip { - margin-left: 10px; -} - -.el-table tr:hover { - background: #f6faff; -} - -.mgb20 { - margin-bottom: 20px; -} - -.move-enter-active, -.move-leave-active { - transition: opacity 0.1s ease; -} - -.move-enter-from, -.move-leave-to { - opacity: 0; -} - -/*BaseForm*/ - -.form-box { - width: 600px; -} - -.form-box .line { - text-align: center; -} - -.el-time-panel__content::after, -.el-time-panel__content::before { - margin-top: -7px; -} - -.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) { - padding-bottom: 0; -} - -/* element-icon 图标 (对应 size:20) */ -/* .el-icon{ - --font-size: 20px !important; -} */ diff --git a/frontend/src/assets/img/img.jpg b/frontend/src/assets/img/img.jpg deleted file mode 100644 index b5901c1f7ad0d5e58f2a1953ae7a462dde7ff4df..0000000000000000000000000000000000000000 Binary files a/frontend/src/assets/img/img.jpg and /dev/null differ diff --git a/frontend/src/assets/js/global.ts b/frontend/src/assets/js/global.ts deleted file mode 100644 index c82130b9c31c62f4dbdde6c70e5bb8ad9cb399da..0000000000000000000000000000000000000000 --- a/frontend/src/assets/js/global.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 全局标题 - */ -export const TITLE: string = "学生选课系统"; - -/** - * axios-baseUrl - */ -// export const API_URL: string = `http://127.0.0.1:8000/api`; // 开发环境 -export const API_URL: string = `http://8.136.82.204:8000/api`; // 线上环境 - -/** - * axios-timeOut - */ -export const TIME_OUT: number = 15000; diff --git a/frontend/src/assets/logo.svg b/frontend/src/assets/logo.svg deleted file mode 100644 index 0f544ccb17f814f09305cf31512a8c86aea1b9be..0000000000000000000000000000000000000000 --- a/frontend/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/components/BaseTable/index.ts b/frontend/src/components/BaseTable/index.ts deleted file mode 100644 index c4e96ab1acb238f10833c1466f1af8d9fa325589..0000000000000000000000000000000000000000 --- a/frontend/src/components/BaseTable/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { FormData } from "@/types/table"; - -export interface State { - pageName: any; - searched: FormData[]; - isShowSearched: boolean; - selectedList: string[]; - relationData: any; - showDialog: boolean; - addOrUpdate: boolean; - isStudent: boolean; - isTeacher: boolean; -} diff --git a/frontend/src/components/BaseTable/index.vue b/frontend/src/components/BaseTable/index.vue deleted file mode 100644 index 3d3233de747318806fdedc01493653435dc73ed4..0000000000000000000000000000000000000000 --- a/frontend/src/components/BaseTable/index.vue +++ /dev/null @@ -1,390 +0,0 @@ - - - - - diff --git a/frontend/src/components/Breadcrumb/index.vue b/frontend/src/components/Breadcrumb/index.vue deleted file mode 100644 index cbceea651bfea222711d30f22ec91a39b55373c5..0000000000000000000000000000000000000000 --- a/frontend/src/components/Breadcrumb/index.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/frontend/src/components/LoadingBar/index.vue b/frontend/src/components/LoadingBar/index.vue deleted file mode 100644 index 36e5e2a41f03223ada1c0fe45017962c1ccc8099..0000000000000000000000000000000000000000 --- a/frontend/src/components/LoadingBar/index.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - - diff --git a/frontend/src/components/Pagination/index.vue b/frontend/src/components/Pagination/index.vue deleted file mode 100644 index 7c8e7c9dd073e4df459c8c09dc4588a3d21b3e1e..0000000000000000000000000000000000000000 --- a/frontend/src/components/Pagination/index.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - diff --git a/frontend/src/layout/Header/index.vue b/frontend/src/layout/Header/index.vue deleted file mode 100644 index e092635ad2a9d63ecc20f6e38598810d9902f6ea..0000000000000000000000000000000000000000 --- a/frontend/src/layout/Header/index.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - diff --git a/frontend/src/layout/Home/index.vue b/frontend/src/layout/Home/index.vue deleted file mode 100644 index e62e070c7d0891c4fac506721c15775154db5429..0000000000000000000000000000000000000000 --- a/frontend/src/layout/Home/index.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/frontend/src/layout/Sidebar/index.vue b/frontend/src/layout/Sidebar/index.vue deleted file mode 100644 index 33dbd4c43b30a6c3b3f9ba9b10ece0b4de3fcd0c..0000000000000000000000000000000000000000 --- a/frontend/src/layout/Sidebar/index.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - diff --git a/frontend/src/layout/Tags/index.vue b/frontend/src/layout/Tags/index.vue deleted file mode 100644 index 45aca8c1a87f7e2ae45bcd425d371610afa736df..0000000000000000000000000000000000000000 --- a/frontend/src/layout/Tags/index.vue +++ /dev/null @@ -1,193 +0,0 @@ - - - - - diff --git a/frontend/src/main.ts b/frontend/src/main.ts deleted file mode 100644 index 996ce2655ed6f131de326517fd4029a391b5855f..0000000000000000000000000000000000000000 --- a/frontend/src/main.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createApp } from "vue"; -import { createPinia } from "pinia"; // 状态管理 - -import App from "./App.vue"; -import router from "./router"; // 路由 - -// 全局ElementPlus -import ElementPlus from "element-plus"; -import zhCn from "element-plus/es/locale/lang/zh-cn"; // 中文 -import "element-plus/dist/index.css"; // 样式文件 -import "@/assets/css/icon.css"; // 阿里云图标 - -const app = createApp(App); - -app.use(createPinia()); -app.use(router); -app.use(ElementPlus, { locale: zhCn }); - -app.mount("#app"); diff --git a/frontend/src/request/auth.ts b/frontend/src/request/auth.ts deleted file mode 100644 index 85d291e348b462ea1888def4ef674556233fadbd..0000000000000000000000000000000000000000 --- a/frontend/src/request/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -const TokenKey = "Authorization$://"; //授权码 -/* - * 获取getItem - * */ -export function getLocal(key?: string) { - return localStorage.getItem(key ? key : TokenKey) as any; -} - -/* - * 设置setItem - * */ -export function setLocal(key: string | undefined, params: any) { - return localStorage.setItem(key ? key : TokenKey, params); -} - -/* - * 移除removeItem - * */ -export function removeLocal(key?: string) { - return localStorage.removeItem(key ? key : TokenKey); -} - -/* - * 清空所有Item - * */ -export function clearLocal() { - return localStorage.clear(); -} diff --git a/frontend/src/request/http.ts b/frontend/src/request/http.ts deleted file mode 100644 index f9b3535855003e38f9a4865cf9345a3a5f1c1db9..0000000000000000000000000000000000000000 --- a/frontend/src/request/http.ts +++ /dev/null @@ -1,135 +0,0 @@ -import axios, { AxiosError, type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from "axios"; -import { ElLoading, ElMessage } from "element-plus"; -import type { LoadingInstance } from "element-plus/lib/components/loading/src/loading"; -import type { ResponseData } from "@/types"; -import { API_URL, TIME_OUT } from "@/assets/js/global"; -import { clearLocal, getLocal } from "./auth"; -import { showStatus } from "./statusCode"; - -// 请求 -class AppRequest { - instance: AxiosInstance; // axios实例 - loading?: LoadingInstance; // 加载动画 - - // 构造器 - constructor(config: AxiosRequestConfig) { - this.instance = axios.create(config); - } - - /** - * 自定义请求 - */ - request(config: AxiosRequestConfig): Promise> { - return new Promise((resolve, reject) => { - this.instance - .request(config) - .then((res) => resolve(res.data)) - .catch((err) => reject(err)); - }); - } - - /** - * 开始加载动画: https://www.cxybb.com/article/weixin_45685252/114917309 - */ - startLoading() { - this.closeLoading(); // 先清除动画, 防止连续请求多次加载 - - this.loading = ElLoading.service({ - target: ".el-table, .ms-content, .info", // 设置加载动画区域(样式class名) - lock: true, // 锁定屏幕的滚动 - text: "正在请求数据...", // 显示文案 - }); - - // 设定定时器,超时5S后自动关闭遮罩层,避免请求失败时,遮罩层一直存在的问题 - setTimeout(() => this.closeLoading(), 5000); // 关闭遮罩层 - } - - /** - * 关闭加载动画 - */ - closeLoading() { - this.loading?.close(); // 关闭遮罩层 - } -} - -// 创建对象 -const http = new AppRequest({ - baseURL: API_URL, // 请求地址 - timeout: TIME_OUT, // 超时时间 - headers: { "Content-Type": "application/json;charset=utf-8" }, // 请求头 - transformRequest: [ - (data) => { - // 请求参数序列化(对象转字符串) - return JSON.stringify(data); - }, - ], - validateStatus() { - // 使用async-await,处理reject情况较为繁琐,所以全部返回resolve,在业务代码中处理异常 - return true; - }, - transformResponse: [ - (data) => { - // 响应数据反序列化(字符串转对象) - if (typeof data === "string" && data.startsWith("{")) { - data = JSON.parse(data); - } - return data; - }, - ], -}); - -// 请求拦截器 -http.instance.interceptors.request.use( - (config: AxiosRequestConfig) => { - http.startLoading(); // 加载动画 - - //获取token,并将其添加至请求头中 - let token = getLocal("Authorization"); - if (token) { - // @ts-ignore (防止下面报错) - config.headers.Authorization = "Bearer " + token; // 前面一定要加 Bearer - } - - return config; - }, - (error: AxiosError) => { - // @ts-ignore - error.data.msg = "请求超时或服务器异常,请检查网络或联系管理员!"; - return Promise.reject(error); - } -); - -// 响应拦截器 -http.instance.interceptors.response.use( - (response: AxiosResponse) => { - http.closeLoading(); // 关闭加载动画 - - let msg = ""; - if (response.status == 200 && typeof response.status == "number") { - // 请求成功 - return response; - } else if (response.status == 401) { - // 后端验证是否有token,没有则返回401 - window.location.href = "/login"; // 跳转登录 - clearLocal(); // 清除本地存储 - msg = "Token已过期,请重新登录!"; - } else if (response.status == 403) { - window.location.href = "/403"; // 没有权限 - msg = "没有权限!"; - } else { - if (response.data.msg != null) { - msg = response.data.msg; // 后端返回的msg - } else { - msg = showStatus(response.status); // 后端未返回msg,前端根据状态码自定义的msg - } - } - ElMessage.error({ message: msg, grouping: true }); - }, - (error: AxiosError) => { - let msg = showStatus(error.response?.status); - ElMessage.error(msg); - return Promise.reject(error); - } -); - -export default http; diff --git a/frontend/src/request/index.ts b/frontend/src/request/index.ts deleted file mode 100644 index b7c54c4fd057d7475f76ea4723512f05cf4c591a..0000000000000000000000000000000000000000 --- a/frontend/src/request/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import http from "./http"; -import type { RequestData, ResponseData } from "@/types"; - -export function get(url: string, data?: RequestData | string): Promise> { - return http.request({ url, params: data, method: "GET" }); -} - -export function post(url: string, data?: RequestData | string): Promise> { - return http.request({ url, data: data, method: "POST" }); -} - -export function put(url: string, data?: RequestData | string): Promise> { - return http.request({ url, data: data, method: "PUT" }); -} - -export function del(url: string, data?: RequestData | string): Promise> { - return http.request({ url, params: data, method: "DELETE" }); -} - -export default http; diff --git a/frontend/src/request/statusCode.ts b/frontend/src/request/statusCode.ts deleted file mode 100644 index 8e18adcc84e205dea6daacba95a0ed05e6cab70e..0000000000000000000000000000000000000000 --- a/frontend/src/request/statusCode.ts +++ /dev/null @@ -1,41 +0,0 @@ -export const showStatus = (status?: number) => { - let message = ""; - switch (status) { - case 400: - message = "请求错误(400)"; - break; - case 401: - message = "未授权,请重新登录(401)"; - break; - case 403: - message = "拒绝访问(403)"; - break; - case 404: - message = "请求出错(404)"; - break; - case 408: - message = "请求超时(408)"; - break; - case 500: - message = "服务器错误(500)"; - break; - case 501: - message = "服务未实现(501)"; - break; - case 502: - message = "网络错误(502)"; - break; - case 503: - message = "服务不可用(503)"; - break; - case 504: - message = "网络超时(504)"; - break; - case 505: - message = "HTTP版本不受支持(505)"; - break; - default: - message = `连接出错(${status})!`; - } - return `${message},请检查网络或联系管理员!`; -}; diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts deleted file mode 100644 index 57427eb45773f47b0d56fb2e030191dc31ffd064..0000000000000000000000000000000000000000 --- a/frontend/src/router/index.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router"; -import Layout from "@/layout/Home/index.vue"; -import { getLocal } from "@/request/auth"; -import { TITLE } from "@/assets/js/global"; - -const routes: RouteRecordRaw[] = [ - { path: "/", redirect: "/dashboard" }, // 重定向 - { - path: "/login", - name: "Login", - meta: { title: "登录" }, - component: () => import("@/views/login/index.vue"), - }, - { - path: "/", - name: "Layout", - component: Layout, - children: [ - { - path: "/dashboard", - name: "Dashboard", - meta: { title: "系统首页", icon: "dashboard", roles: ["admin", "teacher", "student"] }, - component: () => import("@/views/dashboard/index.vue"), - }, - { - path: "/settings", - name: "Settings", - meta: { title: "系统管理", icon: "setting", roles: ["admin", "teacher", "student"] }, - component: () => import("@/views/settings/index.vue"), - children: [ - { - path: "/department", - name: "Department", - meta: { title: "院系管理", roles: ["admin"], icon: "dept" }, - component: () => import("@/views/settings/department/index.vue"), - }, - { - path: "/major", - name: "Major", - meta: { title: "专业管理", roles: ["admin"], icon: "major" }, - component: () => import("@/views/settings/major/index.vue"), - }, - { - path: "/teacher", - name: "Teacher", - meta: { title: "教师管理", roles: ["admin"], icon: "tutor" }, - component: () => import("@/views/settings/teacher/index.vue"), - }, - { - path: "/student", - name: "Student", - meta: { title: "学生管理", roles: ["admin"], icon: "stu" }, - component: () => import("@/views/settings/student/index.vue"), - }, - { - path: "/course", - name: "Course", - meta: { title: "课程管理", roles: ["admin", "teacher", "student"], icon: "intro" }, - component: () => import("@/views/settings/course/index.vue"), - }, - { - path: "/elective", - name: "Elective", - meta: { title: "选课管理", roles: ["admin"], icon: "sc" }, - component: () => import("@/views/settings/elective/index.vue"), - }, - { - path: "/taught", - name: "Taught", - meta: { title: "讲授管理", roles: ["admin"], icon: "taught" }, - component: () => import("@/views/settings/taught/index.vue"), - }, - { - path: "/myTaught", - name: "MyTaught", - meta: { title: "我的讲授", roles: ["teacher"], icon: "taught" }, - component: () => import("@/views/settings/myTaught/index.vue"), - }, - { - path: "/myElective", - name: "MyElective", - meta: { title: "我的课程", roles: ["student"], icon: "sc" }, - component: () => import("@/views/settings/myElective/index.vue"), - }, - ], - }, - { - path: "/messages", - name: "Messages", - meta: { title: "消息中心", icon: "msg", roles: ["admin", "teacher", "student"] }, - component: () => import("@/views/messages/index.vue"), - }, - { - path: "/user", - name: "User", - meta: { title: "个人中心", icon: "user", roles: ["admin", "teacher", "student"] }, - component: () => import("@/views/user/index.vue"), - }, - ], - }, - { - path: "/403", - name: "403", - meta: { title: "没有权限" }, - component: () => import("@/views/error/403/index.vue"), - }, - { - path: "/:pathMatch(.*)*", - name: "404", - meta: { title: "找不到页面" }, - component: () => import("@/views/error/404/index.vue"), - }, - // { - // path: '/:pathMatch(.*)', - // redirect: '/404' - // } -]; - -const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes: routes, -}); - -// 全局路由守卫(前置) -router.beforeEach((to, from, next) => { - // VNode.component?.exposed?.startLoading(); - document.title = `${to.meta.title} | ${TITLE}`; // 页面名 - const userInfo = JSON.parse(getLocal("userInfo")); - const role = getLocal("role"); - if (!userInfo && to.path !== "/login") { - next("/login"); - } else if (to.meta.roles) { - let roles: any = to.meta.roles; - // 如果是管理员权限则可进入,这里只是简单的模拟管理员权限而已 - roles.indexOf(role) > -1 ? next() : next("/403"); - } else { - next(); - } -}); - -// 全局路由守卫(后置) -router.afterEach((to, from) => { - // VNode.component?.exposed?.stopLoading(); -}); - -export default router; diff --git a/frontend/src/stores/data.ts b/frontend/src/stores/data.ts deleted file mode 100644 index bc9257689e11d2d89255da3c2368665ad0a4ba9e..0000000000000000000000000000000000000000 --- a/frontend/src/stores/data.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { defineStore } from "pinia"; -import type { CourseForm, DeptForm, MajorForm, StudentForm, TeacherForm } from "@/types/table"; - -export const useDataStore = defineStore({ - id: "table", - state: () => ({ - departmentData: [] as DeptForm[], // 院系 - majorData: [] as MajorForm[], // 专业 - teacherData: [] as TeacherForm[], // 教师 - studentData: [] as StudentForm[], // 学生 - courseData: [] as CourseForm[], // 课程 - }), - getters: {}, - actions: { - /** - * 存储数据 - * @param prefix 表名 - * @param data 数据 - */ - handleData(prefix: string, data: string[]) { - // console.log(prefix, data); - this[`${prefix}Data`] = data; - }, - }, -}); diff --git a/frontend/src/stores/index.ts b/frontend/src/stores/index.ts deleted file mode 100644 index 21bf8336542354be836d744c2a9e8f4c6fa8943c..0000000000000000000000000000000000000000 --- a/frontend/src/stores/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { defineStore } from "pinia"; -import type { UserInfo } from "@/types"; - -interface Tags { - name: string; - path: string; - title: string; -} - -export const useStore = defineStore({ - id: "index", - state: () => ({ - tagsList: [] as Tags[], // 标签列表 - collapse: false as boolean, // 侧边栏是否折叠 - userInfo: {} as UserInfo, // 用户信息 - messages: 4 as number, // 消息数量 - }), - getters: { - tagNameList(state) { - return state.tagsList.map((item: Tags) => { - return item.name; - }); - }, - }, - actions: { - /** - * 根据索引删除标签 - * @param index 索引值 - */ - delTagsItem(index: number) { - this.tagsList.splice(index, 1); - }, - - /** - * 添加路由对象 - * @param route 路由对象 - */ - setTagsItem(route: Tags) { - this.tagsList.push(route); - }, - - /** - * 关闭全部标签 - */ - clearTags() { - this.tagsList = []; - }, - - /** - * 关闭其他标签 - * @param data 标签数组 - */ - closeTagsOther(data: Tags[]) { - this.tagsList = data; - }, - }, -}); diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts deleted file mode 100644 index f260d3a4d84bf7716aa04eaf3e4ce750c8e7268c..0000000000000000000000000000000000000000 --- a/frontend/src/types/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 请求参数(前端发送的数据格式) - */ -export interface RequestData { - [key: string]: any; -} - -/** - * 响应数据(后端返回字段) - */ -export interface ResponseData { - code: number; // 状态码 - msg: string; // 成功/报错信息 - data: T; // 数据 -} - -/** - * 权限参数类型(限定可选值) - */ -export enum Roles { - admin = "admin", // 管理员权限 - teacher = "teacher", // 教师权限 - student = "student", // 学生权限 -} - -/** - * 缓存用户信息的字段 - */ -export interface UserInfo { - id: number | string; // 用户id - name: string; // 名称 - image: string; // 头像 - address: string; // 地址 - update_time: string; // 最后更新时间 -} diff --git a/frontend/src/types/table.ts b/frontend/src/types/table.ts deleted file mode 100644 index 6cffa0b7268dc974dd95863f0baaeb7b74fe15e8..0000000000000000000000000000000000000000 --- a/frontend/src/types/table.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * 路径参数类型(限定可选值) - */ -export enum PathEnum { - dept = "department", - major = "major", - teacher = "teacher", - student = "student", - course = "course", - taught = "taught", - elective = "elective", -} - -/** - * 查询参数类型 - */ -export interface Query { - id: string; // 请求id - currentPage: number; // 页码 - pageSize: number; // 每页个数 -} - -/** - * form表单时间数据类型(创建时间和更新时间) - */ -export interface TimeForm { - create_time?: string; // 创建时间 - update_time?: string; // 更新时间 -} - -/** - * 院系表数据类型(请使用 ?: , 后面继承接口用到) - */ -export interface DeptForm extends TimeForm { - id?: number | string; // 编号 - name?: string; // 名称 - chairman?: string; // 主任名 - phone?: string; // 手机号 -} - -/** - * 专业表数据类型 - */ -export interface MajorForm extends TimeForm { - id?: number | string; // 编号 - name?: string; // 名称 - assistant?: string; // 辅导员名 - phone?: string; // 手机号 - departmentId?: number | string; // 院系编号 -} - -/** - * 教师表数据类型 - */ -export interface TeacherForm extends TimeForm { - id?: number | string; // 编号 - name?: string; // 名称 - sex?: "0" | "1"; // 性别 - birthday?: string; // 生日 - education?: "1" | "2" | "3"; // 学历 - title?: "1" | "2" | "3" | "4"; // 职称 - address?: string; // 地址 - image?: string; //头像 - password?: string; // 密码 - departmentId?: number | string; // 院系编号 -} - -/** - * 学生表数据类型 - */ -export interface StudentForm extends TimeForm { - id?: number | string; // 编号 - name?: string; // 名称 - sex?: "0" | "1"; // 性别 - birthday?: string; // 生日 - address?: string; // 地址 - image?: string; //头像 - password?: string; // 密码 - majorId?: number | string; // 专业编号 -} - -/** - * 课程表数据类型 - */ -export interface CourseForm extends TimeForm { - id?: number | string; // 编号 - name?: string; // 名称 - credit?: number | string; // 学分 - period?: number | string; // 学时 -} - -/** - * 讲授表数据类型 - */ -export interface TaughtForm extends TimeForm { - id?: number | string; // 编号 - grade?: number | string; // 成绩 - teacherId?: number | string; // 教师编号 - courseId?: number | string; // 课程编号 -} - -/** - * 选课表数据类型 - */ -export interface ElectiveForm extends TimeForm { - id?: number | string; // 编号 - grade?: number | string; // 成绩 - studentId?: number | string; // 学生编号 - courseId?: number | string; // 课程编号 -} - -/** - * form表单数据类型 - */ -export interface FormData extends DeptForm, MajorForm, TeacherForm, StudentForm, CourseForm, TaughtForm, ElectiveForm {} diff --git a/frontend/src/utils/clickRecover.ts b/frontend/src/utils/clickRecover.ts deleted file mode 100644 index c14d67ffb5213368d5d4497d50535b672ad28d0a..0000000000000000000000000000000000000000 --- a/frontend/src/utils/clickRecover.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 点击后鼠标移开恢复按钮默认样式 - */ -export const clickRecover = (event: MouseEvent) => { - let target: any = event.target; - // (如果按钮没有加icon图标的话,target.nodeName == "I"可以去掉) - if (target.nodeName == "I" || target.nodeName == "SPAN") { - target = target.parentNode; - } - target.blur(); -}; diff --git a/frontend/src/utils/handleArray.ts b/frontend/src/utils/handleArray.ts deleted file mode 100644 index 9339c9a3b6453134afb8b1843dcb5024bef93c9a..0000000000000000000000000000000000000000 --- a/frontend/src/utils/handleArray.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { FormData } from "@/types/table"; - -/** - * 加工数组 - * @param data 数组 - * @param keyName 想要返回的 key 名 - * @returns value 列表 - */ -export const valueList = (data: any[], keyName: string): string[] => { - return data.map((item: any) => { - return item[keyName].toString(); - }); -}; - -/** - * 通过id得到name - * @param {*} id 外键 - * @param {*} data 外键对应的表数据 - * @returns 对应的name - */ -export const byIdGetName = (id: string, data: FormData[]) => { - if (!id) { - return null; - } - for (let i = 0, len = data.length; i < len; i++) { - let item = data[i]; - if (item.id == id) { - return item.name; - } - } - return null; -}; diff --git a/frontend/src/utils/handleInject.ts b/frontend/src/utils/handleInject.ts deleted file mode 100644 index 436857eacca183e2a224be4054d5ebae955ab6c4..0000000000000000000000000000000000000000 --- a/frontend/src/utils/handleInject.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { inject, type InjectionKey } from "vue"; - -/** - * 为inject标注类型 - * @param key key值 - * @param fallback 失败返回信息 - * @returns inject(key) - */ -function injectStrict(key: InjectionKey, fallback?: T) { - const resolved = inject(key, fallback); - if (!resolved) { - throw new Error(`Could not resolve ${key.toString}`); - } - return resolved; -} - -export default injectStrict; diff --git a/frontend/src/utils/handleTime.ts b/frontend/src/utils/handleTime.ts deleted file mode 100644 index 9a7a6df1424eda90a163c437d57355a21db9ad6e..0000000000000000000000000000000000000000 --- a/frontend/src/utils/handleTime.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const dateFunction = (time: any) => { - var zoneDate = new Date(time).toJSON(); - var date = new Date(+new Date(zoneDate) + 8 * 3600 * 1000) - .toISOString() - .replace(/T/g, " ") - .replace(/\.[\d]{3}Z/, ""); - return date; -}; diff --git a/frontend/src/utils/spanMethod.ts b/frontend/src/utils/spanMethod.ts deleted file mode 100644 index dbc713a6407fc750a461321ad7c2d4979a91b2f4..0000000000000000000000000000000000000000 --- a/frontend/src/utils/spanMethod.ts +++ /dev/null @@ -1,66 +0,0 @@ -// http://www.manongjc.com/detail/23-npfbicezsphbgyn.html -// https://blog.csdn.net/u012175183/article/details/123205887 - -/** - * 合并相同数据,导出合并行所需的方法(只适合el-table) - * @param {Array} dataArray el-table表数据源 - * @param {Array} mergeRowProp 合并行的列prop - * @param {Array} sameRuleRowProp 相同合并规则行的列prop - */ -export function getSpanMethod(dataArray, mergeRowProp, sameRuleRowProp) { - /** - * 要合并行的数据 - */ - const rowspanNumObject = {}; - - //初始化 rowspanNumObject - mergeRowProp.map((item) => { - rowspanNumObject[item] = new Array(dataArray.length).fill(1, 0, 1).fill(0, 1); - rowspanNumObject[`${item}-index`] = 0; - }); - - //计算相关的合并信息 - for (let i = 1; i < dataArray.length; i++) { - mergeRowProp.map((key) => { - const index = rowspanNumObject[`${key}-index`]; - if (dataArray[i][key] === dataArray[i - 1][key]) { - rowspanNumObject[key][index]++; - } else { - rowspanNumObject[`${key}-index`] = i; - rowspanNumObject[key][i] = 1; - } - }); - } - - /** - * 添加同规则合并行的数据 - */ - if (sameRuleRowProp !== undefined) { - let k = Object.keys(rowspanNumObject).filter((key) => { - if (!key.includes("index")) { - return key; - } - })[0]; - for (let prop of sameRuleRowProp) { - rowspanNumObject[prop] = rowspanNumObject[k]; - rowspanNumObject[`${prop}-index`] = rowspanNumObject[`${k}-index`]; - mergeRowProp.push(prop); - } - } - - /** - * 导出合并方法 - */ - const spanMethod = function ({ row, column, rowIndex, columnIndex }) { - if (mergeRowProp.includes(column["property"])) { - const rowspan = rowspanNumObject[column["property"]][rowIndex]; - if (rowspan > 0) { - return { rowspan: rowspan, colspan: 1 }; - } - return { rowspan: 0, colspan: 0 }; - } - return { rowspan: 1, colspan: 1 }; - }; - - return spanMethod; -} diff --git a/frontend/src/views/dashboard/index.ts b/frontend/src/views/dashboard/index.ts deleted file mode 100644 index af7f0254fd1b98292bdc4d10104f509bc9aaf3db..0000000000000000000000000000000000000000 --- a/frontend/src/views/dashboard/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface Language { - title: string; - percentage: number; - color: string; -} - -export interface Todo { - title: string; - status: boolean; -} - -export interface State { - identity: string; - langDetails: Language[]; - todoList: Todo[]; - visitNum: number; - todoNum: number; - requestNum: number; - showDialog: boolean; - todoText: string; -} - -export enum RolesEnum { - "admin" = "管理员", - "teacher" = "教师", - "student" = "学生", -} diff --git a/frontend/src/views/dashboard/index.vue b/frontend/src/views/dashboard/index.vue deleted file mode 100644 index 474ff554909f71f38a5c0262284b93d0dc729057..0000000000000000000000000000000000000000 --- a/frontend/src/views/dashboard/index.vue +++ /dev/null @@ -1,430 +0,0 @@ - - - - - diff --git a/frontend/src/views/error/403/index.vue b/frontend/src/views/error/403/index.vue deleted file mode 100644 index 54bea1fe992896f908e2ed5de8defd8ffaeb642e..0000000000000000000000000000000000000000 --- a/frontend/src/views/error/403/index.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - - - diff --git a/frontend/src/views/error/404/index.vue b/frontend/src/views/error/404/index.vue deleted file mode 100644 index b8c5021f4be031b14aac71315b7e96139afaf215..0000000000000000000000000000000000000000 --- a/frontend/src/views/error/404/index.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - - - diff --git a/frontend/src/views/login/index.ts b/frontend/src/views/login/index.ts deleted file mode 100644 index dc12d325200c1e378a58be244cd32b726ba2288c..0000000000000000000000000000000000000000 --- a/frontend/src/views/login/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface UserNamePwd { - username: string; - password: string; -} diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue deleted file mode 100644 index b6b31ba632f130a898203d25122a8728e7a6c25c..0000000000000000000000000000000000000000 --- a/frontend/src/views/login/index.vue +++ /dev/null @@ -1,158 +0,0 @@ - - - - - diff --git a/frontend/src/views/messages/index.ts b/frontend/src/views/messages/index.ts deleted file mode 100644 index 3c6c48ced52f83d8746b6cfcf20a12bc76eed9b1..0000000000000000000000000000000000000000 --- a/frontend/src/views/messages/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface Message { - date: string; - title: string; -} - -export interface State { - [key: string]: Message[]; -} diff --git a/frontend/src/views/messages/index.vue b/frontend/src/views/messages/index.vue deleted file mode 100644 index ee7f702c9f9bbe4de5865b4ee330c27ac0727029..0000000000000000000000000000000000000000 --- a/frontend/src/views/messages/index.vue +++ /dev/null @@ -1,166 +0,0 @@ - - - - - diff --git a/frontend/src/views/settings/course/index.ts b/frontend/src/views/settings/course/index.ts deleted file mode 100644 index 3eac16679a1c8cb97ee5110ce5db128ce9c51a61..0000000000000000000000000000000000000000 --- a/frontend/src/views/settings/course/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { CourseForm } from "@/types/table"; - -export interface State { - courseData: CourseForm[]; - pageTotal: number; - isDisabled: boolean; -} diff --git a/frontend/src/views/settings/course/index.vue b/frontend/src/views/settings/course/index.vue deleted file mode 100644 index 1a98c29063bc3b1ce439e0583052d115a47951d7..0000000000000000000000000000000000000000 --- a/frontend/src/views/settings/course/index.vue +++ /dev/null @@ -1,144 +0,0 @@ - - -