original_project
146
README.md
Normal file
@ -0,0 +1,146 @@
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">django-ruoyi-admin</h1>
|
||||
<h4 align="center">基于django+ 若依(Vue3版本)快速开发框架(v1.0.1)</h4>
|
||||
|
||||
<p align="center">
|
||||
👉 <a target="_blank" href="http://124.71.212.219:8028/">个人网站:http://124.71.212.219:8028/</a> 👈
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/mengyinggitee/sanic-vue-admin/stargazers"><img src="https://gitee.com/mengyinggitee/sanic-vue-admin/badge/star.svg"></a>
|
||||
<a href="https://gitee.com/mengyinggitee/sanic-vue-admin/members"><img src="https://gitee.com/mengyinggitee/sanic-vue-admin/badge/fork.svg"></a>
|
||||
<a><img src="https://img.shields.io/badge/QQ-652044581-green"></a>
|
||||
<a><img src=https://img.shields.io/badge/%E5%BE%AE%E4%BF%A1-17783098377-brightgreen"></a>
|
||||
<a><img src=https://img.shields.io/badge/python-3.70-black"></a>
|
||||
</p>
|
||||
|
||||
### 平台介绍
|
||||
|
||||
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。[若依框架地址]([Vue3](https://v3.cn.vuejs.org)),
|
||||
但目前主流的若以框架是基于SpringBoot的java开发语言,企业开发前端常用的是若依框架,而后端没有一个属于python版本的后端,因此萌生了调试一套适合python程序员的快速开发框架,删除广告页面和日常开发者不必要的功能,只保留项目的最核心的配置模块,减少初始化的方式,做到入手既可以开发功能
|
||||
|
||||
python主流框架请查看不同的分支(django+mysql,flask+mongo,sanic-mongo)
|
||||
|
||||
|
||||
### 技术栈介绍
|
||||
|
||||
* 前端技术栈: [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev)
|
||||
若依框架版本。
|
||||
* 后端技术栈: [django](https://www.djangoproject.com/) + [mysql](https://www.mysql.com/) + [redis](https://redis.io/) 实现。
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/20240418233140.png"/></td>
|
||||
</tr>
|
||||
<table>
|
||||
|
||||
### 框架能力
|
||||
* 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
* 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
|
||||
* 岗位管理:配置系统用户所属担任职务。
|
||||
* 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||
|
||||
### 代码优化
|
||||
|
||||
* 后端:收集日常的常用方法,增加常用的企业微信机器人通知,数据加解密等常用方法
|
||||
* 后端:数据库配置增加nacos和本地config的配置的2种配置,多种配置方式
|
||||
* 后端:配置数据库后,项目启动直接初始化相关数据,不需要导入sql的方式,方便快捷开发进度
|
||||
* 前端:删除不必要的广告页面,和不必要的功能,比如日志,人员职位,登陆等相关信息,只保留用户,角色,目录,部门核心的模块功能
|
||||
* 前端:收集封装日常中大佬们封装的一些方法和看到的一些好的方法,实现工具箱似的开发,开箱即用
|
||||
* 前端:优化若依框架的部分ui和提取字段,如title,base_path等变为env配置字段和调试时候的vite代理等均有展示
|
||||
|
||||
### 前端部署
|
||||
|
||||
```
|
||||
# 本地部署
|
||||
# 安装依赖(设置镜像源)
|
||||
yarn --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动服务
|
||||
yarn run dev
|
||||
|
||||
# 打包构建
|
||||
yarn build:prod
|
||||
|
||||
# 前端访问地址
|
||||
http://localhost:80
|
||||
|
||||
# api地址修改:
|
||||
vite.config.js文件 server.proxy.target指向后端地址
|
||||
|
||||
=======================================================================
|
||||
# 线上docker部署
|
||||
# 前提确认宿主机含有node环境,(调试环境) node_version = v21.1.0,
|
||||
# 安装了yarn 和vite环境
|
||||
# npm install -g yarn vite
|
||||
|
||||
# 安装依赖
|
||||
yarn --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动服务
|
||||
yarn run dev
|
||||
|
||||
# 打包构建
|
||||
yarn build:prod
|
||||
|
||||
# 前端访问地址
|
||||
http://localhost:80
|
||||
|
||||
# api地址修改:
|
||||
.env.production文件 VITE_APP_BASE_API 属性
|
||||
|
||||
# 构建命令
|
||||
sh build.sh
|
||||
```
|
||||
|
||||
### 后端部署
|
||||
|
||||
```
|
||||
本地部署
|
||||
# 进入目录
|
||||
cd admin-api
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 配置数据库
|
||||
/config.py 或者 配置nacos地址 (配置mongo和redis的数据)
|
||||
|
||||
# 启动服务
|
||||
python mange.py runserver 0.0.0.0:8000
|
||||
|
||||
=======================================================================
|
||||
# docker部署(docker-compose)
|
||||
docker-compose build && docker-compose up -d
|
||||
|
||||
# 或者执行脚本构建(gunicorn部署,已内置调试)
|
||||
sh build.sh
|
||||
|
||||
内置一个超级管理员账号
|
||||
superAdmin/superAdmin
|
||||
```
|
||||
|
||||
### 在线体验
|
||||
|
||||
演示地址:[http://124.71.212.219:8101/#/login](http://124.71.212.219:8101/#/login)
|
||||
|
||||
账号密码:superAdmin/superAdmin
|
||||
|
||||
如果能帮助到您,快速的构建您的项目,麻烦帮我star一下吧,谢谢咯,如果有问题的请加我微信
|
||||
|
||||
## 演示图
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/gitshow1.jpg"/></td>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/gitshow2.jpg"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/gitshow3.jpg"/></td>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/gitshow4.jpg"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/gitshow5.jpg"/></td>
|
||||
<td style="width:400px"><img src="./admin-ui/src/assets/images/gitshow6.jpg"/></td>
|
||||
</tr>
|
||||
|
||||
<table>
|
||||
8
admin-api/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.idea
|
||||
/chrome
|
||||
/logs
|
||||
/meta
|
||||
/venv
|
||||
/static
|
||||
*.pyc
|
||||
/excel
|
||||
27
admin-api/Dockerfile
Normal file
@ -0,0 +1,27 @@
|
||||
FROM python:3.7
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
EXPOSE 8085
|
||||
|
||||
# 安装依赖
|
||||
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
|
||||
RUN python -m pip install --upgrade pip
|
||||
RUN python -m pip install gunicorn
|
||||
RUN python -m pip install greenlet
|
||||
RUN python -m pip install eventlet
|
||||
RUN python -m pip install gevent
|
||||
RUN python -m pip install -r requirements.txt
|
||||
|
||||
# 创建数据
|
||||
RUN python createDatabase.py
|
||||
RUN python manage.py makemigrations
|
||||
RUN python manage.py migrate
|
||||
RUN python initDatabase.py
|
||||
|
||||
# 修改时区
|
||||
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
|
||||
# gunicorn部署 需要部署nginx 存在跨域问题, 配置文件
|
||||
CMD ["gunicorn" ,"application.wsgi", "-c", "gunicorn.conf.py"]
|
||||
|
||||
# CMD ["python" ,"manage.py", "runserver", "0.0.0.0:8085"]
|
||||
2
admin-api/application/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
import pymysql
|
||||
pymysql.install_as_MySQLdb()
|
||||
16
admin-api/application/asgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for application project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
110
admin-api/application/settings.py
Normal file
@ -0,0 +1,110 @@
|
||||
"""
|
||||
Django settings for application project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.2.25.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from config import config
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-68!wtv#sj9)8*eu7gjr(*9m6v3veg#de!e2(51t)04m$%7abhj'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = config.DEBUG
|
||||
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages',
|
||||
'django.contrib.staticfiles', "django_apscheduler", 'corsheaders', "web.apps.WebConfig"
|
||||
"web", ]
|
||||
|
||||
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middleware.myMiddleware.AuthMiddleware', 'middleware.myMiddleware.LogMiddleware', ]
|
||||
|
||||
ROOT_URLCONF = 'application.urls'
|
||||
|
||||
TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': {
|
||||
'context_processors': ['django.template.context_processors.debug', 'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
|
||||
|
||||
WSGI_APPLICATION = 'application.wsgi.application'
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {'default': {'ENGINE': config.MYSQL_ENGINE, 'HOST': config.MYSQL_HOST, # 数据库ip
|
||||
'PORT': config.MYSQL_PORT, # 数据库端口
|
||||
'USER': config.MYSQL_USER, # 用户名
|
||||
'PASSWORD': config.MYSQL_PASSWORD, # 密码
|
||||
'NAME': config.MYSQL_DB, # 数据库名
|
||||
}}
|
||||
|
||||
CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache", "LOCATION": f'redis://:{config.REDIS_PASSWORD or ""}@{config.REDIS_HOST}:6379',
|
||||
"DECODE_RESPONSES": True, "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
"CONNECTION_POOL_KWARGS": {"max_connections": 100, 'decode_responses': True},
|
||||
"PASSWORD": config.REDIS_PASSWORD, }}}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', },
|
||||
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', },
|
||||
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', },
|
||||
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# 允许所有域名跨域(优先选择)
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
|
||||
# 跨域白名单请求方式
|
||||
CORS_ALLOW_METHODS = ('DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW',)
|
||||
|
||||
# 跨域白名单请求头
|
||||
CORS_ALL_HEADERS = ('XMLHttpRequest', 'accept-encoding', 'authorization', 'content-type', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with')
|
||||
|
||||
# 关闭浏览器退出登录
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
|
||||
# 解决浏览器不能设置cookie问题
|
||||
SESSION_COOKIE_SAMESITE = None
|
||||
X_FRAME_OPTIONS = 'ALLOWALL'
|
||||
21
admin-api/application/urls.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""application URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include('web.urls')),
|
||||
path('api/system/', include('web.system.urls')),
|
||||
]
|
||||
15
admin-api/application/wsgi.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""
|
||||
WSGI config for application project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
3
admin-api/build.sh
Normal file
@ -0,0 +1,3 @@
|
||||
git pull
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
68
admin-api/config.py
Normal file
@ -0,0 +1,68 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author:mengying
|
||||
@file: config.py
|
||||
@date:2023/12/18 9:35
|
||||
@email: 652044581@qq.com
|
||||
@desc:
|
||||
"""
|
||||
import nacos
|
||||
from addict import Dict
|
||||
import json
|
||||
|
||||
|
||||
class Localconfig:
|
||||
"""本地配置(二选一)"""
|
||||
|
||||
# 项目的名称
|
||||
PROJECT_NAME = "django-ruoyi-admin"
|
||||
|
||||
# 配置redis缓存地址
|
||||
REDIS_HOST = '124.71.212.219'
|
||||
REDIS_PORT = 6379
|
||||
REDIS_DB = 0
|
||||
REDIS_PASSWORD = "Zds2Xacb0kbiSGcs"
|
||||
|
||||
# 配置mongo数据库
|
||||
MYSQL_DB = "django_ruoyi_admin" # 注: 数据库名不能用-特殊字符
|
||||
MYSQL_HOST = "124.71.212.219"
|
||||
MYSQL_PORT = 3306
|
||||
MYSQL_USER = "root"
|
||||
MYSQL_PASSWORD = "tpZCI6IiJ9"
|
||||
MYSQL_ENGINE = 'django.db.backends.mysql'
|
||||
|
||||
# 加密随机串(hash-md5)
|
||||
ENCRYPT_STRING = "c-QULHn+u=-BUSQ$"
|
||||
|
||||
DEBUG = False
|
||||
|
||||
@classmethod
|
||||
def get_server_config(cls):
|
||||
return {item: getattr(cls, item) for item in dir(cls)}
|
||||
|
||||
|
||||
class NacosClient:
|
||||
"""nacos配置(二选一)"""
|
||||
|
||||
def __init__(self, addr: str = None, namespace: str = None, data_id: str = None, group_id: str = None,
|
||||
username: str = "nacos", password: str = "nacos"):
|
||||
self.addr = addr or "120.46.187.114:8848"
|
||||
self.namespace = namespace or "7d35aedc-ec57-48c8-8489-9974d19a2942"
|
||||
self.data_id = data_id or "sigin"
|
||||
self.group_id = group_id or "dev"
|
||||
self.client = nacos.NacosClient(self.addr, namespace=self.namespace, username=username, password=password)
|
||||
|
||||
def get_server_config(self):
|
||||
print(self.client.get_config(self.data_id, self.group_id))
|
||||
return json.loads(self.client.get_config(self.data_id, self.group_id))
|
||||
|
||||
|
||||
# nacos的配置
|
||||
# config = Dict(NacosClient().get_server_config())
|
||||
|
||||
# 本地的配置
|
||||
config = Dict(Localconfig.get_server_config())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(config.MYSQL_DB)
|
||||
32
admin-api/createDatabase.py
Normal file
@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author :mengying
|
||||
@Date :2024/5/31 17:31
|
||||
@Email : 652044581@qq.com
|
||||
@Desc : 创建数据库
|
||||
"""
|
||||
import pymysql
|
||||
|
||||
from config import config
|
||||
|
||||
|
||||
def create_database():
|
||||
"""项目启动自动创建数据表"""
|
||||
connection = pymysql.connect(host=config.MYSQL_HOST, port=config.MYSQL_PORT, user=config.MYSQL_USER, passwd=config.MYSQL_PASSWORD)
|
||||
create_database_sql = f"CREATE DATABASE IF NOT EXISTS {config.MYSQL_DB} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
try:
|
||||
# 创建游标对象
|
||||
with connection.cursor() as cursor:
|
||||
# 执行SQL语句
|
||||
cursor.execute(create_database_sql)
|
||||
# 提交事务
|
||||
connection.commit()
|
||||
except pymysql.MySQLError as e:
|
||||
print(f"Error: {e}")
|
||||
finally:
|
||||
# 关闭数据库连接
|
||||
connection.close()
|
||||
|
||||
|
||||
# 初始化创建数据库
|
||||
create_database()
|
||||
20
admin-api/crontab/myScheduler.py
Normal file
@ -0,0 +1,20 @@
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from django_apscheduler.jobstores import DjangoJobStore, register_events, register_job
|
||||
import uuid
|
||||
|
||||
import datetime
|
||||
|
||||
scheduler = BackgroundScheduler()
|
||||
scheduler.add_jobstore(DjangoJobStore(), "default")
|
||||
|
||||
run_date = datetime.datetime.now() + datetime.timedelta(seconds=20)
|
||||
|
||||
|
||||
@register_job(scheduler, "date", id=uuid.uuid4().hex, run_date=run_date, replace_existing=True,
|
||||
timezone='Asia/Shanghai')
|
||||
def setUp_database_scheduler():
|
||||
print("Setting up database scheduler")
|
||||
|
||||
|
||||
register_events(scheduler)
|
||||
scheduler.start()
|
||||
25
admin-api/database/core.py
Normal file
@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author :mengying
|
||||
@Date :2024/5/30 15:39
|
||||
@Email : 652044581@qq.com
|
||||
@Desc : 功能描述
|
||||
"""
|
||||
from django.db import models
|
||||
|
||||
|
||||
class CoreModel(models.Model):
|
||||
"""
|
||||
核心标准抽象模型
|
||||
"""
|
||||
id = models.BigAutoField(primary_key=True, help_text="Id", verbose_name="Id")
|
||||
update_time = models.DateTimeField(auto_now=True, null=True, blank=True, help_text="修改时间", verbose_name="修改时间")
|
||||
create_time = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间", verbose_name="创建时间")
|
||||
remark = models.CharField(max_length=64, blank=True, verbose_name="备注信息", help_text="备注信息")
|
||||
createBy = models.CharField(max_length=64, blank=True, verbose_name="创建者", help_text="创建者")
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
verbose_name = '核心模型'
|
||||
verbose_name_plural = verbose_name
|
||||
21
admin-api/docker-compose.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
version: '3.0'
|
||||
|
||||
services:
|
||||
|
||||
django-vue-api-std:
|
||||
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
||||
image: django-vue-api-image-std
|
||||
|
||||
container_name: django-vue-api-container-std
|
||||
|
||||
volumes:
|
||||
- /opt/django-vue-admin/logs:/app/logs
|
||||
|
||||
ports:
|
||||
- "8090:8085"
|
||||
|
||||
restart: always
|
||||
42
admin-api/gunicorn.conf.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 多进程
|
||||
|
||||
"""gunicorn+gevent 的配置文件"""
|
||||
|
||||
timeout = 60 * 2
|
||||
|
||||
# 预加载资源
|
||||
# preload_app = True
|
||||
# 绑定 ip + 端口
|
||||
bind = "0.0.0.0:8085"
|
||||
# 进程数 = cup数量 * 2 + 1
|
||||
workers = 4 # multiprocessing.cpu_count() * 2 + 1
|
||||
|
||||
# 线程数 = cup数量 * 2
|
||||
threads = 20
|
||||
|
||||
max_requests = 1000
|
||||
|
||||
# 等待队列最大长度,超过这个长度的链接将被拒绝连接
|
||||
backlog = 2048
|
||||
|
||||
# 工作模式--协程
|
||||
worker_class = "gevent"
|
||||
|
||||
# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响
|
||||
# 服务器配置设置的值 1200:中小型项目 上万并发: 中大型
|
||||
# 服务器硬件:宽带+数据库+内存
|
||||
# 服务器的架构:集群 主从
|
||||
worker_connections = 1200
|
||||
|
||||
# 进程名称
|
||||
proc_name = 'w2p.pid'
|
||||
# 进程pid记录文件
|
||||
pidfile = 'app_run.log'
|
||||
# 日志等级
|
||||
loglevel = 'debug'
|
||||
# 日志文件名
|
||||
logfile = 'debug.log'
|
||||
# 访问记录
|
||||
accesslog = 'access.log'
|
||||
# 访问记录格式
|
||||
access_log_format = '%(h)s %(t)s %(U)s %(q)s'
|
||||
386
admin-api/initDatabase.py
Normal file
@ -0,0 +1,386 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author :mengying
|
||||
@Date :2024/5/31 17:17
|
||||
@Email : 652044581@qq.com
|
||||
@Desc : 初始化数据
|
||||
"""
|
||||
import os, django
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
||||
django.setup()
|
||||
|
||||
import logging
|
||||
from utils.mySnowflake import Sf
|
||||
from web.models import SystemInit
|
||||
from web.models import SystemDept, SystemRole, SystemUser, SystemMenu, SystemUserRole
|
||||
from django_redis import get_redis_connection
|
||||
from utils.myEnum import SystemUserTypeEnum
|
||||
from config import config
|
||||
from utils.myEncrypt import HashCipher
|
||||
|
||||
from redis import Redis
|
||||
|
||||
RedisClient: Redis = get_redis_connection()
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
||||
class DatabaseUtils(object):
|
||||
REDIS_KEY = 'init_database'
|
||||
|
||||
@classmethod
|
||||
def init_database(cls):
|
||||
"""初始化数据库"""
|
||||
|
||||
# 多节点初始化
|
||||
if not RedisClient.setnx(cls.REDIS_KEY, cls.REDIS_KEY):
|
||||
return
|
||||
|
||||
RedisClient.expire(cls.REDIS_KEY, 10)
|
||||
hasInit: bool = cls.initFlag()
|
||||
deptId = Sf.generate()
|
||||
roleId = Sf.generate()
|
||||
userId = Sf.generate()
|
||||
|
||||
if not hasInit:
|
||||
cls.initDept(deptId)
|
||||
cls.initRole(roleId)
|
||||
cls.initUser(userId)
|
||||
cls.initUserRole(userId, roleId)
|
||||
cls.initMenu()
|
||||
|
||||
RedisClient.delete(cls.REDIS_KEY)
|
||||
logger.info("init database successfully")
|
||||
|
||||
@classmethod
|
||||
def initDept(cls, deptId):
|
||||
initDeptData = {
|
||||
"createBy": "superAdmin",
|
||||
"deptId": deptId,
|
||||
"ancestors": "0",
|
||||
"parentId": "0",
|
||||
"deptName": "宇驰商贸有限公司",
|
||||
"orderNum": 0,
|
||||
"leader": "admin",
|
||||
"phone": "17783098377",
|
||||
"email": "652044581@qq.com",
|
||||
"status": "0",
|
||||
"delFlag": "0",
|
||||
}
|
||||
SystemDept(**initDeptData).save()
|
||||
|
||||
@classmethod
|
||||
def initRole(cls, roleId):
|
||||
"""初始化角色超级管理员"""
|
||||
initRoleData = {
|
||||
"createBy": "superAdmin",
|
||||
"roleId": roleId,
|
||||
"roleName": "超级管理员",
|
||||
"roleAdmin": True,
|
||||
"roleKey": "superAdmin",
|
||||
"roleSort": "0",
|
||||
"status": "0",
|
||||
"delFlag": "0",
|
||||
}
|
||||
SystemRole(**initRoleData).save()
|
||||
|
||||
@classmethod
|
||||
def initUser(cls, userId):
|
||||
initUserData = {"userId": userId,
|
||||
"username": "superAdmin",
|
||||
"nickName": "superAdmin",
|
||||
"password": HashCipher.md5(config.ENCRYPT_STRING + "superAdmin"),
|
||||
"phone": "17783098375",
|
||||
"email": "652044581@qq.com",
|
||||
"status": "0",
|
||||
"userType": SystemUserTypeEnum.p1.value
|
||||
}
|
||||
SystemUser(**initUserData).save()
|
||||
|
||||
@classmethod
|
||||
def initUserRole(cls, userId, roleId):
|
||||
"""初始化用户角色关系"""
|
||||
initUserRoleData = {"userId": userId, "roleId": roleId}
|
||||
SystemUserRole(**initUserRoleData).save()
|
||||
|
||||
@classmethod
|
||||
def initMenu(cls, ):
|
||||
"""初始化前端菜单"""
|
||||
menuParentId = Sf.generate()
|
||||
menuUserId = Sf.generate()
|
||||
menuRoleId = Sf.generate()
|
||||
menuMenuId = Sf.generate()
|
||||
menuDeptId = Sf.generate()
|
||||
initMenuData = [
|
||||
{
|
||||
"menuId": menuParentId,
|
||||
"menuName": "系统管理",
|
||||
"parentId": "0",
|
||||
"orderNum": 3,
|
||||
"path": "system",
|
||||
"component": "",
|
||||
"perms": "",
|
||||
"query": "",
|
||||
"menuType": "M",
|
||||
"icon": "system",
|
||||
},
|
||||
{
|
||||
"menuId": menuUserId,
|
||||
"menuName": "用户管理",
|
||||
"parentId": menuParentId,
|
||||
"orderNum": 1,
|
||||
"path": "user",
|
||||
"component": "system/user/index",
|
||||
"perms": "system:user:list",
|
||||
"query": "",
|
||||
"menuType": "C",
|
||||
"icon": "user",
|
||||
},
|
||||
|
||||
{
|
||||
"menuId": menuRoleId,
|
||||
"menuName": "角色管理",
|
||||
"parentId": menuParentId,
|
||||
"orderNum": 2,
|
||||
"path": "role",
|
||||
"component": "system/role/index",
|
||||
"perms": "system:role:list",
|
||||
"query": "",
|
||||
"menuType": "C",
|
||||
"icon": "peoples",
|
||||
},
|
||||
{
|
||||
"menuId": menuMenuId,
|
||||
"menuName": "菜单管理",
|
||||
"parentId": menuParentId,
|
||||
"orderNum": 3,
|
||||
"path": "menu",
|
||||
"component": "system/menu/index",
|
||||
"perms": "system:menu:list",
|
||||
"query": "",
|
||||
"menuType": "C",
|
||||
"icon": "tree-table",
|
||||
},
|
||||
{
|
||||
"menuId": menuDeptId,
|
||||
"menuName": "部门管理",
|
||||
"parentId": menuParentId,
|
||||
"orderNum": 4,
|
||||
"path": "dept",
|
||||
"component": "system/dept/index",
|
||||
"perms": "system:dept:list",
|
||||
"query": "",
|
||||
"menuType": "C",
|
||||
"icon": "tree",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "用户查询",
|
||||
"parentId": menuUserId,
|
||||
"orderNum": 1,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:user:query",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "用户新增",
|
||||
"parentId": menuUserId,
|
||||
"orderNum": 2,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:user:add",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "用户修改",
|
||||
"parentId": menuUserId,
|
||||
"orderNum": 3,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:user:edit",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "用户删除",
|
||||
"parentId": menuUserId,
|
||||
"orderNum": 4,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:user:remove",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "角色查询",
|
||||
"parentId": menuRoleId,
|
||||
"orderNum": 1,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:role:query",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "角色新增",
|
||||
"parentId": menuRoleId,
|
||||
"orderNum": 2,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:role:add",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "角色修改",
|
||||
"parentId": menuRoleId,
|
||||
"orderNum": 3,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:role:edit",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "角色删除",
|
||||
"parentId": menuRoleId,
|
||||
"orderNum": 4,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:role:remove",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "菜单查询",
|
||||
"parentId": menuMenuId,
|
||||
"orderNum": 1,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:menu:query",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "菜单新增",
|
||||
"parentId": menuMenuId,
|
||||
"orderNum": 2,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:menu:add",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "菜单修改",
|
||||
"parentId": menuMenuId,
|
||||
"orderNum": 3,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:menu:edit",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "菜单删除",
|
||||
"parentId": menuMenuId,
|
||||
"orderNum": 4,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:menu:remove",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "部门查询",
|
||||
"parentId": menuDeptId,
|
||||
"orderNum": 1,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:dept:query",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "部门新增",
|
||||
"parentId": menuDeptId,
|
||||
"orderNum": 2,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:dept:add",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "部门修改",
|
||||
"parentId": menuDeptId,
|
||||
"orderNum": 3,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:dept:edit",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
{
|
||||
"menuId": Sf.generate(),
|
||||
"menuName": "部门删除",
|
||||
"parentId": menuDeptId,
|
||||
"orderNum": 4,
|
||||
"path": "",
|
||||
"component": "",
|
||||
"perms": "system:dept:remove",
|
||||
"query": "",
|
||||
"menuType": "F",
|
||||
"icon": "#",
|
||||
},
|
||||
]
|
||||
for item in initMenuData:
|
||||
SystemMenu(**item).save()
|
||||
|
||||
@classmethod
|
||||
def initFlag(cls):
|
||||
"""防止多次初始化文件"""
|
||||
init = SystemInit.objects.all()
|
||||
if not init:
|
||||
SystemInit(Init=True).save()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
DatabaseUtils.init_database()
|
||||
20
admin-api/manage.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError("Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?") from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
50
admin-api/middleware/myAuthorization.py
Normal file
@ -0,0 +1,50 @@
|
||||
# -*- coding: <encoding name> -*-
|
||||
"""
|
||||
@Author:mengying
|
||||
@file: myAuthorization.py
|
||||
@date:2023/6/14 16:46
|
||||
@email: 652044581@qq.com
|
||||
@desc: 授权相关
|
||||
"""
|
||||
import json
|
||||
|
||||
from django_redis import get_redis_connection
|
||||
|
||||
|
||||
class CacheKeys:
|
||||
TOKEN_NAME = "Authorization"
|
||||
|
||||
|
||||
class Authorization:
|
||||
|
||||
@staticmethod
|
||||
def get_user_info(request):
|
||||
"""从header中获取访问人员信息放在header中"""
|
||||
RedisClient = get_redis_connection()
|
||||
|
||||
token = request.headers.get(CacheKeys.TOKEN_NAME, '')
|
||||
|
||||
if not token:
|
||||
setattr(request.headers, "user_info", {})
|
||||
return None
|
||||
|
||||
user = RedisClient.get(token)
|
||||
RedisClient.expire(token, 30 * 60)
|
||||
|
||||
if user:
|
||||
user = json.loads(user)
|
||||
user["token"] = token
|
||||
else:
|
||||
user = {}
|
||||
|
||||
setattr(request.headers, "user_info", user)
|
||||
|
||||
@staticmethod
|
||||
def white_list_check(request):
|
||||
"""判断访问名路径是否是白名单"""
|
||||
|
||||
white_list = ['/api/login', '/api/captchaImage', '/api/logout']
|
||||
if request.path in white_list:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
80
admin-api/middleware/myMiddleware.py
Normal file
@ -0,0 +1,80 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author :mengying
|
||||
@Date :2024/5/30 14:16
|
||||
@Email : 652044581@qq.com
|
||||
@Desc : 功能描述
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from django.http.response import JsonResponse
|
||||
|
||||
from middleware.myAuthorization import Authorization
|
||||
from utils.myResFormat import ResultJson, ResultCode
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
|
||||
class AuthMiddleware(MiddlewareMixin):
|
||||
"""用户认证中间件"""
|
||||
|
||||
def process_request(self, request):
|
||||
# 获取用户信息
|
||||
Authorization.get_user_info(request)
|
||||
|
||||
# 检查白名单
|
||||
if not Authorization.white_list_check(request):
|
||||
user_info = getattr(request.headers, 'user_info', {})
|
||||
if not user_info:
|
||||
return JsonResponse(ResultJson(ResultCode.TOKEN_ERROR).result)
|
||||
|
||||
def process_response(self, request, response): # 基于请求响应
|
||||
return response
|
||||
|
||||
def process_exception(self, request, exception):
|
||||
|
||||
formatter = ["接口地址 : %s" % request.get_full_path(), "请求方式 : %s" % request.method, "请求参数 : body: %s" % request.body.decode(),
|
||||
"用户信息 : user: %s" % str(getattr(request.headers, 'user_info', {})), "报错信息 : %s" % str(traceback.format_exc())]
|
||||
error_message = "\n".join(formatter)
|
||||
logger.error(error_message)
|
||||
return JsonResponse(ResultJson(ResultCode.SERVER_ERROR, description=str(exception)).result)
|
||||
|
||||
|
||||
class LogMiddleware(MiddlewareMixin):
|
||||
"""记录日志中间件"""
|
||||
|
||||
def process_request(self, request):
|
||||
uid = uuid.uuid4().hex
|
||||
setattr(request.headers, "my-duration", time.time())
|
||||
setattr(request.headers, "uid", uid)
|
||||
self.recordsRequest(uid, request.get_full_path(), request.method, request.body.decode())
|
||||
|
||||
def process_response(self, request, response): # 基于请求响应
|
||||
duration = time.time() - getattr(request.headers, "my-duration")
|
||||
uid = getattr(request.headers, "uid")
|
||||
self.recordsResponse(uid, request.get_full_path(), response.content.decode(), duration)
|
||||
return response
|
||||
|
||||
@classmethod
|
||||
def white_path(cls, path):
|
||||
request_white_path = []
|
||||
return path in request_white_path
|
||||
|
||||
@classmethod
|
||||
def recordsRequest(cls, uid, path, method, data):
|
||||
request_info = '链路id: %s 接口: %s 请求方式: %s body参数: %s' % (uid, path, method, data)
|
||||
if cls.white_path(path):
|
||||
return
|
||||
logger.info(str(request_info))
|
||||
|
||||
@classmethod
|
||||
def recordsResponse(cls, uid, path, data, duration=None):
|
||||
response_info = '链路id: %s 接口: %s 返回数据: %s 耗时: %s ' % (uid, path, data, duration)
|
||||
if cls.white_path(path):
|
||||
return
|
||||
logger.info(str(response_info))
|
||||
BIN
admin-api/requirements.txt
Normal file
291
admin-api/utils/myDataUtils.py
Normal file
@ -0,0 +1,291 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
========================================================================================================================
|
||||
@project : my-sanic
|
||||
@file: myDataUtils
|
||||
@Author: mengying
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/3/23 15:54
|
||||
@desc: 数据的处理类
|
||||
========================================================================================================================
|
||||
"""
|
||||
import jmespath
|
||||
|
||||
|
||||
class JsonLocator:
|
||||
"""
|
||||
json数据指定位置的快速查询
|
||||
使用教程参见: https://blog.csdn.net/be5yond/article/details/118976017
|
||||
"""
|
||||
|
||||
def __init__(self, json_data):
|
||||
self.json_data = json_data
|
||||
|
||||
def locate(self, query):
|
||||
return jmespath.search(query, self.json_data)
|
||||
|
||||
|
||||
class JsonEditor:
|
||||
"""json数据指定位置的快速修改"""
|
||||
|
||||
def __init__(self, json_data):
|
||||
self.json_data = json_data
|
||||
|
||||
def edit(self, path, new_value):
|
||||
keys = path.split('.')
|
||||
data = self.json_data
|
||||
for index, key in enumerate(keys):
|
||||
if key.isdigit():
|
||||
keys[index] = int(key)
|
||||
for key in keys[:-1]:
|
||||
data = data[key]
|
||||
data[keys[-1]] = new_value
|
||||
|
||||
|
||||
class JsonModifier:
|
||||
"""json数据删除value的bool值是false的值, indent为True只控制第一层级,False删除json全部层级"""
|
||||
|
||||
@staticmethod
|
||||
def deleteBoolKey(json_data, indent=True):
|
||||
if isinstance(json_data, dict):
|
||||
for key, value in list(json_data.items()):
|
||||
if not bool(value):
|
||||
del json_data[key]
|
||||
if indent:
|
||||
return json_data
|
||||
else:
|
||||
JsonModifier.deleteBoolKey(value)
|
||||
elif isinstance(json_data, list):
|
||||
for item in json_data:
|
||||
JsonModifier.deleteBoolKey(item)
|
||||
return json_data
|
||||
|
||||
|
||||
class JsonValidator:
|
||||
"""校验json数据是否包含列表字段"""
|
||||
|
||||
def __init__(self, json_data, fields_list):
|
||||
self.json_data = json_data
|
||||
self.fields_list = fields_list
|
||||
|
||||
def validate(self):
|
||||
for field in self.fields_list:
|
||||
if field not in self.json_data:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class JsonComparator:
|
||||
"""比较2个json数据是否包含"""
|
||||
|
||||
def __init__(self, big_json, small_json):
|
||||
self.big_json = big_json
|
||||
self.small_json = small_json
|
||||
|
||||
def compare(self):
|
||||
for key, value in self.small_json.items():
|
||||
if key not in self.big_json:
|
||||
return False
|
||||
if isinstance(value, dict):
|
||||
if not JsonComparator(self.big_json[key], value).compare():
|
||||
return False
|
||||
elif self.big_json[key] != value:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class JsonDiff:
|
||||
"""比较2个json数据,返回json diff的差异数据"""
|
||||
|
||||
def __init__(self, origin_json, diff_json):
|
||||
self.origin_json = origin_json
|
||||
self.diff_json = diff_json
|
||||
self.container = {}
|
||||
|
||||
def compare(self):
|
||||
for key, value in self.origin_json.items():
|
||||
diff_value = self.diff_json.get(key)
|
||||
if diff_value is not None and value != diff_value:
|
||||
self.container[key] = value
|
||||
return self.container
|
||||
|
||||
|
||||
class JsonRemover:
|
||||
"""删除指定列表,json里面的key"""
|
||||
|
||||
def __init__(self, json_data):
|
||||
self.json_data = json_data
|
||||
|
||||
def remove_fields(self, fields_to_remove):
|
||||
json_keys = self.json_data.keys()
|
||||
for field in fields_to_remove:
|
||||
if field in json_keys:
|
||||
self.json_data.pop(field, None)
|
||||
return self.json_data
|
||||
|
||||
|
||||
class JsonKeeper:
|
||||
"""只保留指定列表,json里面的key"""
|
||||
|
||||
def __init__(self, json_data):
|
||||
self.json_data = json_data
|
||||
self.container = {}
|
||||
|
||||
def keeper_fields(self, fields_to_keeper):
|
||||
json_keys = self.json_data.keys()
|
||||
for field in fields_to_keeper:
|
||||
if field in json_keys:
|
||||
self.container[field] = self.json_data[field]
|
||||
return self.container
|
||||
|
||||
|
||||
class JsonSwap:
|
||||
"""交换json的键值的位置"""
|
||||
|
||||
def __init__(self, json_data):
|
||||
self.json_data = json_data
|
||||
|
||||
def swap(self):
|
||||
return {v: k for k, v in self.json_data.items()}
|
||||
|
||||
|
||||
class ListFilter:
|
||||
"""过滤数据结构 list[dict]中dict满足某种条件的数据"""
|
||||
|
||||
def __init__(self, data, condition):
|
||||
|
||||
self.data = data
|
||||
self.condition = condition
|
||||
self.keeper = []
|
||||
self.filter = []
|
||||
|
||||
def filter(self, reverse=False):
|
||||
for item in self.data:
|
||||
threshold = JsonComparator(big_json=item, small_json=self.condition).compare()
|
||||
if not threshold:
|
||||
self.keeper.append(threshold)
|
||||
else:
|
||||
self.filter.append(threshold)
|
||||
return self.keeper if reverse else self.filter
|
||||
|
||||
|
||||
class List2Dict:
|
||||
"""把list[dict]合并成dict, 注:如果dict里面存在相同字段会被覆盖,特定数据结构使用"""
|
||||
|
||||
def __init__(self, data_list):
|
||||
self.data_list = data_list
|
||||
self.container = {}
|
||||
|
||||
def merge(self):
|
||||
for d in self.data_list:
|
||||
self.container.update(d)
|
||||
return self.container
|
||||
|
||||
|
||||
class TreeBuilder:
|
||||
"""列表数据根据指定的parentId和字数的id组装成关系树"""
|
||||
|
||||
def __init__(self, data):
|
||||
self.data: list = data
|
||||
self.tree: dict = {}
|
||||
|
||||
def build(self, parentKey: str = "parentId", ownerKey: str = "id", topParent: str = None):
|
||||
node_dict = {item[ownerKey]: item for item in self.data}
|
||||
for item in self.data:
|
||||
if item[parentKey] is topParent:
|
||||
self.tree[item[ownerKey]] = item
|
||||
else:
|
||||
parent = node_dict.get(item[parentKey])
|
||||
if parent:
|
||||
if 'children' not in parent:
|
||||
parent['children'] = []
|
||||
parent['children'].append(item)
|
||||
return self.tree
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Example 1 JsonLocator
|
||||
json_data = {
|
||||
"name": {
|
||||
"first": "John",
|
||||
"last": "Doe"
|
||||
},
|
||||
"age": 25,
|
||||
"address": {
|
||||
"street": "123 Main St",
|
||||
"city": "Anytown",
|
||||
"state": "CA",
|
||||
"zip": "12345"
|
||||
}
|
||||
}
|
||||
|
||||
locator = JsonLocator(json_data)
|
||||
print(locator.locate("name.first")) # Output: John
|
||||
|
||||
# Example 1 JsonEditor
|
||||
json_data = {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"cars": {
|
||||
"car1": "Ford",
|
||||
"car2": "BMW",
|
||||
"car3": "Fiat"
|
||||
}
|
||||
}
|
||||
editor = JsonEditor(json_data)
|
||||
editor.edit('cars.car2', 'Mercedes')
|
||||
print(json_data)
|
||||
|
||||
# Example 2 JsonEditor
|
||||
json_data = {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"cars": [
|
||||
{"name": "Ford", "models": ["Fiesta", "Focus", "Mustang"]},
|
||||
{"name": "BMW", "models": ["320", "X3", "X5"]},
|
||||
{"name": "Fiat", "models": ["500", "Panda"]}
|
||||
]
|
||||
}
|
||||
editor = JsonEditor(json_data)
|
||||
editor.edit('cars.1.models.2', 'X6')
|
||||
print(json_data)
|
||||
|
||||
# Example 3 JsonEditor
|
||||
json_data = {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"address": {
|
||||
"street": "Main Street",
|
||||
"city": "New York",
|
||||
"state": "NY",
|
||||
"zip": "10001"
|
||||
}
|
||||
}
|
||||
editor = JsonEditor(json_data)
|
||||
editor.edit('address.zip', '10002')
|
||||
print(json_data)
|
||||
|
||||
# Example 1 JsonModifier
|
||||
json_data = {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"married": "",
|
||||
"pets": [
|
||||
{
|
||||
"animal": "1",
|
||||
"name": "Fido",
|
||||
"is_alive": True
|
||||
},
|
||||
{
|
||||
"animal": "cat",
|
||||
"name": "Fluffy",
|
||||
"is_alive": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
json_data = JsonModifier.deleteBoolKey(json_data, indent=False)
|
||||
print(json_data)
|
||||
|
||||
data = [{'id': '1739096268195635200', 'parentId': '0', 'component': 'Layout', 'hidden': False, 'path': '/system', 'name': 'System', 'redirect': 'noRedirect', 'meta': {'icon': 'system', 'link': '', 'noCache': False, 'title': '系统管理'}}, {'id': '1739096268195635201', 'parentId': '1739096268195635200', 'component': '0', 'hidden': False, 'path': 'user', 'name': 'User', 'redirect': 'noRedirect', 'meta': {'icon': 'user', 'link': '', 'noCache': False, 'title': '用户管理'}}, {'id': '1739096268195635202', 'parentId': '1739096268195635200', 'component': '0', 'hidden': False, 'path': 'role', 'name': 'Role', 'redirect': 'noRedirect', 'meta': {'icon': 'peoples', 'link': '', 'noCache': False, 'title': '角色管理'}}, {'id': '1739096268195635203', 'parentId': '1739096268195635200', 'component': '0', 'hidden': False, 'path': 'menu', 'name': 'Menu', 'redirect': 'noRedirect', 'meta': {'icon': 'tree-table', 'link': '', 'noCache': False, 'title': '菜单管理'}}, {'id': '1739096268195635204', 'parentId': '1739096268195635200', 'component': '0', 'hidden': False, 'path': 'dept', 'name': 'Dept', 'redirect': 'noRedirect', 'meta': {'icon': 'tree', 'link': '', 'noCache': False, 'title': '部门管理'}}]
|
||||
print(TreeBuilder(data).build(parentKey="parentId", ownerKey="id", topParent="0"))
|
||||
59
admin-api/utils/myEncrypt.py
Normal file
@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
========================================================================================================================
|
||||
@Author: 孟颖
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/4/20 10:19
|
||||
@desc: 加密解密模块
|
||||
========================================================================================================================
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
|
||||
|
||||
class HashCipher:
|
||||
|
||||
@staticmethod
|
||||
def md5(message):
|
||||
"""
|
||||
Returns the MD5 hash of the message
|
||||
"""
|
||||
return hashlib.md5(message.encode()).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def sha1(message):
|
||||
"""
|
||||
Returns the SHA1 hash of the message
|
||||
"""
|
||||
return hashlib.sha1(message.encode()).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def sha256(message):
|
||||
"""
|
||||
Returns the SHA256 hash of the message
|
||||
"""
|
||||
return hashlib.sha256(message.encode()).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def sha512(message):
|
||||
"""
|
||||
Returns the SHA512 hash of the message
|
||||
"""
|
||||
return hashlib.sha512(message.encode()).hexdigest()
|
||||
|
||||
|
||||
class Base64Cipher:
|
||||
@staticmethod
|
||||
def bytes_to_base64(bytes_data):
|
||||
"""
|
||||
Converts bytes to base64 string
|
||||
"""
|
||||
return base64.b64encode(bytes_data).decode('utf-8')
|
||||
|
||||
@staticmethod
|
||||
def base64_to_bytes(base64_string):
|
||||
"""
|
||||
Converts base64 string to bytes
|
||||
"""
|
||||
return base64.b64decode(base64_string.encode('utf-8'))
|
||||
54
admin-api/utils/myEnum.py
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
========================================================================================================================
|
||||
@Author: 孟颖
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/4/20 10:19
|
||||
@desc: 枚举模块自定义dict转换
|
||||
========================================================================================================================
|
||||
"""
|
||||
from enum import Enum, unique
|
||||
|
||||
|
||||
class EnumDict(Enum):
|
||||
|
||||
@classmethod
|
||||
def transform(cls):
|
||||
res = dict()
|
||||
for key, value in cls.__members__.items():
|
||||
res[key] = value.value
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def reverse_transform(cls):
|
||||
res = dict()
|
||||
for key, value in cls.__members__.items():
|
||||
res[value.value] = key
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def format_front(cls):
|
||||
return [{"label": key, "value": value.value} for key, value in cls.__members__.items()]
|
||||
|
||||
@classmethod
|
||||
def get_value(cls, key):
|
||||
enum_map = cls.transform()
|
||||
return enum_map.get(key)
|
||||
|
||||
|
||||
@unique
|
||||
class SystemStatusEnum(EnumDict):
|
||||
p0 = "0" # 0正常
|
||||
p1 = "1" # 1停用
|
||||
|
||||
|
||||
@unique
|
||||
class SystemDelEnum(EnumDict):
|
||||
p0 = "0" # 0存在
|
||||
p1 = "2" # 1删除
|
||||
|
||||
|
||||
@unique
|
||||
class SystemUserTypeEnum(EnumDict):
|
||||
p0 = "00" # 系统账号
|
||||
p1 = "01" # 管理员账号
|
||||
48
admin-api/utils/myResFormat.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
========================================================================================================================
|
||||
@file: myResFormat
|
||||
@Author: mengying
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/3/3 12:14
|
||||
@desc: 返回的数据格式定义
|
||||
========================================================================================================================
|
||||
"""
|
||||
from addict import Dict
|
||||
|
||||
|
||||
class ResultCode:
|
||||
"""
|
||||
@param: code返回码
|
||||
@param: msg信息
|
||||
@param: description描述
|
||||
@desc: 返回码
|
||||
"""
|
||||
SUCCESS = (200, '操作成功', None)
|
||||
FAIL = (500, '操作失败', None)
|
||||
TOKEN_ERROR = (401, 'token过期', None)
|
||||
SERVER_ERROR = (500, '服务错误', None)
|
||||
DEPT_ERROR = (500, '部门名称重复', None)
|
||||
ROLE_ERROR = (500, '角色名称或权限字符重复', None)
|
||||
USER_ERROR = (500, '用户名已存在', None)
|
||||
MENU_ERROR = (500, '目录名称重复', None)
|
||||
PASSWORD_ERROR = (500, '密码错误', None)
|
||||
AUTH_CODE_EXP = (500, '验证码过期', None)
|
||||
AUTH_CODE_ERROR = (500, '验证码错误', None)
|
||||
PRE_AUTH_ERROR = (400, '权限不足', None)
|
||||
USER_NOT_EXIST = (500, '用户不存在', None)
|
||||
USER_NOT_ALLOW = (500, '用户被禁用,请联系系统管理员', None)
|
||||
|
||||
|
||||
class ResultJson:
|
||||
result = Dict()
|
||||
|
||||
def __init__(self, ret=ResultCode.SUCCESS, data=None, description=None, postscript=None):
|
||||
self.result.code = ret[0]
|
||||
self.result.msg = ret[1] % postscript if postscript else ret[1]
|
||||
self.result.data = data
|
||||
self.result.description = description or ret[2]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(ResultJson(ret=ResultCode.SUCCESS, data=None, description="成功").result)
|
||||
122
admin-api/utils/mySnowflake.py
Normal file
@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
========================================================================================================================
|
||||
@project : my-sanic
|
||||
@file: myUUID
|
||||
@Author: mengying
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/4/4 12:14
|
||||
@desc: 雪花算法生成分布式id,用户订单生成相关或者uuid使用
|
||||
========================================================================================================================
|
||||
"""
|
||||
|
||||
import time
|
||||
from threading import Lock
|
||||
|
||||
# 64 位 id 的划分,通常机器位和数据位各为 5 位
|
||||
WORKER_ID_BITS = 5 # 机器位
|
||||
DATACENTER_ID_BITS = 5 # 数据位
|
||||
SEQUENCE_BITS = 12 # 循环位
|
||||
|
||||
# 最大取值计算,计算机中负数表示为他的补码
|
||||
MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS) # 2**5 -1 =31
|
||||
MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS)
|
||||
|
||||
# 移位偏移计算
|
||||
WORKER_ID_SHIFT = SEQUENCE_BITS
|
||||
DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
|
||||
TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS
|
||||
|
||||
# X序号循环掩码
|
||||
SEQUENCE_MASK = -1 ^ (-1 << SEQUENCE_BITS)
|
||||
|
||||
# Twitter 元年时间戳
|
||||
TWEPOCH = 1288834974657
|
||||
|
||||
|
||||
class SingletonMeta(type):
|
||||
"""元类单例"""
|
||||
__instance = None
|
||||
__lock = Lock()
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
with cls.__lock:
|
||||
new = kwargs.pop('new', None)
|
||||
if new is True:
|
||||
return super().__call__(*args, **kwargs)
|
||||
if not cls.__instance:
|
||||
cls.__instance = super().__call__(*args, **kwargs)
|
||||
return cls.__instance
|
||||
|
||||
|
||||
class SnowIdWorker(metaclass=SingletonMeta):
|
||||
"""不能实例多次,会生产一样的id"""
|
||||
|
||||
def __init__(self, datacenter_id=1, worker_id=2, sequence=0):
|
||||
"""
|
||||
初始化方法
|
||||
:param datacenter_id:数据id
|
||||
:param worker_id:机器id
|
||||
:param sequence:序列码
|
||||
"""
|
||||
if worker_id > MAX_WORKER_ID or worker_id < 0:
|
||||
raise ValueError('worker_id 值越界')
|
||||
if datacenter_id > MAX_DATACENTER_ID or datacenter_id < 0:
|
||||
raise ValueError('datacenter_id 值越界')
|
||||
|
||||
self.worker_id = worker_id
|
||||
self.datacenter_id = datacenter_id
|
||||
self.sequence = sequence
|
||||
|
||||
self.last_timestamp = -1 # 上次计算的时间戳
|
||||
|
||||
def _gen_timestamp(self):
|
||||
"""
|
||||
生成整数时间戳。
|
||||
:return:
|
||||
"""
|
||||
return int(time.time() * 1000)
|
||||
|
||||
def generate(self) -> str:
|
||||
"""
|
||||
获取新的ID.
|
||||
:return:
|
||||
"""
|
||||
# 获取当前时间戳
|
||||
timestamp = self._gen_timestamp()
|
||||
|
||||
# 时钟回拨的情况
|
||||
if timestamp < self.last_timestamp:
|
||||
raise Exception("clock is moving backwards")
|
||||
|
||||
if timestamp == self.last_timestamp:
|
||||
# 同一毫秒的处理。
|
||||
self.sequence = (self.sequence + 1) & SEQUENCE_MASK
|
||||
if self.sequence == 0:
|
||||
timestamp = self._til_next_millis(self.last_timestamp)
|
||||
else:
|
||||
self.sequence = 0
|
||||
|
||||
self.last_timestamp = timestamp
|
||||
|
||||
new_id = (((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (self.datacenter_id << DATACENTER_ID_SHIFT) | (
|
||||
self.worker_id << WORKER_ID_SHIFT)) | self.sequence
|
||||
return str(new_id)
|
||||
|
||||
def _til_next_millis(self, last_timestamp):
|
||||
"""
|
||||
等到下一毫秒。
|
||||
:param last_timestamp:
|
||||
:return:
|
||||
"""
|
||||
timestamp = self._gen_timestamp()
|
||||
while timestamp <= last_timestamp:
|
||||
timestamp = self._gen_timestamp()
|
||||
return timestamp
|
||||
|
||||
|
||||
Sf = SnowIdWorker()
|
||||
|
||||
if __name__ == '__main__':
|
||||
for i in range(1000):
|
||||
print(Sf.generate())
|
||||
41
admin-api/utils/myTimeFormat.py
Normal file
@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author: 孟颖
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/4/23 10:03
|
||||
@desc: 时间类,生成各类的时间和时间格式转换
|
||||
"""
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class MyTimeUtils:
|
||||
dateTimeType = '%Y-%m-%d %H:%M:%S'
|
||||
dateType = '%Y-%m-%d'
|
||||
timeType = '%H:%M:%S'
|
||||
fileTimeType = "%Y%m%d%H%M%S%f"
|
||||
fileTimeShortType = "%Y%m%d%H%M%S"
|
||||
|
||||
@classmethod
|
||||
def TimeFormat(cls, now_time: datetime = datetime.now(), timeType: str = dateTimeType) -> str:
|
||||
return now_time.strftime(timeType)
|
||||
|
||||
@classmethod
|
||||
def TimeFormatCh(cls, now_time: datetime = datetime.now()) -> str:
|
||||
ch_time = now_time.strftime(cls.dateType)
|
||||
return "%s年%s月%s日" % tuple(ch_time.split("-"))
|
||||
|
||||
@classmethod
|
||||
def TimestampFormat(cls, long: bool = False) -> int:
|
||||
return int(time.time() * 1000) if long else int(time.time())
|
||||
|
||||
@classmethod
|
||||
def TimeOffsetFormat(cls, start_time: datetime = datetime.now(), days: int = 0, hour: int = 0, minute: int = 0, second: int = 0,
|
||||
timeType: str = dateTimeType) -> str:
|
||||
offsetDateTime = start_time + timedelta(days=days, hours=hour, minutes=minute, seconds=second)
|
||||
return cls.TimeFormat(offsetDateTime, timeType)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(MyTimeUtils.TimeOffsetFormat(days=-1, timeType=MyTimeUtils.fileTimeType))
|
||||
print(MyTimeUtils.TimeFormatCh())
|
||||
0
admin-api/web/__init__.py
Normal file
3
admin-api/web/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
admin-api/web/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WebConfigweb(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'web'
|
||||
109
admin-api/web/captchaImage.py
Normal file
@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author: 孟颖
|
||||
@email: 652044581@qq.com
|
||||
@date: 2023/4/13 13:36
|
||||
@desc: 生成验证码
|
||||
"""
|
||||
import pathlib
|
||||
import random
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from io import BytesIO
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class CaptchaImage:
|
||||
|
||||
def __init__(self, width=130, height=35, string_count=4, font_size=30, noise_line=3, noise_point=30):
|
||||
"""
|
||||
:param width: 图片宽度
|
||||
:param height: 图片高度
|
||||
:param string_count: 验证码的数量
|
||||
:param font_size: 字体的大小
|
||||
:param noise_line: 噪声线的数量
|
||||
:param noise_point: 噪声点的数量
|
||||
"""
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.string_count = string_count
|
||||
self.font_size = font_size
|
||||
self.noise_line = noise_line
|
||||
self.noise_point = noise_point
|
||||
|
||||
# 生成验证码
|
||||
def generate(self, width=120, height=35):
|
||||
image = self.background()
|
||||
captcha_string, image = self.draw_string(image)
|
||||
image: Image = self.noise(image)
|
||||
image = image.resize((width, height), resample=Image.BICUBIC)
|
||||
output_buffer = BytesIO()
|
||||
image.save(output_buffer, format='png')
|
||||
binary_data = output_buffer.getvalue()
|
||||
image_base64 = self.io2base64(binary_data)
|
||||
return captcha_string, image_base64
|
||||
|
||||
# io转base64
|
||||
def io2base64(self, content: bytes):
|
||||
encode_data = base64.b64encode(content)
|
||||
return str(encode_data, encoding='utf-8')
|
||||
|
||||
# 生成背景
|
||||
def background(self):
|
||||
background_color = (255, 255, 255)
|
||||
image = Image.new('RGB', (self.width, self.height), background_color)
|
||||
return image
|
||||
|
||||
# 随机颜色
|
||||
def random_color(self):
|
||||
c1 = random.randint(0, 200)
|
||||
c2 = random.randint(0, 200)
|
||||
c3 = random.randint(0, 200)
|
||||
return c1, c2, c3
|
||||
|
||||
# 生成随机字符串
|
||||
def random_string(self):
|
||||
random_num = str(random.randint(0, 9))
|
||||
random_low_alpha = chr(random.randint(97, 122))
|
||||
return random.choice([random_num, random_low_alpha])
|
||||
|
||||
# 合成文字
|
||||
def draw_string(self, image: Image):
|
||||
draw = ImageDraw.Draw(image)
|
||||
font_file = pathlib.Path.joinpath(Path(__file__).parent, "static/fontBold.ttf").as_posix()
|
||||
font = ImageFont.truetype(font=font_file, size=self.font_size)
|
||||
string_container = []
|
||||
for i in range(self.string_count):
|
||||
random_char = self.random_string()
|
||||
draw.text((10 + i * 30, -2), random_char, self.random_color(), font=font)
|
||||
string_container.append(random_char)
|
||||
captcha_string = "".join(string_container)
|
||||
return captcha_string, image
|
||||
|
||||
# 生成噪声
|
||||
def noise(self, image: Image):
|
||||
|
||||
draw = ImageDraw.Draw(image)
|
||||
# 噪声线
|
||||
for i in range(self.noise_line):
|
||||
x1 = random.randint(0, self.width)
|
||||
x2 = random.randint(0, self.width)
|
||||
y1 = random.randint(0, self.height)
|
||||
y2 = random.randint(0, self.height)
|
||||
draw.line((x1, y1, x2, y2), fill=self.random_color())
|
||||
|
||||
# 噪声点
|
||||
for i in range(self.noise_point):
|
||||
draw.point([random.randint(0, self.width), random.randint(0, self.height)], fill=self.random_color())
|
||||
x = random.randint(0, self.width)
|
||||
y = random.randint(0, self.height)
|
||||
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=self.random_color())
|
||||
|
||||
return image
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
captcha = CaptchaImage()
|
||||
|
||||
# 生成验证码图片和字符串
|
||||
captcha_string, image_base64 = captcha.generate(width=108, height=36)
|
||||
37
admin-api/web/migrations/0001_initial.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Generated by Django 3.2.25 on 2024-05-31 04:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SystemDept',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('parentId', models.CharField(default='', help_text='父id', max_length=64, verbose_name='父id')),
|
||||
('deptName', models.CharField(default='', help_text='部门名称', max_length=64, verbose_name='部门名称')),
|
||||
('deptId', models.CharField(default='', help_text='部门id', max_length=64, unique=True, verbose_name='部门id')),
|
||||
('orderNum', models.IntegerField(default=0, help_text='排序', verbose_name='排序')),
|
||||
('leader', models.CharField(default='', help_text='负责人', max_length=64, verbose_name='负责人')),
|
||||
('phone', models.CharField(default='', help_text='联系电话', max_length=64, verbose_name='联系电话')),
|
||||
('email', models.CharField(default='', help_text='邮箱', max_length=64, verbose_name='邮箱')),
|
||||
('status', models.CharField(default='', help_text='启用状态 (0正常 1停用)', max_length=64, verbose_name='启用状态')),
|
||||
('delFlag', models.CharField(default='', help_text='是否删除 (0存在 2删除)', max_length=64, verbose_name='是否删除')),
|
||||
('ancestors', models.CharField(default='', help_text='上级目录id', max_length=64, verbose_name='上级目录id')),
|
||||
('createBy', models.CharField(default='', help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '部门管理',
|
||||
'db_table': 'system_dept',
|
||||
},
|
||||
),
|
||||
]
|
||||
174
admin-api/web/migrations/0002_auto_20240531_1456.py
Normal file
@ -0,0 +1,174 @@
|
||||
# Generated by Django 3.2.25 on 2024-05-31 06:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SystemInit',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
|
||||
('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
('Init', models.BooleanField(blank=True, default=False, help_text='是否已经初始化数据库')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '核心模型',
|
||||
'verbose_name_plural': '核心模型',
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SystemMenu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
|
||||
('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
('menuId', models.CharField(default='', help_text='菜单ID', max_length=64, unique=True, verbose_name='菜单ID')),
|
||||
('menuName', models.CharField(default='', help_text='菜单名称', max_length=64, verbose_name='菜单名称')),
|
||||
('parentId', models.CharField(default='', help_text='父菜单ID', max_length=64, verbose_name='父菜单ID')),
|
||||
('orderNum', models.IntegerField(default=0, help_text='显示顺序', verbose_name='显示顺序')),
|
||||
('path', models.CharField(default='', help_text='路由地址', max_length=64, verbose_name='路由地址')),
|
||||
('component', models.CharField(default='', help_text='组件路径', max_length=64, verbose_name='组件路径')),
|
||||
('query', models.CharField(default='', help_text='路由参数', max_length=64, verbose_name='路由参数')),
|
||||
('isFrame', models.CharField(default='', help_text='是否为外链(0是 1否)', max_length=4, verbose_name='是否为外链(0是 1否)')),
|
||||
('isCache', models.CharField(default='', help_text='是否缓存(0缓存 1不缓存)', max_length=4, verbose_name='是否缓存(0缓存 1不缓存)')),
|
||||
('visible', models.CharField(default='', help_text='菜单状态(0显示 1隐藏)', max_length=4, verbose_name='菜单状态(0显示 1隐藏)')),
|
||||
('menuType', models.CharField(default='', help_text='菜单类型(M目录 C菜单 F按钮)', max_length=4, verbose_name='菜单类型(M目录 C菜单 F按钮)')),
|
||||
('status', models.CharField(default='', help_text='菜单状态(0正常 1停用)', max_length=4, verbose_name='菜单状态(0正常 1停用)')),
|
||||
('perms', models.CharField(default='', help_text='权限标识', max_length=32, verbose_name='权限标识')),
|
||||
('icon', models.CharField(default='', help_text='菜单图', max_length=32, verbose_name='菜单图')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '目录管理',
|
||||
'db_table': 'system_menu',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SystemRole',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
|
||||
('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
('roleId', models.CharField(default='', help_text='角色id', max_length=64, unique=True, verbose_name='角色id')),
|
||||
('roleName', models.CharField(default='', help_text='角色名称', max_length=64, verbose_name='角色名称')),
|
||||
('roleKey', models.CharField(default='', help_text='角色权限字符', max_length=64, verbose_name='角色权限字符')),
|
||||
('roleSort', models.IntegerField(default=0, help_text='角色排序', verbose_name='角色排序')),
|
||||
('roleAdmin', models.BooleanField(default=False, help_text='是否是超级管理员', max_length=64, verbose_name='是否是超级管理员')),
|
||||
('status', models.CharField(default='0', help_text='启用状态 (0正常 1停用)', max_length=4, verbose_name='启用状态 (0正常 1停用)')),
|
||||
('menuCheckStrictly', models.BooleanField(default=True, help_text='菜单树选择项是否关联显示', verbose_name='菜单树选择项是否关联显示')),
|
||||
('deptCheckStrictly', models.BooleanField(default=True, help_text='部门树选择项是否关联显示', verbose_name='部门树选择项是否关联显示')),
|
||||
('delFlag', models.CharField(default='0', help_text='是否删除 (0代表存在 2代表删除)', max_length=4, verbose_name='是否删除 (0代表存在 2代表删除)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色管理',
|
||||
'db_table': 'system_role',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SystemRoleMenu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
|
||||
('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
('menuId', models.CharField(default='', help_text='菜单id', max_length=64, verbose_name='菜单id')),
|
||||
('roleId', models.CharField(default='', help_text='角色id', max_length=64, verbose_name='角色id')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色与目录关系映射',
|
||||
'db_table': 'system_role_menu',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SystemUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
|
||||
('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
('userId', models.CharField(default='', help_text='用户id', max_length=64, unique=True, verbose_name='用户id')),
|
||||
('deptId', models.CharField(default='', help_text='部门id', max_length=64, verbose_name='部门id')),
|
||||
('username', models.CharField(default='', help_text='用户账号', max_length=64, verbose_name='用户账号')),
|
||||
('nickName', models.CharField(default='', help_text='用户昵称', max_length=64, verbose_name='用户昵称')),
|
||||
('userType', models.CharField(default=False, help_text='用户类型(00系统用户)', max_length=4, verbose_name='用户类型(00系统用户)')),
|
||||
('email', models.CharField(default='', help_text='邮箱', max_length=64, verbose_name='邮箱')),
|
||||
('phone', models.CharField(default='', help_text='手机号码', max_length=64, verbose_name='手机号码')),
|
||||
('avatar', models.CharField(default='', help_text='头像', max_length=64, verbose_name='头像')),
|
||||
('password', models.CharField(default='', help_text='密码(加密后)', max_length=64, verbose_name='密码(加密后)')),
|
||||
('status', models.CharField(default='0', help_text='帐号状态(0正常 1停用)', max_length=4, verbose_name='帐号状态(0正常 1停用)')),
|
||||
('delFlag', models.CharField(default='0', help_text='删除标志(0代表存在 2代表删除)', max_length=4, verbose_name='删除标志(0代表存在 2代表删除)')),
|
||||
('loginIp', models.CharField(default='', help_text='最后登陆ip', max_length=64, verbose_name='最后登陆ip')),
|
||||
('loginDate', models.CharField(default='', help_text='最后登陆日期', max_length=64, verbose_name='最后登陆日期')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户管理',
|
||||
'db_table': 'system_user',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SystemUserRole',
|
||||
fields=[
|
||||
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
|
||||
('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
|
||||
('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
|
||||
('userId', models.CharField(default='', help_text='用户id', max_length=64, verbose_name='菜单id')),
|
||||
('roleId', models.CharField(default='', help_text='角色id', max_length=64, verbose_name='角色id')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色与人员关系映射',
|
||||
'db_table': 'system_role_user',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='systemdept',
|
||||
name='remark',
|
||||
field=models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemdept',
|
||||
name='createBy',
|
||||
field=models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemdept',
|
||||
name='delFlag',
|
||||
field=models.CharField(default='0', help_text='是否删除 (0存在 2删除)', max_length=4, verbose_name='是否删除'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemdept',
|
||||
name='email',
|
||||
field=models.CharField(default='', help_text='邮箱', max_length=32, verbose_name='邮箱'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemdept',
|
||||
name='leader',
|
||||
field=models.CharField(default='', help_text='负责人', max_length=32, verbose_name='负责人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemdept',
|
||||
name='phone',
|
||||
field=models.CharField(default='', help_text='联系电话', max_length=32, verbose_name='联系电话'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemdept',
|
||||
name='status',
|
||||
field=models.CharField(default='0', help_text='启用状态 (0正常 1停用)', max_length=4, verbose_name='启用状态'),
|
||||
),
|
||||
]
|
||||
21
admin-api/web/migrations/0003_auto_20240531_2104.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Generated by Django 3.2.25 on 2024-05-31 13:04
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web', '0002_auto_20240531_1456'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='systeminit',
|
||||
options={'verbose_name': '部门管理'},
|
||||
),
|
||||
migrations.AlterModelTable(
|
||||
name='systeminit',
|
||||
table='system_init',
|
||||
),
|
||||
]
|
||||
58
admin-api/web/migrations/0004_auto_20240614_1628.py
Normal file
@ -0,0 +1,58 @@
|
||||
# Generated by Django 3.2.25 on 2024-06-14 08:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web', '0003_auto_20240531_2104'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='systemmenu',
|
||||
name='isCache',
|
||||
field=models.CharField(default='0', help_text='是否缓存(0缓存 1不缓存)', max_length=4, verbose_name='是否缓存(0缓存 1不缓存)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemmenu',
|
||||
name='isFrame',
|
||||
field=models.CharField(default='1', help_text='是否为外链(0是 1否)', max_length=4, verbose_name='是否为外链(0是 1否)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemmenu',
|
||||
name='status',
|
||||
field=models.CharField(default='0', help_text='菜单状态(0正常 1停用)', max_length=4, verbose_name='菜单状态(0正常 1停用)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemmenu',
|
||||
name='visible',
|
||||
field=models.CharField(default='0', help_text='菜单状态(0显示 1隐藏)', max_length=4, verbose_name='菜单状态(0显示 1隐藏)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemuser',
|
||||
name='userType',
|
||||
field=models.CharField(default='00', help_text='用户类型(00系统用户)', max_length=4, verbose_name='用户类型(00系统用户)'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='systemdept',
|
||||
index=models.Index(fields=['deptId'], name='SystemDept_deptId'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='systemmenu',
|
||||
index=models.Index(fields=['menuId'], name='SystemMenu_menuId'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='systemrole',
|
||||
index=models.Index(fields=['roleId'], name='SystemRole_roleId'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='systemrolemenu',
|
||||
index=models.Index(fields=['roleId'], name='SystemRoleMenu_roleId'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='systemuser',
|
||||
index=models.Index(fields=['userId'], name='SystemUser_userId'),
|
||||
),
|
||||
]
|
||||
0
admin-api/web/migrations/__init__.py
Normal file
108
admin-api/web/models.py
Normal file
@ -0,0 +1,108 @@
|
||||
from django.db import models
|
||||
|
||||
from database.core import CoreModel
|
||||
|
||||
|
||||
class SystemInit(CoreModel):
|
||||
Init = models.BooleanField(default=False, blank=True, help_text="是否已经初始化数据库")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_init'
|
||||
verbose_name = '部门管理'
|
||||
|
||||
|
||||
class SystemDept(CoreModel):
|
||||
parentId = models.CharField(max_length=64, default="", verbose_name="父id", help_text="父id")
|
||||
deptName = models.CharField(max_length=64, default="", verbose_name="部门名称", help_text="部门名称")
|
||||
deptId = models.CharField(unique=True, max_length=64, default="", verbose_name="部门id", help_text="部门id")
|
||||
orderNum = models.IntegerField(default=0, verbose_name="排序", help_text="排序")
|
||||
leader = models.CharField(max_length=32, default="", verbose_name="负责人", help_text="负责人")
|
||||
phone = models.CharField(max_length=32, default="", verbose_name="联系电话", help_text="联系电话")
|
||||
email = models.CharField(max_length=32, default="", verbose_name="邮箱", help_text="邮箱")
|
||||
status = models.CharField(max_length=4, default="0", verbose_name="启用状态", help_text="启用状态 (0正常 1停用)")
|
||||
delFlag = models.CharField(max_length=4, default="0", verbose_name="是否删除", help_text="是否删除 (0存在 2删除)")
|
||||
ancestors = models.CharField(max_length=64, default="", verbose_name="上级目录id", help_text="上级目录id")
|
||||
|
||||
class Meta:
|
||||
indexes = [models.Index(fields=['deptId'], name='SystemDept_deptId')]
|
||||
db_table = 'system_dept'
|
||||
verbose_name = '部门管理'
|
||||
|
||||
|
||||
class SystemMenu(CoreModel):
|
||||
menuId = models.CharField(unique=True, max_length=64, default="", verbose_name="菜单ID", help_text="菜单ID")
|
||||
menuName = models.CharField(max_length=64, default="", verbose_name="菜单名称", help_text="菜单名称")
|
||||
parentId = models.CharField(max_length=64, default="", verbose_name="父菜单ID", help_text="父菜单ID")
|
||||
orderNum = models.IntegerField(default=0, verbose_name="显示顺序", help_text="显示顺序")
|
||||
path = models.CharField(max_length=64, default="", verbose_name="路由地址", help_text="路由地址")
|
||||
component = models.CharField(max_length=64, default="", verbose_name="组件路径", help_text="组件路径")
|
||||
query = models.CharField(max_length=64, default="", verbose_name="路由参数", help_text="路由参数")
|
||||
isFrame = models.CharField(max_length=4, default="1", verbose_name="是否为外链(0是 1否)", help_text="是否为外链(0是 1否)")
|
||||
isCache = models.CharField(max_length=4, default="0", verbose_name="是否缓存(0缓存 1不缓存)", help_text="是否缓存(0缓存 1不缓存)")
|
||||
visible = models.CharField(max_length=4, default="0", verbose_name="菜单状态(0显示 1隐藏)", help_text="菜单状态(0显示 1隐藏)")
|
||||
menuType = models.CharField(max_length=4, default="", verbose_name="菜单类型(M目录 C菜单 F按钮)", help_text="菜单类型(M目录 C菜单 F按钮)")
|
||||
status = models.CharField(max_length=4, default="0", verbose_name="菜单状态(0正常 1停用)", help_text="菜单状态(0正常 1停用)")
|
||||
perms = models.CharField(max_length=32, default="", verbose_name="权限标识", help_text="权限标识")
|
||||
icon = models.CharField(max_length=32, default="", verbose_name="菜单图", help_text="菜单图")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_menu'
|
||||
verbose_name = '目录管理'
|
||||
indexes = [models.Index(fields=['menuId'], name='SystemMenu_menuId')]
|
||||
|
||||
|
||||
class SystemRole(CoreModel):
|
||||
roleId = models.CharField(unique=True, max_length=64, default="", verbose_name="角色id", help_text="角色id")
|
||||
roleName = models.CharField(max_length=64, default="", verbose_name="角色名称", help_text="角色名称")
|
||||
roleKey = models.CharField(max_length=64, default="", verbose_name="角色权限字符", help_text="角色权限字符")
|
||||
roleSort = models.IntegerField(default=0, verbose_name="角色排序", help_text="角色排序")
|
||||
roleAdmin = models.BooleanField(max_length=64, default=False, verbose_name="是否是超级管理员", help_text="是否是超级管理员")
|
||||
status = models.CharField(max_length=4, default="0", verbose_name="启用状态 (0正常 1停用)", help_text="启用状态 (0正常 1停用)")
|
||||
menuCheckStrictly = models.BooleanField(default=True, verbose_name="菜单树选择项是否关联显示", help_text="菜单树选择项是否关联显示")
|
||||
deptCheckStrictly = models.BooleanField(default=True, verbose_name="部门树选择项是否关联显示", help_text="部门树选择项是否关联显示")
|
||||
delFlag = models.CharField(max_length=4, default="0", verbose_name="是否删除 (0代表存在 2代表删除)", help_text="是否删除 (0代表存在 2代表删除)")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_role'
|
||||
verbose_name = '角色管理'
|
||||
indexes = [models.Index(fields=['roleId'], name='SystemRole_roleId')]
|
||||
|
||||
|
||||
class SystemUser(CoreModel):
|
||||
userId = models.CharField(unique=True, max_length=64, default="", verbose_name="用户id", help_text="用户id")
|
||||
deptId = models.CharField(max_length=64, default="", verbose_name="部门id", help_text="部门id")
|
||||
username = models.CharField(max_length=64, default="", verbose_name="用户账号", help_text="用户账号")
|
||||
nickName = models.CharField(max_length=64, default="", verbose_name="用户昵称", help_text="用户昵称")
|
||||
userType = models.CharField(max_length=4, default="00", verbose_name="用户类型(00系统用户)", help_text="用户类型(00系统用户)")
|
||||
email = models.CharField(max_length=64, default="", verbose_name="邮箱", help_text="邮箱")
|
||||
phone = models.CharField(max_length=64, default="", verbose_name="手机号码", help_text="手机号码")
|
||||
avatar = models.CharField(max_length=64, default="", verbose_name="头像", help_text="头像")
|
||||
password = models.CharField(max_length=64, default="", verbose_name="密码(加密后)", help_text="密码(加密后)")
|
||||
status = models.CharField(max_length=4, default="0", verbose_name="帐号状态(0正常 1停用)", help_text="帐号状态(0正常 1停用)")
|
||||
delFlag = models.CharField(max_length=4, default="0", verbose_name="删除标志(0代表存在 2代表删除)", help_text="删除标志(0代表存在 2代表删除)")
|
||||
loginIp = models.CharField(max_length=64, default="", verbose_name="最后登陆ip", help_text="最后登陆ip")
|
||||
loginDate = models.CharField(max_length=64, default="", verbose_name="最后登陆日期", help_text="最后登陆日期")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_user'
|
||||
verbose_name = '用户管理'
|
||||
indexes = [models.Index(fields=['userId'], name='SystemUser_userId')]
|
||||
|
||||
|
||||
class SystemRoleMenu(CoreModel):
|
||||
menuId = models.CharField(max_length=64, default="", verbose_name="菜单id", help_text="菜单id")
|
||||
roleId = models.CharField(max_length=64, default="", verbose_name="角色id", help_text="角色id")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_role_menu'
|
||||
verbose_name = '角色与目录关系映射'
|
||||
indexes = [models.Index(fields=['roleId'], name='SystemRoleMenu_roleId')]
|
||||
|
||||
|
||||
class SystemUserRole(CoreModel):
|
||||
userId = models.CharField(max_length=64, default="", verbose_name="菜单id", help_text="用户id")
|
||||
roleId = models.CharField(max_length=64, default="", verbose_name="角色id", help_text="角色id")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_role_user'
|
||||
verbose_name = '角色与人员关系映射'
|
||||
42
admin-api/web/paginator.py
Normal file
@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author :mengying
|
||||
@Date :2024/6/13 14:27
|
||||
@Email : 652044581@qq.com
|
||||
@Desc : 分页器
|
||||
"""
|
||||
from rest_framework.exceptions import NotFound
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
|
||||
|
||||
class StandardResultsSetPagination(PageNumberPagination):
|
||||
# 默认每页显示的数据条数
|
||||
page_size = 10
|
||||
|
||||
# 获取URL参数中设置的每页显示数据条数
|
||||
page_size_query_param = 'pageSize'
|
||||
|
||||
# 获取URL参数中传入的页码key
|
||||
page_query_param = 'pageNum'
|
||||
|
||||
# 最大支持的每页显示的数据条数
|
||||
max_page_size = 50
|
||||
|
||||
def paginate_queryset_data(self, queryset, request, view=None, serializer=None):
|
||||
try:
|
||||
queryset = super().paginate_queryset(queryset, request=request, view=view)
|
||||
ser = serializer(queryset, many=True)
|
||||
return ser.data
|
||||
except NotFound as e:
|
||||
return []
|
||||
|
||||
def paginate_queryset_count(self, queryset, request, view=None, serializer=None):
|
||||
try:
|
||||
res = {}
|
||||
queryset = super().paginate_queryset(queryset, request=request, view=view)
|
||||
ser = serializer(queryset, many=True)
|
||||
res["data"] = ser.data
|
||||
res["count"] = self.page.paginator.count
|
||||
return res
|
||||
except NotFound as e:
|
||||
return {}
|
||||
55
admin-api/web/serializer.py
Normal file
@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author :mengying
|
||||
@Date :2024/6/3 17:19
|
||||
@Email : 652044581@qq.com
|
||||
@Desc : 功能描述
|
||||
"""
|
||||
from rest_framework import serializers
|
||||
|
||||
from web.models import SystemUser, SystemUserRole, SystemMenu, SystemRole, SystemDept
|
||||
|
||||
|
||||
class SystemUserSerializer(serializers.ModelSerializer):
|
||||
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class SystemDeptSerializer(serializers.ModelSerializer):
|
||||
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SystemDept
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class SystemRoleSerializer(serializers.ModelSerializer):
|
||||
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SystemRole
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class SystemMenuSerializer(serializers.ModelSerializer):
|
||||
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SystemMenu
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class SystemUserRoleSerializer(serializers.ModelSerializer):
|
||||
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SystemUserRole
|
||||
fields = '__all__'
|
||||
BIN
admin-api/web/static/fontBold.ttf
Normal file
48
admin-api/web/system/urls.py
Normal file
@ -0,0 +1,48 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from web.system import views as systemView
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
# 部门相关
|
||||
path('dept/list', systemView.DeptListView.as_view(), name="部门列表"),
|
||||
path('dept', systemView.DeptCreateView.as_view(), name="部门新增/修改"),
|
||||
re_path('dept/(?P<deptId>[0-9]+$)', systemView.DeptDetailView.as_view(), name="部门详情/删除"),
|
||||
re_path('dept/list/exclude/(?P<deptId>[0-9]+$)', systemView.DeptExcludeView.as_view(), name="部门排除查询"),
|
||||
|
||||
# 角色相关
|
||||
path('role/list', systemView.RoleListView.as_view(), name="角色列表"),
|
||||
path('role', systemView.RoleCreateView.as_view(), name="角色新增"),
|
||||
path('role/changeStatus', systemView.RoleStatusView.as_view(), name="角色状态修改"),
|
||||
re_path('role/(?P<roleId>[0-9]+$)', systemView.RoleDetailView.as_view(), name="角色新增"),
|
||||
|
||||
# 角色认证
|
||||
path('role/authUser/allocatedList', systemView.AuthUserAllocatedListView.as_view(), name="分配用户列表"),
|
||||
path('role/authUser/cancel', systemView.AuthUserCancelView.as_view(), name="角色取消人员授权"),
|
||||
path('role/authUser/cancelAll', systemView.AuthUserCancelAllView.as_view(), name="批量取消授权"),
|
||||
path('role/authUser/unallocatedList', systemView.AuthUserUnallocatedListView.as_view(), name="未授权用户列表"),
|
||||
path('role/authUser/selectAll', systemView.AuthUserSelectAllView.as_view(), name="批量绑定角色人员"),
|
||||
|
||||
# 用户相关
|
||||
re_path('user/authRole/(?P<userId>[0-9]+$)', systemView.AuthRoleView.as_view(), name="用户角色绑定"),
|
||||
path('user/authRole', systemView.AuthRoleView.as_view(), name="用户角色绑定修改"),
|
||||
path('user/list', systemView.UserListView.as_view(), name="用户列表"),
|
||||
path('user/deptTree', systemView.UserDeptTreeView.as_view(), name="用户列表"),
|
||||
path('user/changeStatus', systemView.UserStatusView.as_view(), name="用户状态修改"),
|
||||
path('user/', systemView.UserCreateView.as_view(), name="用户新增"),
|
||||
re_path('user/(?P<userId>[0-9 ,]+$)', systemView.UserDetailView.as_view(), name="用户详情/删除"),
|
||||
path('user/resetPwd', systemView.UserResetPwdView.as_view(), name="重置密码"),
|
||||
|
||||
# 个人中心
|
||||
path('user/profile/updatePwd', systemView.UserUpdatePwdView.as_view(), name="修改密码"),
|
||||
path('user/profile', systemView.UserProfileView.as_view(), name="个人中心"),
|
||||
path('user/profile/avatar', systemView.UserProfileAvatarView.as_view(), name="修改头像"),
|
||||
|
||||
# 目录相关
|
||||
path('menu/list', systemView.MenuListView.as_view(), name="目录列表"),
|
||||
re_path('menu/(?P<menuId>[0-9]+$)', systemView.MenuDetailView.as_view(), name="目录详情/删除"),
|
||||
path('menu', systemView.MenuCreateView.as_view(), name="目录新增"),
|
||||
path('menu/treeselect', systemView.MenuTreeSelectView.as_view(), name="树选择器"),
|
||||
re_path('menu/roleMenuTreeselect/(?P<roleId>[0-9]+$)', systemView.MenuRoleTreeSelectView.as_view(), name="已选择的树选择器"),
|
||||
|
||||
]
|
||||
776
admin-api/web/system/views.py
Normal file
@ -0,0 +1,776 @@
|
||||
import json
|
||||
from functools import wraps
|
||||
|
||||
from addict import Dict
|
||||
from django.db.models import Q
|
||||
from django.http.response import JsonResponse
|
||||
from django_redis import get_redis_connection
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from config import config
|
||||
from utils.myDataUtils import TreeBuilder
|
||||
from utils.myEncrypt import HashCipher
|
||||
from utils.myEnum import SystemDelEnum, SystemStatusEnum, SystemUserTypeEnum
|
||||
from utils.myResFormat import ResultJson, ResultCode
|
||||
from utils.mySnowflake import Sf
|
||||
from web.models import SystemDept, SystemUser, SystemRole, SystemUserRole, SystemMenu, SystemRoleMenu
|
||||
from web.paginator import StandardResultsSetPagination
|
||||
from web.serializer import SystemDeptSerializer, SystemUserSerializer, SystemRoleSerializer, SystemMenuSerializer
|
||||
|
||||
RedisClient = get_redis_connection()
|
||||
|
||||
|
||||
# 检验权限字符串的装饰器
|
||||
def permAuth(perKey):
|
||||
def decorator(function):
|
||||
@wraps(function)
|
||||
def decorated_function(self, *args, **kwargs):
|
||||
# 获取token中的用户权限
|
||||
user_info = {}
|
||||
for item in args:
|
||||
if isinstance(item, Request):
|
||||
user_info = item.headers.user_info
|
||||
user_permissions = user_info.get("permissions", [])
|
||||
|
||||
# 判断是否有权限
|
||||
if "*:*:*" in user_permissions or perKey in user_permissions:
|
||||
response = function(self, *args, **kwargs)
|
||||
return response
|
||||
else:
|
||||
return JsonResponse(ResultJson(ret=ResultCode.PRE_AUTH_ERROR).result)
|
||||
|
||||
return decorated_function
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def response2json(response) -> dict:
|
||||
return json.loads(response.content.decode())
|
||||
|
||||
|
||||
class DeptListView(APIView):
|
||||
"""【系统部门】列表接口"""
|
||||
|
||||
@permAuth("system:dept:list")
|
||||
def get(self, request, *args, **kwargs):
|
||||
deptName: str = request.GET.get('deptName')
|
||||
status: str = request.GET.get('status')
|
||||
|
||||
q = Q()
|
||||
q.connector = 'AND'
|
||||
|
||||
if deptName:
|
||||
q.children.append(('deptName', deptName))
|
||||
|
||||
if status:
|
||||
q.children.append(('status', status))
|
||||
|
||||
q.children.append(('delFlag', SystemDelEnum.p0.value))
|
||||
|
||||
dept = SystemDept.objects.filter(q)
|
||||
deptData = SystemDeptSerializer(instance=dept, many=True).data
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=deptData).result)
|
||||
|
||||
|
||||
class DeptCreateView(APIView):
|
||||
"""【系统部门】新增部门"""
|
||||
|
||||
@permAuth("system:dept:add")
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
data: dict = request.data
|
||||
|
||||
parentId = request.data.get('parentId')
|
||||
|
||||
dept_obj = SystemDept.objects.filter(deptId=parentId)
|
||||
if not dept_obj:
|
||||
raise Exception("未找到系统部门父级id")
|
||||
|
||||
dept_obj = dept_obj.first()
|
||||
data["ancestors"] = dept_obj.ancestors + "," + dept_obj.deptId
|
||||
data["deptId"] = Sf.generate()
|
||||
|
||||
# 判断同一父级部门下不重名
|
||||
deptNameExist = SystemDept.objects.filter(deptName=data.get("deptName"), parentId=data.get("parentId"))
|
||||
if deptNameExist:
|
||||
return JsonResponse(ResultJson(ret=ResultCode.DEPT_ERROR).result)
|
||||
|
||||
SystemDept.objects.create(**data)
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=data).result)
|
||||
|
||||
|
||||
@permAuth("system:dept:edit")
|
||||
def put(self, request, *args, **kwargs):
|
||||
data: dict = request.data
|
||||
deptId = data.get('deptId')
|
||||
|
||||
deptData = SystemDept.objects.get(deptId=deptId)
|
||||
|
||||
# 判断原始目录的父级id是否变化
|
||||
if deptData.parentId == data.get('parentId'):
|
||||
SystemDept.objects.filter(deptId=deptId).update(**data)
|
||||
else:
|
||||
# 处理本条数据的上级关联关系
|
||||
parentId = data.get('parentId')
|
||||
parentData = SystemDept.objects.get(deptId=parentId)
|
||||
repAncestors = parentData.ancestors + "," + parentId
|
||||
data['ancestors'] = repAncestors
|
||||
SystemDept.objects.filter(deptId=deptId).update(**data)
|
||||
|
||||
# 处理下级关联的路径问题
|
||||
oldAncestors = deptData.ancestors + "," + deptData.deptId
|
||||
SystemDeptDataList = SystemDept.objects.filter(ancestors__contains=oldAncestors, delFlag=SystemDelEnum.p0.value)
|
||||
for item in SystemDeptDataList:
|
||||
new_ancestors = item.ancestors
|
||||
new_ancestors = str(new_ancestors).replace(oldAncestors, repAncestors + "," + deptData.deptId)
|
||||
item.ancestors = new_ancestors
|
||||
item.save()
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class DeptDetailView(APIView):
|
||||
"""【系统部门】新增部门"""
|
||||
|
||||
@permAuth("system:dept:query")
|
||||
def get(self, request, deptId, *args, **kwargs):
|
||||
"""【系统部门】部门详情"""
|
||||
data = SystemDept.objects.get(deptId=deptId, delFlag=SystemDelEnum.p0.value)
|
||||
ser = SystemDeptSerializer(instance=data)
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=ser.data).result)
|
||||
|
||||
@permAuth("system:dept:remove")
|
||||
def delete(self, request, deptId, *args, **kwargs):
|
||||
# 更新部门状态
|
||||
SystemDept.objects.filter(deptId=deptId).update(delFlag=SystemDelEnum.p1.value)
|
||||
|
||||
# 更新人员绑定部门
|
||||
SystemUser.objects.filter(deptId=deptId).update(deptId="")
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class DeptExcludeView(APIView):
|
||||
"""【系统部门】排除查询"""
|
||||
|
||||
@permAuth("system:dept:query")
|
||||
def get(self, request, deptId, *args, **kwargs):
|
||||
# 查询部门id不是指定id或者ancestors不包含该id,且状态未删除的数据
|
||||
userData = SystemDept.objects.filter(delFlag=SystemDelEnum.p0.value, status=SystemStatusEnum.p0.value).exclude(deptId=deptId,
|
||||
ancestors__contains=deptId)
|
||||
ser = SystemDeptSerializer(instance=userData, many=True)
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=ser.data).result)
|
||||
|
||||
|
||||
class RoleListView(APIView):
|
||||
"""【系统角色】列表接口"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
roleName: str = request.GET.get('roleName')
|
||||
roleKey: str = request.GET.get('roleKey')
|
||||
status: str = request.GET.get('status')
|
||||
|
||||
q = Q()
|
||||
q.connector = 'AND'
|
||||
|
||||
if roleName:
|
||||
q.children.append(('roleName', roleName))
|
||||
|
||||
if roleKey:
|
||||
q.children.append(('roleKey', roleKey))
|
||||
|
||||
if status:
|
||||
q.children.append(('status', status))
|
||||
|
||||
q.children.append(('roleAdmin', False))
|
||||
q.children.append(('delFlag', SystemDelEnum.p0.value))
|
||||
|
||||
roleData = SystemRole.objects.filter(q).order_by('roleSort')
|
||||
|
||||
paginator = StandardResultsSetPagination()
|
||||
role_list = paginator.paginate_queryset_count(roleData, self.request, view=self, serializer=SystemRoleSerializer)
|
||||
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=role_list).result)
|
||||
|
||||
|
||||
class RoleCreateView(APIView):
|
||||
"""【系统角色】新增接口"""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
data: dict = request.data
|
||||
roleKey = data.get("roleKey")
|
||||
roleName = data.get("roleName")
|
||||
menuIds = data.pop("menuIds", [])
|
||||
roleId = Sf.generate()
|
||||
data["roleId"] = roleId
|
||||
|
||||
roleData = SystemRole.objects.filter(Q(roleKey=roleKey, delFlag=SystemDelEnum.p0.value) | Q(roleName=roleName, delFlag=SystemDelEnum.p0.value))
|
||||
|
||||
if roleData:
|
||||
return JsonResponse(ResultJson(ret=ResultCode.ROLE_ERROR).result)
|
||||
|
||||
# 删除原始的关系
|
||||
SystemRoleMenu.objects.filter(roleId=roleId).delete()
|
||||
for menuId in menuIds:
|
||||
SystemRoleMenu.objects.create(menuId=menuId, roleId=roleId)
|
||||
|
||||
# 创建角色数据
|
||||
SystemRole.objects.create(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
def put(self, request, *args, **kwargs):
|
||||
data: dict = request.data
|
||||
roleId = data.get("roleId")
|
||||
menuIds = data.pop("menuIds", [])
|
||||
roleAdmin = data.pop("roleAdmin", False)
|
||||
|
||||
# 删除原始的关系
|
||||
SystemRoleMenu.objects.filter(roleId=roleId).delete()
|
||||
for menuId in menuIds:
|
||||
SystemRoleMenu.objects.create(menuId=menuId, roleId=roleId)
|
||||
|
||||
# 更新角色数据
|
||||
SystemRole.objects.filter(roleId=roleId).update(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class RoleStatusView(APIView):
|
||||
"""【系统角色】修改状态"""
|
||||
|
||||
def put(self, request):
|
||||
data: dict = request.data
|
||||
roleId = data.get("roleId")
|
||||
SystemRole.objects.filter(roleId=roleId).update(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class RoleDetailView(APIView):
|
||||
@permAuth("system:role:query")
|
||||
def get(self, request, roleId: str):
|
||||
"""【系统角色】获取角色信息"""
|
||||
|
||||
roleData = SystemRole.objects.get(roleId=roleId)
|
||||
roleData = SystemRoleSerializer(instance=roleData).data
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=roleData).result)
|
||||
|
||||
@permAuth("system:role:remove")
|
||||
def delete(self, request, roleId: str):
|
||||
"""【系统角色】获取删除角色信息"""
|
||||
|
||||
roleIds = roleId.split(',')
|
||||
SystemRole.objects.filter(roleId__in=roleIds).update(delFlag=SystemDelEnum.p1.value)
|
||||
|
||||
SystemUserRole.objects.filter(roleId__in=roleIds).delete()
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class AuthUserCancelView(APIView):
|
||||
def put(self, request):
|
||||
"""【系统角色】取消角色人员授权"""
|
||||
# 处理角色用户关系
|
||||
roleId: str = request.data.get('roleId')
|
||||
userId: str = request.data.get('userId')
|
||||
|
||||
# 判断是批量删除还是单个删除
|
||||
SystemUserRole.objects.filter(roleId=roleId, userId=userId).delete()
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class AuthUserAllocatedListView(APIView):
|
||||
|
||||
def get(self, request):
|
||||
"""【系统角色】给角色分配人员"""
|
||||
roleId: str = request.GET.get('roleId')
|
||||
username: str = request.GET.get('username')
|
||||
phone: str = request.GET.get('phone')
|
||||
|
||||
UserRoleIds = SystemUserRole.objects.filter(roleId=roleId)
|
||||
|
||||
q = Q()
|
||||
q.connector = 'AND'
|
||||
|
||||
if username:
|
||||
q.children.append(('username', username))
|
||||
|
||||
if phone:
|
||||
q.children.append(('phone', phone))
|
||||
|
||||
q.children.append(('userId__in', [item.userId for item in UserRoleIds]))
|
||||
q.children.append(('delFlag', SystemDelEnum.p0.value))
|
||||
q.children.append(('userType', SystemUserTypeEnum.p0.value))
|
||||
|
||||
userData = SystemUser.objects.filter(q)
|
||||
|
||||
paginator = StandardResultsSetPagination()
|
||||
|
||||
# 组装人员的部门信息
|
||||
userData = paginator.paginate_queryset_count(userData, self.request, view=self, serializer=SystemUserSerializer)
|
||||
for item in userData.get("data", []):
|
||||
deptId = item.get('deptId')
|
||||
if not deptId:
|
||||
item["dept"] = {}
|
||||
else:
|
||||
deptData = SystemDept.objects.get(deptId=deptId)
|
||||
item["dept"] = SystemDeptSerializer(instance=deptData).data
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=userData).result)
|
||||
|
||||
|
||||
class AuthUserUnallocatedListView(APIView):
|
||||
|
||||
def get(self, request):
|
||||
"""【系统角色】获取未授权用户列表"""
|
||||
roleId: str = request.GET.get('roleId')
|
||||
username: str = request.GET.get('username')
|
||||
phone: str = request.GET.get('phone')
|
||||
|
||||
UserRoleIds = SystemUserRole.objects.filter(roleId=roleId)
|
||||
|
||||
q = Q()
|
||||
q.connector = 'AND'
|
||||
|
||||
if username:
|
||||
q.children.append(('username', username))
|
||||
|
||||
if phone:
|
||||
q.children.append(('phone', phone))
|
||||
|
||||
q.children.append(('delFlag', SystemDelEnum.p0.value))
|
||||
q.children.append(('userType', SystemUserTypeEnum.p0.value))
|
||||
|
||||
userData = SystemUser.objects.filter(q).exclude(userId__in=[item.userId for item in UserRoleIds])
|
||||
|
||||
paginator = StandardResultsSetPagination()
|
||||
|
||||
# 组装人员的部门信息
|
||||
userData = paginator.paginate_queryset_count(userData, self.request, view=self, serializer=SystemUserSerializer)
|
||||
for item in userData.get("data", []):
|
||||
deptId = item.get('deptId')
|
||||
if not deptId:
|
||||
item["dept"] = {}
|
||||
else:
|
||||
deptData = SystemDept.objects.get(deptId=deptId)
|
||||
item["dept"] = SystemDeptSerializer(instance=deptData).data
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=userData).result)
|
||||
|
||||
|
||||
class AuthUserSelectAllView(APIView):
|
||||
def put(self, request):
|
||||
"""【系统角色】批量绑定角色人员"""
|
||||
roleId: str = request.data.get('roleId')
|
||||
userIds: str = request.data.get('userIds')
|
||||
userIdList = userIds.split(',')
|
||||
|
||||
# 批量插入用户关系
|
||||
for userId in userIdList:
|
||||
SystemUserRole.objects.create(**dict(userId=userId, roleId=roleId))
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class AuthUserCancelAllView(APIView):
|
||||
def put(self, request):
|
||||
"""【系统角色】批量取消角色人员授权"""
|
||||
# 处理角色用户关系
|
||||
roleId: str = request.data.get('roleId')
|
||||
userIds: str = request.data.get('userIds')
|
||||
userIdList = userIds.split(',')
|
||||
|
||||
# 判断是批量删除还是单个删除
|
||||
SystemUserRole.objects.filter(roleId=roleId, userId__in=userIdList).delete()
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class UserListView(APIView):
|
||||
"""【系统用户】列表接口"""
|
||||
|
||||
@permAuth("system:user:list")
|
||||
def get(self, request):
|
||||
|
||||
deptId: str = request.GET.get('deptId')
|
||||
username: str = request.GET.get('username')
|
||||
phone: str = request.GET.get('phone')
|
||||
status: str = request.GET.get('status')
|
||||
|
||||
q = Q()
|
||||
q.connector = 'AND'
|
||||
|
||||
if deptId:
|
||||
deptData = SystemDept.objects.filter(ancestors__contains=deptId)
|
||||
deptIds = [item.deptId for item in deptData]
|
||||
deptIds.append(deptId)
|
||||
q.children.append(('deptId__in', deptIds))
|
||||
|
||||
if username:
|
||||
q.children.append(('username', username))
|
||||
|
||||
if phone:
|
||||
q.children.append(('phone', phone))
|
||||
|
||||
if status:
|
||||
q.children.append(('status', status))
|
||||
|
||||
q.children.append(('delFlag', SystemDelEnum.p0.value))
|
||||
q.children.append(('userType', SystemUserTypeEnum.p0.value))
|
||||
|
||||
userData = SystemUser.objects.filter(q)
|
||||
|
||||
# 分页
|
||||
paginator = StandardResultsSetPagination()
|
||||
userData = paginator.paginate_queryset_count(userData, self.request, view=self, serializer=SystemUserSerializer)
|
||||
|
||||
# 组装人员的部门信息
|
||||
for item in userData.get("data", []):
|
||||
deptId = item.get('deptId')
|
||||
if not deptId:
|
||||
item["dept"] = {}
|
||||
else:
|
||||
deptData = SystemDept.objects.get(deptId=deptId)
|
||||
item["dept"] = SystemDeptSerializer(instance=deptData).data
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=userData).result)
|
||||
|
||||
|
||||
class UserDeptTreeView(APIView):
|
||||
"""【系统用户】部门的结构树"""
|
||||
|
||||
@classmethod
|
||||
def deptFormat(cls, SystemDeptList):
|
||||
container = []
|
||||
for item in SystemDeptList:
|
||||
deptItem = {"id": item.get("deptId"), "parentId": item.get("parentId"), "label": item.get("deptName"), }
|
||||
container.append(deptItem)
|
||||
return container
|
||||
|
||||
def get(self, request):
|
||||
# 查询全部数据
|
||||
deptData = SystemDept.objects.filter(delFlag=SystemDelEnum.p0.value, status=SystemStatusEnum.p0.value)
|
||||
|
||||
# 组装前端所需要的数据结构
|
||||
SystemDeptList = SystemDeptSerializer(instance=deptData, many=True)
|
||||
DeptList = self.deptFormat(SystemDeptList.data)
|
||||
|
||||
# 组装成树的结构
|
||||
TreeData = TreeBuilder(DeptList).build(parentKey="parentId", ownerKey="id", topParent="0")
|
||||
TreeDataList = list(TreeData.values())
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=TreeDataList).result)
|
||||
|
||||
|
||||
class UserStatusView(APIView):
|
||||
"""【系统用户】修改用户状态"""
|
||||
|
||||
@permAuth("*:*:*")
|
||||
def put(self, request):
|
||||
data: dict = request.data
|
||||
userId = data.get("userId")
|
||||
SystemUser.objects.filter(userId=userId).update(**data)
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class UserCreateView(APIView):
|
||||
"""【系统用户】新增接口"""
|
||||
|
||||
@permAuth("*:*:*")
|
||||
def post(self, request):
|
||||
data: dict = request.data
|
||||
username = data.get("username")
|
||||
password = data.get('password', "")
|
||||
|
||||
userId = Sf.generate()
|
||||
|
||||
# 判断用户名是否重复
|
||||
userExist = SystemUser.objects.filter(username=username, delFlag=SystemDelEnum.p0.value)
|
||||
if userExist:
|
||||
return JsonResponse(ResultJson(ret=ResultCode.USER_ERROR).result)
|
||||
|
||||
# 处理角色用户关系
|
||||
roleIds = data.pop("roleIds", [])
|
||||
for roleId in roleIds:
|
||||
SystemUserRole.objects.create(**dict(roleId=roleId, userId=userId))
|
||||
|
||||
# 用户表插入数据
|
||||
user_info = request.headers.user_info
|
||||
username = user_info.get('user', {}).get('username')
|
||||
data["userId"] = userId
|
||||
data["createBy"] = username
|
||||
data["password"] = HashCipher.md5(config.ENCRYPT_STRING + password)
|
||||
SystemUser.objects.create(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
@permAuth("*:*:*")
|
||||
def put(self, request):
|
||||
"""【系统用户】修改信息"""
|
||||
# 处理角色用户关系
|
||||
data: dict = request.data
|
||||
|
||||
userId = data.get('userId')
|
||||
roleIds = data.pop("roleIds", [])
|
||||
|
||||
# 删除以前的角色人员关系,在增加新的关系
|
||||
SystemUserRole.objects.filter(userId=userId).delete()
|
||||
for roleId in roleIds:
|
||||
SystemUserRole.objects.create(**dict(roleId=roleId, userId=userId))
|
||||
|
||||
SystemUser.objects.filter(userId=userId).update(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
@permAuth("*:*:*")
|
||||
def get(self, request):
|
||||
"""【系统用户】获取现有的角色列表"""
|
||||
res = Dict()
|
||||
roleData = SystemRole.objects.filter(status=SystemStatusEnum.p0.value, delFlag=SystemDelEnum.p0.value, roleAdmin=False)
|
||||
res.roles = SystemRoleSerializer(instance=roleData, many=True).data
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=res).result)
|
||||
|
||||
|
||||
class UserDetailView(APIView):
|
||||
@permAuth("system:user:query")
|
||||
def get(self, request, userId: str):
|
||||
"""【系统用户】获取用户信息"""
|
||||
res = Dict()
|
||||
|
||||
# 找到用户绑定的角色id
|
||||
userData = SystemUser.objects.get(userId=userId)
|
||||
|
||||
userRoleData = SystemUserRole.objects.filter(userId=userId)
|
||||
roleIds = [item.roleId for item in userRoleData]
|
||||
roleData = SystemRole.objects.filter(status=SystemStatusEnum.p0.value, roleAdmin=False, delFlag=SystemDelEnum.p0.value)
|
||||
|
||||
# 组装数据
|
||||
res.user = SystemUserSerializer(instance=userData).data
|
||||
res.roleIds = roleIds
|
||||
res.roles = SystemRoleSerializer(instance=roleData, many=True).data
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=res).result)
|
||||
|
||||
@permAuth("system:user:remove")
|
||||
def delete(self, request, userId: str):
|
||||
"""【系统用户】获取删除用户信息"""
|
||||
|
||||
# 分割路径的userId,变为list
|
||||
userIds = userId.split(',')
|
||||
SystemUser.objects.filter(userId__in=userIds).update(delFlag=SystemDelEnum.p1.value)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class UserProfileView(APIView):
|
||||
|
||||
def get(self, request):
|
||||
"""【个人中心】获取信息"""
|
||||
user_info = request.headers.user_info
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=user_info).result)
|
||||
|
||||
def put(self, request):
|
||||
"""【个人中心】修改"""
|
||||
data: dict = request.data
|
||||
user_info = request.headers.user_info
|
||||
userId = user_info.get('user', {}).get('userId')
|
||||
SystemUser.objects.filter(userId=userId).update(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class UserProfileAvatarView(APIView):
|
||||
|
||||
def post(self, request):
|
||||
"""【个人中心】修改头像"""
|
||||
avatar = request.Files.get("avatarfile")
|
||||
user_info = request.headers.user_info
|
||||
userId = user_info.get('user', {}).get('userId')
|
||||
|
||||
# TODO: 上传头像文件流,生成一个url,更新用户信息的头像字段(没有obs, 功能未作)
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=user_info).result)
|
||||
|
||||
|
||||
class UserResetPwdView(APIView):
|
||||
@permAuth("*:*:*")
|
||||
def put(self, request):
|
||||
"""【系统用户】重置密码/ 需要管理员权限"""
|
||||
data: dict = request.data
|
||||
password = data.get('password')
|
||||
userId = data.get('userId')
|
||||
|
||||
# 修改密码
|
||||
password_md5 = HashCipher.md5(config.ENCRYPT_STRING + password)
|
||||
|
||||
SystemUser.objects.filter(userId=userId).update(password=password_md5)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class UserUpdatePwdView(APIView):
|
||||
def put(self, request):
|
||||
"""【系统用户】更新密码"""
|
||||
oldPassword = request.GET.get('oldPassword')
|
||||
newPassword = request.GET.get('newPassword')
|
||||
|
||||
# 判断旧密码是否正确
|
||||
user_info = request.headers.user_info
|
||||
userId = user_info.get('user', {}).get('userId', "")
|
||||
|
||||
# 对密码进行加密
|
||||
old_encrypt_password = HashCipher.md5(config.ENCRYPT_STRING + str(oldPassword))
|
||||
new_encrypt_password = HashCipher.md5(config.ENCRYPT_STRING + str(newPassword))
|
||||
|
||||
userData = SystemUser.objects.filter(password=old_encrypt_password, userId=userId)
|
||||
|
||||
if not userData:
|
||||
return JsonResponse(ResultJson(ret=ResultCode.PASSWORD_ERROR).result)
|
||||
else:
|
||||
SystemUser.objects.filter(userId=userId).update(password=new_encrypt_password)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class AuthRoleView(APIView):
|
||||
|
||||
def get(self, request, userId: str):
|
||||
"""【系统用户】查询角色映射关系"""
|
||||
# 查询用户信息
|
||||
userData = SystemUser.objects.get(userId=userId)
|
||||
|
||||
# 查询用户角色映射关系
|
||||
userRoleList = SystemUserRole.objects.filter(userId=userId)
|
||||
|
||||
# 查询角色信息
|
||||
roleIds = [item.roleId for item in userRoleList]
|
||||
roleData = SystemRole.objects.filter(roleId__in=roleIds)
|
||||
|
||||
# 序列化
|
||||
userData = SystemUserSerializer(instance=userData).data
|
||||
roleData = SystemRoleSerializer(instance=roleData, many=True).data
|
||||
|
||||
# 组装返回数据
|
||||
resData = dict(user=userData, roles=roleData)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=resData).result)
|
||||
|
||||
def put(self, request):
|
||||
"""【系统用户】修改角色映射关系"""
|
||||
userId = request.GET.get("userId")
|
||||
roleIds = request.GET.get("roleIds")
|
||||
roleIds = roleIds.split(",")
|
||||
|
||||
# 删除以前的角色人员关系,在增加新的关系
|
||||
SystemUserRole.objects.filter(userId=userId).delete()
|
||||
for roleId in roleIds:
|
||||
SystemUserRole.objects.create(**dict(roleId=roleId, userId=userId))
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=None).result)
|
||||
|
||||
|
||||
class MenuDetailView(APIView):
|
||||
@permAuth("system:menu:query")
|
||||
def get(self, request, menuId: str):
|
||||
"""【系统用户】查询"""
|
||||
menuData = SystemMenu.objects.get(menuId=menuId)
|
||||
menuData = SystemMenuSerializer(instance=menuData).data
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=menuData).result)
|
||||
|
||||
@permAuth("system:menu:remove")
|
||||
def delete(self, request, menuId: str):
|
||||
"""【系统用户】删除"""
|
||||
|
||||
# 删除目录信息
|
||||
SystemMenu.objects.filter(menuId=menuId).delete()
|
||||
|
||||
# 删除角色目录映射
|
||||
SystemRoleMenu.objects.filter(menuId=menuId).delete()
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class MenuListView(APIView):
|
||||
|
||||
@permAuth("system:menu:list")
|
||||
def get(self, request):
|
||||
"""【系统目录】列表"""
|
||||
menuName: str = request.GET.get('menuName')
|
||||
status: str = request.GET.get('status')
|
||||
|
||||
q = Q()
|
||||
q.connector = 'AND'
|
||||
|
||||
if menuName:
|
||||
q.children.append(('menuName', menuName))
|
||||
|
||||
if status:
|
||||
q.children.append(('status', status))
|
||||
|
||||
q.children.append(('visible', SystemDelEnum.p0.value))
|
||||
|
||||
# 查询数据列表
|
||||
menuData = SystemMenu.objects.filter(q).order_by('orderNum')
|
||||
menuData = SystemMenuSerializer(instance=menuData, many=True).data
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=menuData).result)
|
||||
|
||||
|
||||
class MenuCreateView(APIView):
|
||||
|
||||
@permAuth("system:menu:add")
|
||||
def post(self, request):
|
||||
"""【系统目录】新增"""
|
||||
data: dict = request.data
|
||||
parentId = data.get("parentId")
|
||||
menuName = data.get("menuName")
|
||||
# 生成父级数据
|
||||
data["menuId"] = Sf.generate()
|
||||
|
||||
menuExist = SystemMenu.objects.filter(parentId=parentId, menuName=menuName)
|
||||
|
||||
if menuExist:
|
||||
return JsonResponse(ResultJson(ret=ResultCode.MENU_ERROR).result)
|
||||
|
||||
SystemMenu.objects.create(**data)
|
||||
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=data).result)
|
||||
|
||||
@permAuth("system:menu:edit")
|
||||
def put(self, request):
|
||||
"""【系统目录】修改"""
|
||||
data: dict = request.data
|
||||
menuId = data.get('menuId')
|
||||
SystemMenu.objects.filter(menuId=menuId).update(**data)
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class MenuTreeSelectView(APIView):
|
||||
"""【系统目录】树选择"""
|
||||
|
||||
@classmethod
|
||||
def MenuFormat(cls, MenuData):
|
||||
container = []
|
||||
for item in MenuData:
|
||||
deptItem = {"id": item.get("menuId"), "parentId": item.get("parentId"), "label": item.get("menuName"), }
|
||||
container.append(deptItem)
|
||||
return container
|
||||
|
||||
def get(self, request):
|
||||
menuData = SystemMenu.objects.filter(status=SystemStatusEnum.p0.value)
|
||||
menuData = SystemMenuSerializer(instance=menuData, many=True).data
|
||||
|
||||
# 组装前端所需要的数据结构
|
||||
MenuList = self.MenuFormat(menuData)
|
||||
|
||||
# 组装成树的结构
|
||||
TreeData = TreeBuilder(MenuList).build(parentKey="parentId", ownerKey="id", topParent="0")
|
||||
TreeDataList = list(TreeData.values())
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=TreeDataList).result)
|
||||
|
||||
|
||||
class MenuRoleTreeSelectView(APIView):
|
||||
"""【系统目录】已经选择树选择器"""
|
||||
|
||||
def get(self, request, roleId: str):
|
||||
res = Dict()
|
||||
|
||||
# 查询树的列表
|
||||
TreeDataListResponse = MenuTreeSelectView().get(request)
|
||||
TreeDataList = response2json(TreeDataListResponse).get("data", [])
|
||||
|
||||
# 查询选中数据的
|
||||
res.menus = TreeDataList
|
||||
roleMenuData = SystemRoleMenu.objects.filter(roleId=roleId)
|
||||
|
||||
res.checkedKeys = [item.menuId for item in roleMenuData]
|
||||
return JsonResponse(ResultJson(ret=ResultCode.SUCCESS, data=res.to_dict()).result)
|
||||
3
admin-api/web/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
10
admin-api/web/urls.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.urls import path
|
||||
from web import views as webView
|
||||
|
||||
urlpatterns = [
|
||||
path('captchaImage', webView.CaptchaImageView.as_view(), name="获取验证码"),
|
||||
path('login', webView.LoginView.as_view(), name="登录"),
|
||||
path('logout', webView.LogoutView.as_view(), name="退出登录"),
|
||||
path('getInfo', webView.GetInfoView.as_view(), name="获取登陆信息"),
|
||||
path('getRouters', webView.GetRoutersView.as_view(), name="获取用户权限路由"),
|
||||
]
|
||||
199
admin-api/web/views.py
Normal file
@ -0,0 +1,199 @@
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from addict import Dict
|
||||
from django.http.response import JsonResponse
|
||||
from django_redis import get_redis_connection
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from config import config
|
||||
from utils.myDataUtils import TreeBuilder
|
||||
from utils.myEncrypt import HashCipher
|
||||
from utils.myEnum import SystemDelEnum, SystemStatusEnum
|
||||
from utils.myResFormat import ResultJson, ResultCode
|
||||
from utils.myTimeFormat import MyTimeUtils
|
||||
from web.captchaImage import CaptchaImage
|
||||
from web.models import SystemUser, SystemUserRole, SystemRole, SystemRoleMenu, SystemMenu
|
||||
from web.serializer import SystemUserSerializer, SystemMenuSerializer, SystemRoleSerializer
|
||||
|
||||
RedisClient = get_redis_connection()
|
||||
|
||||
|
||||
class CaptchaImageView(APIView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""【系统用户】获取验证码"""
|
||||
captcha = CaptchaImage()
|
||||
|
||||
# 生成验证码图片和字符串
|
||||
captcha_string, image_base64 = captcha.generate(width=120, height=40)
|
||||
|
||||
res = Dict()
|
||||
uid = uuid.uuid4().hex
|
||||
res.img = image_base64
|
||||
res.uuid = uid
|
||||
res.captchaEnabled = True
|
||||
|
||||
RedisClient.set(uid, captcha_string, ex=5 * 60)
|
||||
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=res.to_dict()).result)
|
||||
|
||||
|
||||
class LoginView(APIView):
|
||||
"登录"
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
username = request.data.get("username", "")
|
||||
password = request.data.get("password", "")
|
||||
|
||||
# 如果有验证码的情况,校验方式
|
||||
uid = request.data.get("uuid", "")
|
||||
code = request.data.get("code", "")
|
||||
|
||||
# 密码加密
|
||||
password = HashCipher.md5(config.ENCRYPT_STRING + str(password))
|
||||
|
||||
if uid:
|
||||
authCode = RedisClient.get(uid)
|
||||
if not authCode:
|
||||
return JsonResponse(ResultJson(ResultCode.AUTH_CODE_EXP).result)
|
||||
else:
|
||||
if str(authCode).strip() != str(code).strip():
|
||||
return JsonResponse(ResultJson(ResultCode.AUTH_CODE_ERROR).result)
|
||||
|
||||
Users = SystemUser.objects.filter(username=username, password=password)
|
||||
if not Users:
|
||||
return JsonResponse(ResultJson(ResultCode.PASSWORD_ERROR).result)
|
||||
|
||||
User = Users.first()
|
||||
user_data = SystemUserSerializer(instance=User).data
|
||||
|
||||
# 用户是否被删除
|
||||
if User.delFlag == SystemDelEnum.p1.value:
|
||||
return JsonResponse(ResultJson(ResultCode.USER_NOT_EXIST).result)
|
||||
|
||||
# 用户是否被禁用
|
||||
if User.status == SystemStatusEnum.p1.value:
|
||||
return JsonResponse(ResultJson(ResultCode.USER_NOT_ALLOW).result)
|
||||
|
||||
# 更新用户的登陆信息
|
||||
User.loginIp = request.META.get("REMOTE_ADDR", "")
|
||||
User.loginDate = MyTimeUtils.TimeFormat()
|
||||
User.save()
|
||||
|
||||
# 获取角色信息
|
||||
token = uuid.uuid4().hex
|
||||
userInfo = Dict()
|
||||
|
||||
# 找到用户id和角色的映射
|
||||
UserRoleMap = SystemUserRole.objects.filter(userId=User.userId)
|
||||
|
||||
RoleDataList = SystemRole.objects.filter(roleId__in=[roleItem.roleId for roleItem in UserRoleMap], delFlag=SystemDelEnum.p0.value,
|
||||
status=SystemStatusEnum.p0.value)
|
||||
|
||||
userInfo.user = user_data
|
||||
userInfo.roles = [role.roleKey for role in RoleDataList]
|
||||
user_data["role"] = SystemRoleSerializer(instance=RoleDataList, many=True).data
|
||||
|
||||
if any([role.roleAdmin for role in RoleDataList]):
|
||||
userInfo.permissions = ["*:*:*"]
|
||||
else:
|
||||
|
||||
roleIds = list(set([item.roleId for item in RoleDataList]))
|
||||
|
||||
# 对目录权限进行去重
|
||||
RoleMenus = SystemRoleMenu.objects.filter(roleId__in=roleIds)
|
||||
|
||||
menuIds = list(set([item.menuId for item in RoleMenus]))
|
||||
|
||||
Menus = SystemMenu.objects.filter(menuId__in=menuIds, status=SystemStatusEnum.p0.value)
|
||||
|
||||
userInfo.permissions = [item.perms for item in Menus]
|
||||
|
||||
RedisClient.set(token, json.dumps(userInfo.to_dict()), ex=60 * 60)
|
||||
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=token).result)
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
"退出登录"
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
token = request.headers.user_info.get("token")
|
||||
|
||||
# token不存在的情况
|
||||
if not token:
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS).result)
|
||||
|
||||
RedisClient.delete(token)
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS).result)
|
||||
|
||||
|
||||
class GetInfoView(APIView):
|
||||
"获取登陆信息"
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user_info = request.headers.user_info
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=user_info).result)
|
||||
|
||||
|
||||
class GetRoutersView(APIView):
|
||||
"获取用户权限路由"
|
||||
|
||||
def routerFormat(self, MenuData):
|
||||
# 目录数据组装前端的路由格式
|
||||
container = []
|
||||
for item in MenuData:
|
||||
# 组装目录
|
||||
if item.get('menuType') == 'M':
|
||||
|
||||
# 判断是否外链接
|
||||
if item.get('isFrame') == "0":
|
||||
menuItem = {"id": item.get('menuId'), "parentId": item.get('parentId'), "component": "Layout", "hidden": bool(int(item.get('visible'))),
|
||||
"path": item.get('path'), "name": item.get('path'),
|
||||
"meta": {"icon": item.get('icon'), "link": item.get('path'), "noCache": bool(int(item.get('isCache'))),
|
||||
"title": item.get('menuName'), }
|
||||
|
||||
}
|
||||
else:
|
||||
menuItem = {"id": item.get('menuId'), "parentId": item.get('parentId'), "component": "Layout", "hidden": bool(int(item.get('visible'))),
|
||||
"path": "/" + item.get('path'), "name": str(item.get('path')).capitalize(), "redirect": "noRedirect",
|
||||
"meta": {"icon": item.get('icon'), "link": "", "noCache": bool(int(item.get('isCache'))), "title": item.get('menuName'), }}
|
||||
container.append(menuItem)
|
||||
|
||||
# 处理组件形式
|
||||
elif item.get('menuType') == 'C':
|
||||
menuItem = {"id": item.get('menuId'), "parentId": item.get('parentId'), "component": item.get('component'),
|
||||
"hidden": bool(int(item.get('visible'))), "path": item.get('path'), "name": str(item.get('path')).capitalize(),
|
||||
"redirect": "noRedirect",
|
||||
"meta": {"icon": item.get('icon'), "link": "", "noCache": bool(int(item.get('isCache'))), "title": item.get('menuName'), }}
|
||||
container.append(menuItem)
|
||||
|
||||
# 基础数据组装后,组装成树结构
|
||||
treeData = TreeBuilder(container).build(parentKey="parentId", ownerKey="id", topParent="0")
|
||||
return list(treeData.values()) if treeData else []
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
user_info = request.headers.user_info
|
||||
|
||||
# 获取用户的角色信息
|
||||
roleData = user_info.get("user", {}).get("role", [])
|
||||
roleAdminList = [roleItem.get("roleAdmin") for roleItem in roleData]
|
||||
|
||||
if any(roleAdminList):
|
||||
Menus = SystemMenu.objects.all()
|
||||
else:
|
||||
roleIds = list(set([item.get("roleId") for item in roleData]))
|
||||
|
||||
# 对目录权限进行去重
|
||||
RoleMenus = SystemRoleMenu.objects.filter(roleId__in=roleIds)
|
||||
|
||||
menuIds = list(set([item.menuId for item in RoleMenus]))
|
||||
|
||||
Menus = SystemMenu.objects.filter(menuId__in=menuIds, status=SystemStatusEnum.p0.value)
|
||||
|
||||
MenuData = SystemMenuSerializer(instance=Menus, many=True).data
|
||||
routerInfo = self.routerFormat(MenuData)
|
||||
return JsonResponse(ResultJson(ResultCode.SUCCESS, data=routerInfo).result)
|
||||
9
admin-ui/.env.development
Normal file
@ -0,0 +1,9 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 若依后台管理系统
|
||||
|
||||
# 开发环境配置
|
||||
VITE_APP_ENV = 'development'
|
||||
|
||||
# 若依管理系统/开发环境
|
||||
VITE_APP_BASE_API = '/dev-api'
|
||||
|
||||
11
admin-ui/.env.production
Normal file
@ -0,0 +1,11 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = '若依管理系统'
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV = 'production'
|
||||
|
||||
# 若依管理系统/生产环境
|
||||
VITE_APP_BASE_API = 'http://124.71.212.219:8090/api'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
11
admin-ui/.env.staging
Normal file
@ -0,0 +1,11 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 若依后台管理
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV = 'staging'
|
||||
|
||||
# 若依管理系统/生产环境
|
||||
VITE_APP_BASE_API = '/stage-api'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
23
admin-ui/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
4
admin-ui/Dockerfile
Normal file
@ -0,0 +1,4 @@
|
||||
FROM nginx:latest
|
||||
WORKDIR /app
|
||||
COPY ./dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
0
admin-ui/LICENSE
Normal file
4
admin-ui/build.sh
Normal file
@ -0,0 +1,4 @@
|
||||
git pull
|
||||
yarn run build:prod
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
18
admin-ui/docker-compose.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
version: '3.0'
|
||||
|
||||
services:
|
||||
|
||||
django-vue-ui-std:
|
||||
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
||||
image: django-vue-ui-image-std
|
||||
|
||||
container_name: django-vue-ui-container-std
|
||||
|
||||
ports:
|
||||
- "8101:80"
|
||||
|
||||
restart: always
|
||||
46
admin-ui/html/ie.html
Normal file
216
admin-ui/index.html
Normal file
@ -0,0 +1,216 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<title>后台管理系统</title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.chromeframe {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
#loader-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
#loader {
|
||||
display: block;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
-ms-animation: spin 2s linear infinite;
|
||||
-moz-animation: spin 2s linear infinite;
|
||||
-o-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
#loader:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
-webkit-animation: spin 3s linear infinite;
|
||||
-moz-animation: spin 3s linear infinite;
|
||||
-o-animation: spin 3s linear infinite;
|
||||
-ms-animation: spin 3s linear infinite;
|
||||
animation: spin 3s linear infinite;
|
||||
}
|
||||
|
||||
#loader:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
-moz-animation: spin 1.5s linear infinite;
|
||||
-o-animation: spin 1.5s linear infinite;
|
||||
-ms-animation: spin 1.5s linear infinite;
|
||||
-webkit-animation: spin 1.5s linear infinite;
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#loader-wrapper .loader-section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 51%;
|
||||
height: 100%;
|
||||
background: #7171C6;
|
||||
z-index: 1000;
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-left {
|
||||
-webkit-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-right {
|
||||
-webkit-transform: translateX(100%);
|
||||
-ms-transform: translateX(100%);
|
||||
transform: translateX(100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.loaded #loader {
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper {
|
||||
visibility: hidden;
|
||||
-webkit-transform: translateY(-100%);
|
||||
-ms-transform: translateY(-100%);
|
||||
transform: translateY(-100%);
|
||||
-webkit-transition: all 0.3s 1s ease-out;
|
||||
transition: all 0.3s 1s ease-out;
|
||||
}
|
||||
|
||||
.no-js #loader-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-js h1 {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
#loader-wrapper .load_title {
|
||||
font-family: 'Open Sans';
|
||||
color: #FFF;
|
||||
font-size: 19px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
z-index: 9999999999999;
|
||||
position: absolute;
|
||||
top: 60%;
|
||||
opacity: 1;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
#loader-wrapper .load_title span {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 13px;
|
||||
color: #FFF;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="loader-wrapper">
|
||||
<div id="loader"></div>
|
||||
<div class="loader-section section-left"></div>
|
||||
<div class="loader-section section-right"></div>
|
||||
<div class="load_title">系统加载中,请稍后.......</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.js">
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
45
admin-ui/package.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "ruoyi",
|
||||
"version": "3.8.7",
|
||||
"description": "若依管理系统",
|
||||
"author": "若依",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:prod": "vite build --mode production",
|
||||
"build:stage": "vite build --mode staging",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "10.6.1",
|
||||
"axios": "0.27.2",
|
||||
"echarts": "5.4.3",
|
||||
"element-plus": "2.4.3",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.6.2",
|
||||
"js-cookie": "3.0.5",
|
||||
"jsencrypt": "3.3.2",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"vue": "3.3.9",
|
||||
"vue-cropper": "1.1.1",
|
||||
"vue-router": "4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "4.5.0",
|
||||
"@vue/compiler-sfc": "3.3.9",
|
||||
"sass": "1.69.5",
|
||||
"unplugin-auto-import": "0.17.1",
|
||||
"vite": "5.0.4",
|
||||
"vite-plugin-compression": "0.5.1",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.0"
|
||||
}
|
||||
}
|
||||
BIN
admin-ui/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
15
admin-ui/src/App.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
// 初始化主题样式
|
||||
handleThemeStyle(useSettingsStore().theme)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
51
admin-ui/src/api/login.js
Normal file
@ -0,0 +1,51 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
url: '/login',
|
||||
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data) {
|
||||
return request({
|
||||
url: '/register',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url: '/getInfo',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return request({
|
||||
url: '/captchaImage',
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
9
admin-ui/src/api/menu.js
Normal file
@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取路由
|
||||
export const getRouters = () => {
|
||||
return request({
|
||||
url: '/getRouters',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
57
admin-ui/src/api/monitor/cache.js
Normal file
@ -0,0 +1,57 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询缓存详细
|
||||
export function getCache() {
|
||||
return request({
|
||||
url: '/monitor/cache',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询缓存名称列表
|
||||
export function listCacheName() {
|
||||
return request({
|
||||
url: '/monitor/cache/getNames',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询缓存键名列表
|
||||
export function listCacheKey(cacheName) {
|
||||
return request({
|
||||
url: '/monitor/cache/getKeys/' + cacheName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询缓存内容
|
||||
export function getCacheValue(cacheName, cacheKey) {
|
||||
return request({
|
||||
url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理指定名称缓存
|
||||
export function clearCacheName(cacheName) {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheName/' + cacheName,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理指定键名缓存
|
||||
export function clearCacheKey(cacheKey) {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheKey/' + cacheKey,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理全部缓存
|
||||
export function clearCacheAll() {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheAll',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
71
admin-ui/src/api/monitor/job.js
Normal file
@ -0,0 +1,71 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询定时任务调度列表
|
||||
export function listJob(query) {
|
||||
return request({
|
||||
url: '/monitor/job/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询定时任务调度详细
|
||||
export function getJob(jobId) {
|
||||
return request({
|
||||
url: '/monitor/job/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增定时任务调度
|
||||
export function addJob(data) {
|
||||
return request({
|
||||
url: '/monitor/job',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改定时任务调度
|
||||
export function updateJob(data) {
|
||||
return request({
|
||||
url: '/monitor/job',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除定时任务调度
|
||||
export function delJob(jobId) {
|
||||
return request({
|
||||
url: '/monitor/job/' + jobId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 任务状态修改
|
||||
export function changeJobStatus(jobId, status) {
|
||||
const data = {
|
||||
jobId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/monitor/job/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 定时任务立即执行一次
|
||||
export function runJob(jobId, jobGroup) {
|
||||
const data = {
|
||||
jobId,
|
||||
jobGroup
|
||||
}
|
||||
return request({
|
||||
url: '/monitor/job/run',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
26
admin-ui/src/api/monitor/jobLog.js
Normal file
@ -0,0 +1,26 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询调度日志列表
|
||||
export function listJobLog(query) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除调度日志
|
||||
export function delJobLog(jobLogId) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/' + jobLogId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空调度日志
|
||||
export function cleanJobLog() {
|
||||
return request({
|
||||
url: '/monitor/jobLog/clean',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
34
admin-ui/src/api/monitor/logininfor.js
Normal file
@ -0,0 +1,34 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询登录日志列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除登录日志
|
||||
export function delLogininfor(infoId) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/' + infoId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 解锁用户登录状态
|
||||
export function unlockLogininfor(userName) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/unlock/' + userName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空登录日志
|
||||
export function cleanLogininfor() {
|
||||
return request({
|
||||
url: '/monitor/logininfor/clean',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
18
admin-ui/src/api/monitor/online.js
Normal file
@ -0,0 +1,18 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询在线用户列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/online/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 强退用户
|
||||
export function forceLogout(tokenId) {
|
||||
return request({
|
||||
url: '/monitor/online/' + tokenId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
26
admin-ui/src/api/monitor/operlog.js
Normal file
@ -0,0 +1,26 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询操作日志列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/operlog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除操作日志
|
||||
export function delOperlog(operId) {
|
||||
return request({
|
||||
url: '/monitor/operlog/' + operId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空操作日志
|
||||
export function cleanOperlog() {
|
||||
return request({
|
||||
url: '/monitor/operlog/clean',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
9
admin-ui/src/api/monitor/server.js
Normal file
@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取服务信息
|
||||
export function getServer() {
|
||||
return request({
|
||||
url: '/monitor/server',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
60
admin-ui/src/api/system/config.js
Normal file
@ -0,0 +1,60 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询参数列表
|
||||
export function listConfig(query) {
|
||||
return request({
|
||||
url: '/system/config/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询参数详细
|
||||
export function getConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据参数键名查询参数值
|
||||
export function getConfigKey(configKey) {
|
||||
return request({
|
||||
url: '/system/config/configKey/' + configKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增参数配置
|
||||
export function addConfig(data) {
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改参数配置
|
||||
export function updateConfig(data) {
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除参数配置
|
||||
export function delConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新参数缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/config/refreshCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
52
admin-ui/src/api/system/dept.js
Normal file
@ -0,0 +1,52 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询部门列表
|
||||
export function listDept(query) {
|
||||
return request({
|
||||
url: '/system/dept/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门列表(排除节点)
|
||||
export function listDeptExcludeChild(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/list/exclude/' + deptId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门详细
|
||||
export function getDept(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/' + deptId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增部门
|
||||
export function addDept(data) {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改部门
|
||||
export function updateDept(data) {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除部门
|
||||
export function delDept(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/' + deptId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
52
admin-ui/src/api/system/dict/data.js
Normal file
@ -0,0 +1,52 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典数据列表
|
||||
export function listData(query) {
|
||||
return request({
|
||||
url: '/system/dict/data/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典数据详细
|
||||
export function getData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts(dictType) {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + dictType,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export function addData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export function updateData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export function delData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
60
admin-ui/src/api/system/dict/type.js
Normal file
@ -0,0 +1,60 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典类型列表
|
||||
export function listType(query) {
|
||||
return request({
|
||||
url: '/system/dict/type/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典类型详细
|
||||
export function getType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典类型
|
||||
export function addType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典类型
|
||||
export function updateType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典类型
|
||||
export function delType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新字典缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/dict/type/refreshCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取字典选择框列表
|
||||
export function optionselect() {
|
||||
return request({
|
||||
url: '/system/dict/type/optionselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
60
admin-ui/src/api/system/menu.js
Normal file
@ -0,0 +1,60 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询菜单列表
|
||||
export function listMenu(query) {
|
||||
return request({
|
||||
url: '/system/menu/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询菜单详细
|
||||
export function getMenu(menuId) {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询菜单下拉树结构
|
||||
export function treeselect() {
|
||||
return request({
|
||||
url: '/system/menu/treeselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色ID查询菜单下拉树结构
|
||||
export function roleMenuTreeselect(roleId) {
|
||||
return request({
|
||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增菜单
|
||||
export function addMenu(data) {
|
||||
return request({
|
||||
url: '/system/menu',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改菜单
|
||||
export function updateMenu(data) {
|
||||
return request({
|
||||
url: '/system/menu',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除菜单
|
||||
export function delMenu(menuId) {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
admin-ui/src/api/system/notice.js
Normal file
@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询公告列表
|
||||
export function listNotice(query) {
|
||||
return request({
|
||||
url: '/system/notice/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询公告详细
|
||||
export function getNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增公告
|
||||
export function addNotice(data) {
|
||||
return request({
|
||||
url: '/system/notice',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改公告
|
||||
export function updateNotice(data) {
|
||||
return request({
|
||||
url: '/system/notice',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除公告
|
||||
export function delNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
admin-ui/src/api/system/post.js
Normal file
@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询岗位列表
|
||||
export function listPost(query) {
|
||||
return request({
|
||||
url: '/system/post/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询岗位详细
|
||||
export function getPost(postId) {
|
||||
return request({
|
||||
url: '/system/post/' + postId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增岗位
|
||||
export function addPost(data) {
|
||||
return request({
|
||||
url: '/system/post',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改岗位
|
||||
export function updatePost(data) {
|
||||
return request({
|
||||
url: '/system/post',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除岗位
|
||||
export function delPost(postId) {
|
||||
return request({
|
||||
url: '/system/post/' + postId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
119
admin-ui/src/api/system/role.js
Normal file
@ -0,0 +1,119 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询角色列表
|
||||
export function listRole(query) {
|
||||
return request({
|
||||
url: '/system/role/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色详细
|
||||
export function getRole(roleId) {
|
||||
return request({
|
||||
url: '/system/role/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增角色
|
||||
export function addRole(data) {
|
||||
return request({
|
||||
url: '/system/role',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改角色
|
||||
export function updateRole(data) {
|
||||
return request({
|
||||
url: '/system/role',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 角色数据权限
|
||||
export function dataScope(data) {
|
||||
return request({
|
||||
url: '/system/role/dataScope',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 角色状态修改
|
||||
export function changeRoleStatus(roleId, status) {
|
||||
const data = {
|
||||
roleId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/system/role/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
export function delRole(roleId) {
|
||||
return request({
|
||||
url: '/system/role/' + roleId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色已授权用户列表
|
||||
export function allocatedUserList(query) {
|
||||
return request({
|
||||
url: '/system/role/authUser/allocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色未授权用户列表
|
||||
export function unallocatedUserList(query) {
|
||||
return request({
|
||||
url: '/system/role/authUser/unallocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 取消用户授权角色
|
||||
export function authUserCancel(data) {
|
||||
return request({
|
||||
url: '/system/role/authUser/cancel',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 批量取消用户授权角色
|
||||
export function authUserCancelAll(data) {
|
||||
return request({
|
||||
url: '/system/role/authUser/cancelAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 授权用户选择
|
||||
export function authUserSelectAll(data) {
|
||||
return request({
|
||||
url: '/system/role/authUser/selectAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色ID查询部门树结构
|
||||
export function deptTreeSelect(roleId) {
|
||||
return request({
|
||||
url: '/system/role/deptTree/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
135
admin-ui/src/api/system/user.js
Normal file
@ -0,0 +1,135 @@
|
||||
import request from '@/utils/request'
|
||||
import { parseStrEmpty } from "@/utils/ruoyi";
|
||||
|
||||
// 查询用户列表
|
||||
export function listUser(query) {
|
||||
return request({
|
||||
url: '/system/user/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户详细
|
||||
export function getUser(userId) {
|
||||
return request({
|
||||
url: '/system/user/' + parseStrEmpty(userId),
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url: '/system/user/',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户
|
||||
export function updateUser(data) {
|
||||
return request({
|
||||
url: '/system/user/',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function delUser(userId) {
|
||||
return request({
|
||||
url: '/system/user/' + userId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function resetUserPwd(userId, password) {
|
||||
const data = {
|
||||
userId,
|
||||
password
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/resetPwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户状态修改
|
||||
export function changeUserStatus(userId, status) {
|
||||
const data = {
|
||||
userId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户个人信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户个人信息
|
||||
export function updateUserProfile(data) {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询授权角色
|
||||
export function getAuthRole(userId) {
|
||||
return request({
|
||||
url: '/system/user/authRole/' + userId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 保存授权角色
|
||||
export function updateAuthRole(data) {
|
||||
return request({
|
||||
url: '/system/user/authRole',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门下拉树结构
|
||||
export function deptTreeSelect() {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
76
admin-ui/src/api/tool/gen.js
Normal file
@ -0,0 +1,76 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询生成表数据
|
||||
export function listTable(query) {
|
||||
return request({
|
||||
url: '/tool/gen/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询db数据库列表
|
||||
export function listDbTable(query) {
|
||||
return request({
|
||||
url: '/tool/gen/db/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询表详细信息
|
||||
export function getGenTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改代码生成信息
|
||||
export function updateGenTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 导入表
|
||||
export function importTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/importTable',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 预览生成代码
|
||||
export function previewTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/preview/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除表数据
|
||||
export function delTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成代码(自定义路径)
|
||||
export function genCode(tableName) {
|
||||
return request({
|
||||
url: '/tool/gen/genCode/' + tableName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 同步数据库
|
||||
export function synchDb(tableName) {
|
||||
return request({
|
||||
url: '/tool/gen/synchDb/' + tableName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
1
admin-ui/src/assets/icons/svg/404.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
admin-ui/src/assets/icons/svg/bug.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
admin-ui/src/assets/icons/svg/build.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1568899741379" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2054" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M960 591.424V368.96c0-0.288 0.16-0.512 0.16-0.768S960 367.68 960 367.424V192a32 32 0 0 0-32-32H96a32 32 0 0 0-32 32v175.424c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768v222.464c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768V864a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32v-271.04c0-0.288 0.16-0.512 0.16-0.768S960 591.68 960 591.424z m-560-31.232v-160H608v160h-208z m208 64V832h-208v-207.808H608z m-480-224h208v160H128v-160z m544 0h224v160h-224v-160zM896 224v112.192H128V224h768zM128 624.192h208V832H128v-207.808zM672 832v-207.808h224V832h-224z" p-id="2055"></path></svg>
|
||||
|
After Width: | Height: | Size: 954 B |
1
admin-ui/src/assets/icons/svg/button.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></path></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
1
admin-ui/src/assets/icons/svg/cascader.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1576153230908" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="971" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M772.87036133 734.06115723c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714843H475.90991211c-56.60705567 0-102.66723633-46.06018067-102.66723633-102.66723633V600.82446289h305.859375c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012S827.9942627 467.50537109 772.87036133 467.50537109c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714844H373.24267578V401.01062011h321.92687989c55.12390137 0 99.94812012-44.82421875 99.94812011-99.94812011V190.07312011C795.11767578 134.94921875 750.29345703 90.125 695.16955567 90.125H251.12963867C196.0057373 90.125 151.18151855 134.94921875 151.18151855 190.07312011V301.0625c0 55.12390137 44.82421875 99.94812012 99.94812012 99.94812012h55.53588867v296.96044921c0 93.35632325 75.97045898 169.32678223 169.32678224 169.32678223h203.19213866c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012s-44.90661622-99.86572266-100.03051758-99.86572265z m0-199.89624024c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857423s-14.91394043 33.28857422-33.28857422 33.28857421-33.28857422-14.91394043-33.28857422-33.28857421 14.91394043-33.28857422 33.28857422-33.28857422zM217.75866699 301.0625V190.07312011c0-18.37463379 14.91394043-33.28857422 33.28857423-33.28857421h444.03991698c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857422V301.0625c0 18.37463379-14.91394043 33.28857422-33.28857422 33.28857422H251.12963867c-18.37463379 0-33.37097168-14.91394043-33.37097168-33.28857422z m555.11169434 566.23535156c-18.37463379 0-33.28857422-14.91394043-33.28857422-33.28857422 0-18.37463379 14.91394043-33.28857422 33.28857422-33.28857422s33.28857422 14.91394043 33.28857422 33.28857422c0.08239747 18.29223633-14.91394043 33.28857422-33.28857422 33.28857422z" p-id="972"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
1
admin-ui/src/assets/icons/svg/chart.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg>
|
||||
|
After Width: | Height: | Size: 179 B |
1
admin-ui/src/assets/icons/svg/checkbox.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1575982282951" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="902" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M828.40625 90.125H195.59375C137.375 90.125 90.125 137.375 90.125 195.59375v632.8125c0 58.21875 47.25 105.46875 105.46875 105.46875h632.8125c58.21875 0 105.46875-47.25 105.46875-105.46875V195.59375c0-58.21875-47.25-105.46875-105.46875-105.46875z m52.734375 738.28125c0 29.16-23.57015625 52.734375-52.734375 52.734375H195.59375c-29.109375 0-52.734375-23.574375-52.734375-52.734375V195.59375c0-29.109375 23.625-52.734375 52.734375-52.734375h632.8125c29.16 0 52.734375 23.625 52.734375 52.734375v632.8125z" p-id="903"></path><path d="M421.52890625 709.55984375a36.28125 36.28125 0 0 1-27.55265625-12.66890625L205.17453125 476.613125a36.28546875 36.28546875 0 0 1 55.10109375-47.22890625l164.986875 192.4846875 342.16171875-298.48078125a36.2896875 36.2896875 0 0 1 47.70984375 54.68765625L445.3859375 700.6203125a36.3234375 36.3234375 0 0 1-23.85703125 8.93953125z" p-id="904"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
admin-ui/src/assets/icons/svg/clipboard.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg>
|
||||
|
After Width: | Height: | Size: 971 B |
1
admin-ui/src/assets/icons/svg/code.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1546567861908" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2422" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M318.577778 819.2L17.066667 512l301.511111-307.2 45.511111 45.511111L96.711111 512l267.377778 261.688889zM705.422222 819.2l-45.511111-45.511111L927.288889 512l-267.377778-261.688889 45.511111-45.511111L1006.933333 512zM540.785778 221.866667l55.751111 11.150222L483.157333 802.133333l-55.751111-11.093333z" p-id="2423"></path></svg>
|
||||
|
After Width: | Height: | Size: 717 B |
1
admin-ui/src/assets/icons/svg/color.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1577252187056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2508" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M747.59340925 691.12859384c11.51396329 0.25305413 22.43746719-0.21087818 40.74171707-1.51832482 29.35428085-2.10878421 35.84933734-2.36183835 46.47761114-0.8856895 24.71495444 3.37405491 41.12129828 21.76265671 32.47528161 47.95376084-85.57447632 258.19957947-442.00123984 249.76444099-628.67084683 50.73735554-153.47733892-159.33976008-153.09775772-414.41833795 0.92786545-573.42069196 159.71934128-162.67163983 424.03439521-166.59397897 565.78689185 0.63263534 80.38686649 94.81095318 108.34934958 169.16669549 89.11723508 230.57450162-15.01454608 47.99593598-50.61082928 77.68762207-119.77896259 114.63352789-4.89237973 2.65706845-29.35428085 15.52065436-35.84933652 19.02123633-46.94154346 25.30541465-63.51659033 41.20565021-62.20914449 58.45550757 2.95229856 39.13904114 24.16667102 52.7196135 70.98168823 53.81618115z m44.41100207 50.10472101c-19.82257471 1.43397372-32.05352527 1.940082-45.63409763 1.6448519-70.34905207-1.60267593-115.98314969-30.91478165-121.38163769-101.64341492-3.45840683-46.05585397 24.7571304-73.13264758 89.24376132-107.96976837 6.7902866-3.66928501 31.37871396-16.57504688 36.06021551-19.06341229 57.69634516-30.83042972 85.15271997-53.73183005 94.76877722-84.47790866 12.77923398-40.78389304-9.10994898-98.94417051-79.24812286-181.6507002-121.17075953-142.97559219-350.14258521-139.60153647-489.2380134 2.06660824-134.49827774 138.84237405-134.79350784 362.12048163-0.42175717 501.637667 158.53842169 168.99799328 451.9968783 181.18676788 534.57688175-11.80919339-4.68150156 0.2952301-10.71262573 0.67481131-18.72600705 1.26527069z" p-id="2509"></path><path d="M346.03865637 637.18588562a78.82636652 78.82636652 0 0 0 78.32025825-79.29029883c0-43.69401562-35.005823-79.29029883-78.32025825-79.29029882a78.82636652 78.82636652 0 0 0-78.36243338 79.29029882c0 43.69401562 35.005823 79.29029883 78.36243338 79.29029883z m0-51.7495729a27.07679361 27.07679361 0 0 1-26.5706845-27.54072593c0-15.30977536 11.97789643-27.54072593 26.5706845-27.54072592 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072592a27.07679361 27.07679361 0 0 1-26.57068533 27.54072593zM475.7289063 807.11174353a78.82636652 78.82636652 0 0 0 78.3624334-79.29029882c0-43.69401562-34.96364785-79.29029883-78.32025825-79.29029883a78.82636652 78.82636652 0 0 0-78.32025742 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029882z m0-51.74957208a27.07679361 27.07679361 0 0 1-26.57068532-27.54072674c0-15.30977536 12.06224753-27.54072593 26.57068532-27.54072593 14.59278892 0 26.57068533 12.23095057 26.57068453 27.54072593a27.07679361 27.07679361 0 0 1-26.57068453 27.54072674zM601.24376214 377.21492718a78.82636652 78.82636652 0 0 0 78.32025742-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025742-79.29029882a78.82636652 78.82636652 0 0 0-78.32025823 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025824 79.29029883z m1e-8-51.74957208a27.07679361 27.07679361 0 0 1-26.57068534-27.54072675c0-15.30977536 11.97789643-27.54072593 26.57068534-27.54072591 14.55061295 0 26.57068533 12.23095057 26.57068451 27.54072592a27.07679361 27.07679361 0 0 1-26.57068451 27.54072674zM378.80916809 433.85687983a78.82636652 78.82636652 0 0 0 78.32025824-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025824-79.29029802a78.82636652 78.82636652 0 0 0-78.32025742 79.29029802c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029883z m0-51.74957209a27.07679361 27.07679361 0 0 1-26.57068451-27.54072674c0-15.30977536 11.97789643-27.54072593 26.57068451-27.54072593 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072593a27.07679361 27.07679361 0 0 1-26.57068533 27.54072674z" p-id="2510"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
1
admin-ui/src/assets/icons/svg/component.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1575804206892" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3145" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M826.56 470.016c-32.896 0-64.384 12.288-89.984 35.52l0-104.96c0-62.208-50.496-112.832-112.64-113.088L623.936 287.04 519.552 287.104C541.824 262.72 554.56 230.72 554.56 197.12c0-73.536-59.904-133.44-133.504-133.44-73.472 0-133.376 59.904-133.376 133.44 0 32.896 12.224 64.256 35.52 89.984L175.232 287.104l0 0.576C113.728 288.704 64 338.88 64 400.576l0.32 0 0.32 116.48C60.864 544.896 70.592 577.728 100.8 588.48c12.736 4.608 37.632 7.488 60.864-25.28 12.992-18.368 34.24-29.248 56.64-29.248 38.336 0 69.504 31.104 69.504 69.312 0 38.4-31.168 69.504-69.504 69.504-22.656 0-44.032-11.264-57.344-30.4C138.688 610.112 112.576 615.36 102.464 619.136c-29.824 10.752-39.104 43.776-38.144 67.392l0 160.384L64 846.912C64 909.248 114.752 960 177.216 960l446.272 0c62.4 0 113.152-50.752 113.152-113.152l0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.536 0 133.44-59.904 133.44-133.504C960 529.92 900.096 470.016 826.56 470.016zM826.56 672.896c-22.72 0-44.032-11.264-57.344-30.4-22.272-32.384-48.448-27.136-58.56-23.36-29.824 10.752-39.04 43.776-38.08 67.392l0 160.384c0 27.136-22.016 49.152-49.152 49.152L177.216 896.064C150.08 896 128 873.984 128 846.848l0.32 0 0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.6 0 133.504-59.904 133.504-133.504 0-73.472-59.904-133.376-133.504-133.376-32.896 0-64.32 12.288-89.984 35.52l0-104.96L128 400.512c0-27.072 22.08-49.152 49.216-49.152L177.216 351.04 334.656 350.72c3.776 0.512 7.616 0.832 11.52 0.832 24.896 0 50.752-10.816 60.032-37.056 4.544-12.736 7.424-37.568-25.344-60.736C362.624 240.768 351.68 219.52 351.68 197.12c0-38.272 31.104-69.44 69.376-69.44 38.336 0 69.504 31.168 69.504 69.44 0 22.72-11.264 44.032-30.528 57.472C427.968 276.736 433.088 302.784 436.8 313.024c10.752 29.888 43.072 39.232 67.392 38.08l119.232 0 0 0.384c27.136 0 49.152 22.08 49.152 49.152l0.256 116.48c-3.776 27.84 6.016 60.736 36.224 71.488 12.736 4.608 37.632 7.488 60.8-25.28 13.056-18.368 34.24-29.248 56.704-29.248C864.832 534.016 896 565.12 896 603.392 896 641.728 864.832 672.896 826.56 672.896z" p-id="3146"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
1
admin-ui/src/assets/icons/svg/dashboard.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
1
admin-ui/src/assets/icons/svg/date-range.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1579774833889" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1376" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M887.466667 192.853333h-100.693334V119.466667c0-10.24-6.826667-17.066667-17.066666-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H303.786667V119.466667c0-10.24-6.826667-17.066667-17.066667-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H168.96c-46.08 0-85.333333 37.546667-85.333333 85.333334V836.266667c0 46.08 37.546667 85.333333 85.333333 85.333333H887.466667c46.08 0 85.333333-37.546667 85.333333-85.333333V278.186667c0-47.786667-37.546667-85.333333-85.333333-85.333334z m-718.506667 34.133334h100.693333v66.56c0 10.24 6.826667 17.066667 17.066667 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56h450.56v66.56c0 10.24 6.826667 17.066667 17.066666 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56H887.466667c27.306667 0 51.2 22.186667 51.2 51.2v88.746666H117.76v-88.746666c0-29.013333 22.186667-51.2 51.2-51.2zM887.466667 887.466667H168.96c-27.306667 0-51.2-22.186667-51.2-51.2V401.066667H938.666667V836.266667c0 27.306667-22.186667 51.2-51.2 51.2z" p-id="1377"></path><path d="M858.453333 493.226667H327.68c-10.24 0-17.066667 6.826667-17.066667 17.066666v114.346667h-116.053333c-10.24 0-17.066667 6.826667-17.066667 17.066667v133.12c0 10.24 6.826667 17.066667 17.066667 17.066666H460.8c10.24 0 17.066667-6.826667 17.066667-17.066666v-114.346667h380.586666c10.24 0 17.066667-6.826667 17.066667-17.066667v-133.12c0-10.24-6.826667-17.066667-17.066667-17.066666z m-413.013333 34.133333v97.28h-98.986667v-97.28h98.986667z m-230.4 131.413333h98.986667v98.986667h-98.986667v-98.986667z m131.413333 97.28v-97.28h98.986667v97.28h-98.986667z m133.12-228.693333h97.28v98.986667h-97.28v-98.986667z m131.413334 0h98.986666v98.986667h-98.986666v-98.986667z m230.4 97.28h-98.986667v-98.986667h98.986667v98.986667z" p-id="1378"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
1
admin-ui/src/assets/icons/svg/date.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1577186573535" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1068" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M479.85714249 608.42857168h64.28571502c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285751s-12.85714249-32.14285751-32.14285664-32.14285664h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285662s12.85714249 32.14285751 32.14285664 32.14285753z m-2e-8 122.14285665h64.28571504c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285665s-12.85714249-32.14285751-32.14285664-32.14285751h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285751s12.85714249 32.14285751 32.14285664 32.14285664z m353.57142921-559.28571416h-128.57142921v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285664-32.14285753s-32.14285751 12.85714249-32.14285751 32.14285753v32.14285664h-257.14285665v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285752-32.14285753s-32.14285751 12.85714249-32.14285664 32.14285753v32.14285664h-128.57142919c-70.71428585 0-128.57142832 57.85714249-128.57142832 122.14285751v501.42857081c0 70.71428585 57.85714249 128.57142832 128.57142832 122.14285751h642.85714335c70.71428585 0 128.57142832-57.85714249 128.57142833-122.14285751v-501.42857081c0-70.71428585-57.85714249-122.14285753-128.57142833-122.14285751z m64.28571415 623.57142832c0 32.14285751-32.14285751 64.28571415-64.28571416 64.28571504h-642.85714335c-32.14285751 0-64.28571415-25.71428583-64.28571417-64.28571504v-372.85714249h771.42857168v372.85714249z m0-437.14285664h-771.42857168v-64.28571417c0-32.14285751 32.14285751-64.28571415 64.28571417-64.28571415h128.57142919v32.14285664c0 19.28571417 12.85714249 32.14285751 32.14285664 32.14285751s32.14285751-12.85714249 32.14285753-32.14285751v-32.14285664h257.14285665v32.14285664c0 19.28571417 12.85714249 32.14285751 32.1428575 32.14285751s32.14285751-12.85714249 32.14285664-32.14285751v-32.14285664h128.57142921c32.14285751 0 64.28571415 25.71428583 64.28571415 64.28571415v64.28571417z m-610.71428583 372.85714247h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285664s-12.85714249-32.14285751-32.14285753-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285751s12.85714249 32.14285751 32.14285751 32.14285665z m385.71428583-122.14285664h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285751s-12.85714249-32.14285751-32.14285751-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285664s12.85714249 32.14285751 32.14285753 32.14285751z m-385.71428583 0h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285751s-12.85714249-32.14285751-32.14285753-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285664s12.85714249 32.14285751 32.14285751 32.14285751z m385.71428583 122.14285665h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285665s-12.85714249-32.14285751-32.14285751-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285751s12.85714249 32.14285751 32.14285753 32.14285665z" p-id="1069"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
1
admin-ui/src/assets/icons/svg/dict.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566035680909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3601" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M1002.0848 744.672l-33.568 10.368c0.96 7.264 2.144 14.304 2.144 21.76 0 7.328-1.184 14.432-2.368 21.568l33.792 10.56c7.936 2.24 14.496 7.616 18.336 14.752 3.84 7.328 4.672 15.808 1.952 23.552-5.376 16-23.168 24.672-39.936 19.68l-34.176-10.624c-7.136 12.8-15.776 24.672-26.208 35.2l20.8 27.488a28.96 28.96 0 0 1 5.824 22.816 29.696 29.696 0 0 1-12.704 19.616 32.544 32.544 0 0 1-44.416-6.752l-20.8-27.552c-13.696 6.56-28.192 11.2-43.008 13.888v33.632c0 16.736-14.112 30.432-31.648 30.432-17.6 0-31.872-13.696-31.872-30.432v-33.632a167.616 167.616 0 0 1-42.88-13.888l-20.928 27.552c-10.72 13.76-30.08 16.64-44.288 6.752a29.632 29.632 0 0 1-12.704-19.616 29.28 29.28 0 0 1 5.696-22.816l20.896-27.808a166.72 166.72 0 0 1-27.008-34.688l-33.376 10.432c-16.8 5.184-34.56-3.552-39.936-19.616a29.824 29.824 0 0 1 20.224-38.24l33.472-10.432c-0.8-7.264-2.016-14.304-2.016-21.824 0-7.36 1.184-14.496 2.304-21.632l-33.792-10.368c-16.672-5.376-25.632-22.496-20.224-38.432 5.376-16 23.136-24.672 39.936-19.68l34.016 10.752c7.328-12.672 15.84-24.8 26.336-35.328l-20.8-27.552a29.44 29.44 0 0 1 6.944-42.432 32.704 32.704 0 0 1 44.384 6.752l20.832 27.616c13.696-6.432 28.224-11.2 43.104-13.952v-33.568c0-16.736 14.048-30.432 31.648-30.432 17.536 0 31.808 13.568 31.808 30.432v33.504c15.072 2.688 29.344 7.808 42.848 14.016l20.992-27.616a32.48 32.48 0 0 1 44.224-6.752 29.568 29.568 0 0 1 7.136 42.432l-21.024 27.808c10.432 10.432 19.872 21.888 27.04 34.752l33.376-10.432c16.768-5.12 34.56 3.68 39.936 19.68 5.536 15.936-3.712 33.056-20.32 38.304z m-206.016-74.432c-61.344 0-111.136 47.808-111.136 106.56 0 58.88 49.792 106.496 111.136 106.496 61.312 0 111.104-47.616 111.104-106.496 0-58.752-49.792-106.56-111.104-106.56z" p-id="3602"></path><path d="M802.7888 57.152h-76.448c0-22.08-21.024-38.24-42.848-38.24H39.3968a39.68 39.68 0 0 0-39.36 40.032v795.616s41.888 120.192 110.752 120.192H673.2848a227.488 227.488 0 0 1-107.04-97.44H117.6368s-40.608-13.696-40.608-41.248l470.304-0.256 1.664 3.36a227.68 227.68 0 0 1-12.64-73.632c0-60.576 24-118.624 66.88-161.44a228.352 228.352 0 0 1 123.552-63.392l-3.2 0.288 2.144-424.672h38.208l0.576 421.024c27.04 0 52.672 4.8 76.64 13.344V101.536c0.032 0-6.304-44.384-38.368-44.384zM149.7648 514.336H72.3888v-77.408H149.7648v77.408z m0-144.32H72.3888v-77.44H149.7648v77.44z m0-137.248H72.3888v-77.44H149.7648v77.44z m501.856 281.568H206.0848v-77.408h445.536v77.408z m0-144.32H206.0848v-77.44h445.536v77.44z m0-137.248H206.0848v-77.44h445.536v77.44z" p-id="3603"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1
admin-ui/src/assets/icons/svg/documentation.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M71.984 44.815H115.9L71.984 9.642v35.173zM16.094.05h63.875l47.906 38.37v76.74c0 3.392-1.682 6.645-4.677 9.044-2.995 2.399-7.056 3.746-11.292 3.746H16.094c-4.236 0-8.297-1.347-11.292-3.746-2.995-2.399-4.677-5.652-4.677-9.044V12.84C.125 5.742 7.23.05 16.094.05zm71.86 102.32V89.58h-71.86v12.79h71.86zm23.952-25.58V64H16.094v12.79h95.812z"/></svg>
|
||||
|
After Width: | Height: | Size: 418 B |
1
admin-ui/src/assets/icons/svg/download.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1569915748289" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3062" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M768.35456 416a256 256 0 1 0-512 0 192 192 0 1 0 0 384v64a256 256 0 0 1-58.88-505.216 320.128 320.128 0 0 1 629.76 0A256.128 256.128 0 0 1 768.35456 864v-64a192 192 0 0 0 0-384z m-512 384h64v64H256.35456v-64z m448 0h64v64h-64v-64z" fill="#333333" p-id="3063"></path><path d="M539.04256 845.248V512.192a32.448 32.448 0 0 0-32-32.192c-17.664 0-32 14.912-32 32.192v333.056l-36.096-36.096a32.192 32.192 0 0 0-45.056 0.192 31.616 31.616 0 0 0-0.192 45.056l90.88 90.944a31.36 31.36 0 0 0 22.528 9.088 30.08 30.08 0 0 0 22.4-9.088l90.88-90.88a32.192 32.192 0 0 0-0.192-45.12 31.616 31.616 0 0 0-45.056-0.192l-36.096 36.096z" fill="#333333" p-id="3064"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
admin-ui/src/assets/icons/svg/drag.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M73.137 29.08h-9.209 29.7L63.886.093 34.373 29.08h20.49v27.035H27.238v17.948h27.625v27.133h18.274V74.063h27.41V56.115h-27.41V29.08zm-9.245 98.827l27.518-26.711H36.59l27.302 26.71zM.042 64.982l27.196 27.029V38.167L.042 64.982zm100.505-26.815V92.01l27.41-27.029-27.41-26.815z"/></svg>
|
||||
|
After Width: | Height: | Size: 356 B |
1
admin-ui/src/assets/icons/svg/druid.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566036347051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5853" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M832 128H192a64.19 64.19 0 0 0-64 64v640a64.19 64.19 0 0 0 64 64h640a64.19 64.19 0 0 0 64-64V192a64.19 64.19 0 0 0-64-64z m0 703.89l-0.11 0.11H192.11l-0.11-0.11V768h640zM832 544H720L605.6 696.54 442.18 435.07 333.25 544H192v-64h114.75l147.07-147.07L610.4 583.46 688 480h144z m0-288H192v-63.89l0.11-0.11h639.78l0.11 0.11z" p-id="5854"></path></svg>
|
||||
|
After Width: | Height: | Size: 724 B |
1
admin-ui/src/assets/icons/svg/edit.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>
|
||||
|
After Width: | Height: | Size: 818 B |
1
admin-ui/src/assets/icons/svg/education.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M88.883 119.565c-7.284 0-19.434 2.495-21.333 8.25v.127c-4.232.13-5.222 0-7.108 0-1.895-5.76-14.045-8.256-21.333-8.256H0V0h42.523c9.179 0 17.109 5.47 21.47 13.551C68.352 5.475 76.295 0 85.478 0H128v119.57l-39.113-.005h-.004zM60.442 24.763c0-9.651-8.978-16.507-17.777-16.507H7.108V111.43H39.11c7.054-.14 18.177.082 21.333 6.12v-4.628c-.134-5.722-.004-13.522 0-13.832V27.413l.004-2.655-.004.005zm60.442-16.517h-35.55c-8.802 0-17.78 6.856-17.78 16.493v74.259c.004.32.138 8.115 0 13.813v4.627c3.155-6.022 14.279-6.26 21.333-6.114h32V8.25l-.003-.005z"/></svg>
|
||||
|
After Width: | Height: | Size: 627 B |
1
admin-ui/src/assets/icons/svg/email.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg>
|
||||
|
After Width: | Height: | Size: 347 B |
1
admin-ui/src/assets/icons/svg/example.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>
|
||||
|
After Width: | Height: | Size: 497 B |
1
admin-ui/src/assets/icons/svg/excel.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg>
|
||||
|
After Width: | Height: | Size: 459 B |