original_project

This commit is contained in:
haotian 2025-07-25 10:03:06 +08:00
commit 632d32c0c6
267 changed files with 17391 additions and 0 deletions

146
README.md Normal file
View 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+mysqlflask+mongosanic-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和提取字段如titlebase_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
View File

@ -0,0 +1,8 @@
.idea
/chrome
/logs
/meta
/venv
/static
*.pyc
/excel

27
admin-api/Dockerfile Normal file
View 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"]

View File

@ -0,0 +1,2 @@
import pymysql
pymysql.install_as_MySQLdb()

View 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()

View 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'

View 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')),
]

View 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
View File

@ -0,0 +1,3 @@
git pull
docker-compose build
docker-compose up -d

68
admin-api/config.py Normal file
View File

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
"""
@Authormengying
@file config.py
@date2023/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)

View 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()

View 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()

View 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

View 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

View 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
View 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
View 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()

View File

@ -0,0 +1,50 @@
# -*- coding: <encoding name> -*-
"""
@Authormengying
@file myAuthorization.py
@date2023/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

View 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

Binary file not shown.

View 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"))

View 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
View 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" # 管理员账号

View 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)

View 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())

View 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())

View File

3
admin-api/web/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
admin-api/web/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class WebConfigweb(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'web'

View 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)

View 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',
},
),
]

View 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='启用状态'),
),
]

View 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',
),
]

View 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'),
),
]

View File

108
admin-api/web/models.py Normal file
View 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 = '角色与人员关系映射'

View 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 {}

View 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__'

Binary file not shown.

View 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="已选择的树选择器"),
]

View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
admin-api/web/urls.py Normal file
View 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
View 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)

View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
FROM nginx:latest
WORKDIR /app
COPY ./dist /usr/share/nginx/html
EXPOSE 80

0
admin-ui/LICENSE Normal file
View File

4
admin-ui/build.sh Normal file
View File

@ -0,0 +1,4 @@
git pull
yarn run build:prod
docker-compose build
docker-compose up -d

View 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

File diff suppressed because one or more lines are too long

216
admin-ui/index.html Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

15
admin-ui/src/App.vue Normal file
View 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
View 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
View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
// 获取路由
export const getRouters = () => {
return request({
url: '/getRouters',
method: 'get'
})
}

View 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'
})
}

View 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
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
// 获取服务信息
export function getServer() {
return request({
url: '/monitor/server',
method: 'get'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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'
})
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

Some files were not shown because too many files have changed in this diff Show More