feat(auth): 添加第三方OAuth登录支持

- 实现OAuth登录重定向和回调处理
- 更新配置文件以支持多种OAuth提供商
- 添加错误处理和状态管理功能
- 更新文档以反映新功能和配置要求
This commit is contained in:
zhangtao
2026-05-03 06:42:13 +08:00
parent e4f778877b
commit b12f7a0a11
699 changed files with 89018 additions and 31150 deletions
@@ -1,13 +1,17 @@
import json
import secrets
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Path, Query, Request
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse, RedirectResponse
from redis.asyncio.client import Redis from redis.asyncio.client import Redis
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.common.response import ErrorResponse, SuccessResponse from app.common.response import ErrorResponse, SuccessResponse
from app.config.setting import settings from app.config.setting import settings
from app.core.dependencies import db_getter, get_current_user, redis_getter from app.core.dependencies import db_getter, get_current_user, redis_getter
from app.core.exceptions import CustomException
from app.core.redis_crud import RedisCURD
from app.core.logger import log from app.core.logger import log
from app.core.router_class import OperationLogRoute from app.core.router_class import OperationLogRoute
from app.core.security import CustomOAuth2PasswordRequestForm from app.core.security import CustomOAuth2PasswordRequestForm
@@ -20,6 +24,15 @@ from .schema import (
LogoutPayloadSchema, LogoutPayloadSchema,
RefreshTokenPayloadSchema, RefreshTokenPayloadSchema,
) )
from .oauth_service import (
STATE_PREFIX,
build_authorize_url,
complete_oauth_login,
oauth_service_error_redirect,
oauth_service_frontend_redirect_from_token,
save_oauth_state,
_callback_url,
)
from .service import AutoLoginService, CaptchaService, LoginService from .service import AutoLoginService, CaptchaService, LoginService
AuthRouter = APIRouter(route_class=OperationLogRoute, prefix="/auth", tags=["认证授权"]) AuthRouter = APIRouter(route_class=OperationLogRoute, prefix="/auth", tags=["认证授权"])
@@ -236,3 +249,100 @@ async def auto_login_controller(
) )
log.info("用户免登录成功") log.info("用户免登录成功")
return SuccessResponse(data=login_token.model_dump(), msg="登录成功") return SuccessResponse(data=login_token.model_dump(), msg="登录成功")
@AuthRouter.get(
"/oauth/{provider}/login",
summary="第三方OAuth跳转",
description="浏览器重定向到微信/GitHub/Gitee/QQ 授权页;redirect_uri 为授权完成后回到前端的登录页地址(如 http://localhost:5173/login)。",
)
async def oauth_login_redirect_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
provider: Annotated[str, Path(description="wechat | qq | github | gitee")],
redirect_uri: Annotated[
str | None,
Query(description="OAuth 完成后浏览器回到的前端登录页完整 URL"),
] = None,
) -> RedirectResponse:
allowed = {"wechat", "qq", "github", "gitee"}
fe = redirect_uri or settings.OAUTH_FRONTEND_FALLBACK
if provider not in allowed:
return RedirectResponse(
url=oauth_service_error_redirect(fe, "不支持的 OAuth 渠道"),
status_code=302,
)
if not redirect_uri:
return RedirectResponse(
url=oauth_service_error_redirect(fe, "缺少 redirect_uri 参数"),
status_code=302,
)
try:
state = secrets.token_urlsafe(32)
await save_oauth_state(
redis=redis,
state=state,
provider=provider,
frontend_redirect=redirect_uri,
)
cb = _callback_url(request, provider)
url = build_authorize_url(provider=provider, callback_url=cb, state=state)
return RedirectResponse(url=url, status_code=302)
except CustomException as e:
return RedirectResponse(
url=oauth_service_error_redirect(redirect_uri, e.msg),
status_code=302,
)
@AuthRouter.get(
"/oauth/{provider}/callback",
summary="第三方OAuth回调",
include_in_schema=False,
)
async def oauth_callback_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
db: Annotated[AsyncSession, Depends(db_getter)],
provider: Annotated[str, Path()],
code: Annotated[str | None, Query()] = None,
state: Annotated[str | None, Query()] = None,
) -> RedirectResponse:
fe_fallback = settings.OAUTH_FRONTEND_FALLBACK
async def resolve_frontend() -> str:
if not state:
return fe_fallback
raw = await RedisCURD(redis).get(f"{STATE_PREFIX}{state}")
if not raw:
return fe_fallback
if isinstance(raw, bytes):
raw = raw.decode("utf-8")
try:
payload = json.loads(raw)
return str(payload.get("frontend_redirect") or fe_fallback).strip() or fe_fallback
except json.JSONDecodeError:
return fe_fallback
if provider not in {"wechat", "qq", "github", "gitee"}:
url = oauth_service_error_redirect(await resolve_frontend(), "不支持的 OAuth 渠道")
return RedirectResponse(url=url, status_code=302)
if not code or not state:
url = oauth_service_error_redirect(
await resolve_frontend(), "授权被取消或参数不完整"
)
return RedirectResponse(url=url, status_code=302)
try:
token, fe = await complete_oauth_login(
request=request,
redis=redis,
db=db,
provider=provider,
code=code,
state=state,
)
success_url = oauth_service_frontend_redirect_from_token(fe, token)
return RedirectResponse(url=success_url, status_code=302)
except CustomException as e:
fe = await resolve_frontend()
return RedirectResponse(url=oauth_service_error_redirect(fe, e.msg), status_code=302)
@@ -0,0 +1,437 @@
"""
第三方 OAuth2 登录(微信开放平台扫码、QQ、GitHub、Gitee)。
各平台需在开放平台登记「授权回调域 / redirect_uri」为:
{API}/system/auth/oauth/{provider}/callback
例如:https://your-domain.com/api/v1/system/auth/oauth/github/callback
环境变量见 Settings 中 OAUTH_* 字段。
"""
from __future__ import annotations
import json
import secrets
from typing import Any, Literal, Tuple
from urllib.parse import quote, urlencode
import httpx
from fastapi import Request
from redis.asyncio.client import Redis
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.v1.module_system.auth.schema import AuthSchema
from app.api.v1.module_system.user.crud import UserCRUD
from app.api.v1.module_system.user.model import UserModel
from app.api.v1.module_system.user.schema import UserRegisterSchema
from app.api.v1.module_system.user.service import UserService
from app.config.setting import settings
from app.core.exceptions import CustomException
from app.core.logger import log
from app.core.redis_crud import RedisCURD
from .schema import JWTOutSchema
from .service import LoginService
OAuthProvider = Literal["wechat", "qq", "github", "gitee"]
STATE_PREFIX = "oauth_state:"
STATE_TTL_SECONDS = 600
def _callback_url(request: Request, provider: OAuthProvider) -> str:
root = str(request.base_url).rstrip("/")
return f"{root}/system/auth/oauth/{provider}/callback"
def _frontend_error_redirect(frontend_base: str, message: str) -> str:
sep = "&" if "?" in frontend_base else "?"
return f"{frontend_base}{sep}oauth_error={quote(message, safe='')}"
def _frontend_success_redirect(
frontend_base: str, access_token: str, refresh_token: str, token_type: str
) -> str:
q = urlencode(
{
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": token_type,
}
)
sep = "&" if "?" in frontend_base else "?"
return f"{frontend_base}{sep}{q}"
def _require_credentials(provider: OAuthProvider) -> tuple[str, str]:
if provider == "github":
cid, sec = settings.OAUTH_GITHUB_CLIENT_ID, settings.OAUTH_GITHUB_CLIENT_SECRET
elif provider == "gitee":
cid, sec = settings.OAUTH_GITEE_CLIENT_ID, settings.OAUTH_GITEE_CLIENT_SECRET
elif provider == "wechat":
cid, sec = settings.OAUTH_WECHAT_OPEN_APP_ID, settings.OAUTH_WECHAT_OPEN_APP_SECRET
elif provider == "qq":
cid, sec = settings.OAUTH_QQ_APP_ID, settings.OAUTH_QQ_APP_SECRET
else:
raise CustomException(msg="不支持的 OAuth 渠道")
if not cid or not sec:
raise CustomException(msg=f"{provider} OAuth 未配置(客户端密钥为空)")
return cid, sec
def build_authorize_url(
*,
provider: OAuthProvider,
callback_url: str,
state: str,
) -> str:
"""构造跳转至第三方授权页的 URL。"""
cid, _ = _require_credentials(provider)
if provider == "github":
params = {
"client_id": cid,
"redirect_uri": callback_url,
"scope": "user:email",
"state": state,
}
return "https://github.com/login/oauth/authorize?" + urlencode(params)
if provider == "gitee":
params = {
"client_id": cid,
"redirect_uri": callback_url,
"response_type": "code",
"state": state,
}
return "https://gitee.com/oauth/authorize?" + urlencode(params)
if provider == "wechat":
params = {
"appid": cid,
"redirect_uri": callback_url,
"response_type": "code",
"scope": "snsapi_login",
"state": state,
}
return "https://open.weixin.qq.com/connect/qrconnect?" + urlencode(params) + "#wechat_redirect"
if provider == "qq":
params = {
"response_type": "code",
"client_id": cid,
"redirect_uri": callback_url,
"state": state,
"scope": "get_user_info",
}
return "https://graph.qq.com/oauth2.0/authorize?" + urlencode(params)
raise CustomException(msg="不支持的 OAuth 渠道")
async def _http_json(method: str, url: str, **kwargs: Any) -> Any:
timeout = getattr(settings, "HTTPX_DEFAULT_TIMEOUT", 15.0)
async with httpx.AsyncClient(timeout=timeout) as client:
r = await client.request(method, url, **kwargs)
r.raise_for_status()
try:
return r.json()
except json.JSONDecodeError:
text = r.text
log.error(f"OAuth 非 JSON 响应: {text[:500]}")
raise CustomException(msg="OAuth 接口返回异常")
async def _http_text(method: str, url: str, **kwargs: Any) -> str:
timeout = getattr(settings, "HTTPX_DEFAULT_TIMEOUT", 15.0)
async with httpx.AsyncClient(timeout=timeout) as client:
r = await client.request(method, url, **kwargs)
r.raise_for_status()
return r.text
async def exchange_github_token(client_id: str, client_secret: str, code: str, redirect_uri: str) -> str:
data = await _http_json(
"POST",
"https://github.com/login/oauth/access_token",
headers={"Accept": "application/json"},
data={
"client_id": client_id,
"client_secret": client_secret,
"code": code,
"redirect_uri": redirect_uri,
},
)
if not isinstance(data, dict):
raise CustomException(msg="GitHub token 响应格式错误")
token = data.get("access_token")
if not token:
raise CustomException(msg=data.get("error_description") or "GitHub 换取令牌失败")
return str(token)
async def exchange_gitee_token(client_id: str, client_secret: str, code: str, redirect_uri: str) -> str:
qs = urlencode(
{
"grant_type": "authorization_code",
"code": code,
"client_id": client_id,
"client_secret": client_secret,
"redirect_uri": redirect_uri,
}
)
data = await _http_json("GET", f"https://gitee.com/oauth/token?{qs}")
if not isinstance(data, dict):
raise CustomException(msg="Gitee token 响应格式错误")
token = data.get("access_token")
if not token:
raise CustomException(msg=data.get("error_description") or "Gitee 换取令牌失败")
return str(token)
async def exchange_wechat_token(app_id: str, secret: str, code: str) -> tuple[str, str]:
qs = urlencode(
{
"appid": app_id,
"secret": secret,
"code": code,
"grant_type": "authorization_code",
}
)
data = await _http_json("GET", f"https://api.weixin.qq.com/sns/oauth2/access_token?{qs}")
if not isinstance(data, dict):
raise CustomException(msg="微信 token 响应格式错误")
token = data.get("access_token")
openid = data.get("openid")
if not token or not openid:
raise CustomException(msg=data.get("errmsg") or "微信换取令牌失败")
return str(token), str(openid)
async def exchange_qq_token(client_id: str, client_secret: str, code: str, redirect_uri: str) -> tuple[str, str]:
qs = urlencode(
{
"grant_type": "authorization_code",
"client_id": client_id,
"client_secret": client_secret,
"code": code,
"redirect_uri": redirect_uri,
}
)
text = await _http_text("GET", f"https://graph.qq.com/oauth2.0/token?{qs}")
parts = dict(p.split("=", 1) for p in text.split("&") if "=" in p)
token = parts.get("access_token")
if not token:
raise CustomException(msg="QQ 换取 access_token 失败")
me = await _http_json(
"GET",
"https://graph.qq.com/oauth2.0/me",
params={"access_token": token, "fmt": "json"},
)
if not isinstance(me, dict):
raise CustomException(msg="QQ openid 响应格式错误")
openid = me.get("openid")
if not openid:
raise CustomException(msg="QQ 获取 openid 失败")
return str(token), str(openid)
async def fetch_github_profile(access_token: str) -> tuple[str, str, str | None]:
headers = {"Authorization": f"Bearer {access_token}", "Accept": "application/json"}
user = await _http_json("GET", "https://api.github.com/user", headers=headers)
if not isinstance(user, dict):
raise CustomException(msg="GitHub 用户信息格式错误")
login = str(user.get("login") or "")
name = str(user.get("name") or login or "github")
email = user.get("email")
if not email:
emails = await _http_json("GET", "https://api.github.com/user/emails", headers=headers)
if isinstance(emails, list):
primary = next((e for e in emails if isinstance(e, dict) and e.get("primary")), None)
if primary:
email = primary.get("email")
return login, name, email
async def fetch_gitee_profile(access_token: str) -> tuple[str, str, str | None]:
user = await _http_json(
"GET",
"https://gitee.com/api/v5/user",
params={"access_token": access_token},
)
if not isinstance(user, dict):
raise CustomException(msg="Gitee 用户信息格式错误")
login = str(user.get("login") or "")
name = str(user.get("name") or login)
email = user.get("email")
return login, name, email
async def fetch_wechat_profile(access_token: str, openid: str) -> tuple[str, str]:
qs = urlencode({"access_token": access_token, "openid": openid, "lang": "zh_CN"})
user = await _http_json("GET", f"https://api.weixin.qq.com/sns/userinfo?{qs}")
if not isinstance(user, dict):
raise CustomException(msg="微信用户信息格式错误")
nickname = str(user.get("nickname") or "wechat")
unionid = user.get("unionid")
oid = unionid or openid
return str(oid), nickname
async def fetch_qq_profile(access_token: str, app_id: str, openid: str) -> tuple[str, str]:
qs = urlencode(
{
"access_token": access_token,
"oauth_consumer_key": app_id,
"openid": openid,
}
)
user = await _http_json("GET", f"https://graph.qq.com/user/get_user_info?{qs}")
if not isinstance(user, dict):
raise CustomException(msg="QQ 用户信息格式错误")
if user.get("ret") not in (0, "0", None):
raise CustomException(msg=user.get("msg") or "QQ 用户信息失败")
nickname = str(user.get("nickname") or "qq")
return openid, nickname
def _username_for_oauth(provider: OAuthProvider, unique_id: str) -> str:
"""生成符合注册规则的登录名:oauth_{provider}_{id}"""
raw = f"oauth_{provider}_{unique_id}"
raw = "".join(c if c.isalnum() or c in "_-." else "_" for c in raw)[:32]
if len(raw) < 3:
raw = (raw + "usr")[:32]
if not raw[0].isalpha():
raw = "o" + raw[:31]
return raw
async def ensure_oauth_user(
*,
db: AsyncSession,
provider: OAuthProvider,
unique_id: str,
display_name: str,
) -> UserModel:
auth = AuthSchema(db=db, user=None, check_data_scope=False)
username = _username_for_oauth(provider, unique_id)
existing = await UserCRUD(auth).get_by_username_crud(username=username)
if existing:
return existing
reg = UserRegisterSchema(
username=username,
password=secrets.token_urlsafe(24),
name=(display_name or username)[:32],
role_ids=list(settings.OAUTH_DEFAULT_ROLE_IDS),
)
await UserService.register_user_service(auth=auth, data=reg)
user = await UserCRUD(auth).get_by_username_crud(username=username)
if not user:
raise CustomException(msg="OAuth 注册失败")
log.info(f"OAuth 自动注册用户: {username} ({provider})")
return user
async def complete_oauth_login(
*,
request: Request,
redis: Redis,
db: AsyncSession,
provider: OAuthProvider,
code: str,
state: str,
) -> Tuple[JWTOutSchema, str]:
rc = RedisCURD(redis)
raw = await rc.get(f"{STATE_PREFIX}{state}")
if not raw:
raise CustomException(msg="登录状态已失效,请重试")
if isinstance(raw, bytes):
raw = raw.decode("utf-8")
payload = json.loads(raw)
if payload.get("provider") != provider:
raise CustomException(msg="OAuth 状态不匹配")
frontend = str(payload.get("frontend_redirect") or "").strip()
if not frontend:
raise CustomException(msg="缺少前端回调地址")
callback_url = _callback_url(request, provider)
cid, csec = _require_credentials(provider)
if provider == "github":
access = await exchange_github_token(cid, csec, code, callback_url)
login_k, name, _email = await fetch_github_profile(access)
uid = login_k
elif provider == "gitee":
access = await exchange_gitee_token(cid, csec, code, callback_url)
login_k, name, _email = await fetch_gitee_profile(access)
uid = login_k
elif provider == "wechat":
access, openid = await exchange_wechat_token(cid, csec, code)
uid, name = await fetch_wechat_profile(access, openid)
elif provider == "qq":
access, openid = await exchange_qq_token(cid, csec, code, callback_url)
uid, name = await fetch_qq_profile(access, cid, openid)
else:
raise CustomException(msg="不支持的 OAuth 渠道")
user = await ensure_oauth_user(db=db, provider=provider, unique_id=uid, display_name=name)
if user.status == "1":
raise CustomException(msg="用户已被停用")
user = await UserCRUD(AuthSchema(db=db, user=None, check_data_scope=False)).update_last_login_crud(
id=user.id
)
if not user:
raise CustomException(msg="用户不存在")
login_type = f"oauth_{provider}"
token = await LoginService.create_token_service(
request=request, redis=redis, user=user, login_type=login_type
)
await rc.delete(f"{STATE_PREFIX}{state}")
return token, frontend
async def save_oauth_state(
*,
redis: Redis,
state: str,
provider: OAuthProvider,
frontend_redirect: str,
) -> None:
rc = RedisCURD(redis)
ok = await rc.set(
f"{STATE_PREFIX}{state}",
json.dumps({"provider": provider, "frontend_redirect": frontend_redirect}),
expire=STATE_TTL_SECONDS,
)
if not ok:
raise CustomException(msg="缓存 OAuth 状态失败")
def oauth_service_frontend_redirect_from_token(
frontend_base: str, token: JWTOutSchema
) -> str:
return _frontend_success_redirect(
frontend_base,
token.access_token,
token.refresh_token,
token.token_type,
)
def oauth_service_error_redirect(frontend_base: str, message: str) -> str:
return _frontend_error_redirect(frontend_base, message)
__all__ = [
"OAuthProvider",
"STATE_PREFIX",
"build_authorize_url",
"complete_oauth_login",
"save_oauth_state",
"_callback_url",
"oauth_service_frontend_redirect_from_token",
"oauth_service_error_redirect",
]
+16
View File
@@ -120,6 +120,22 @@ class Settings(BaseSettings):
CAPTCHA_FONT_SIZE: int = 32 # 字体大小 CAPTCHA_FONT_SIZE: int = 32 # 字体大小
CAPTCHA_FONT_PATH: str = "static/assets/font/Arial.ttf" # 字体路径 CAPTCHA_FONT_PATH: str = "static/assets/font/Arial.ttf" # 字体路径
# ================================================= #
# ***************** 第三方 OAuth 登录(可选)********* #
# ================================================= #
# 自动注册用户的默认角色 ID 列表(须与库中角色主键一致)
OAUTH_DEFAULT_ROLE_IDS: list[int] = [2]
# 回调异常时回跳的前端地址(与前端实际 /login 一致,含协议与端口)
OAUTH_FRONTEND_FALLBACK: str = "http://127.0.0.1:5173/login"
OAUTH_GITHUB_CLIENT_ID: str = ""
OAUTH_GITHUB_CLIENT_SECRET: str = ""
OAUTH_GITEE_CLIENT_ID: str = ""
OAUTH_GITEE_CLIENT_SECRET: str = ""
OAUTH_WECHAT_OPEN_APP_ID: str = ""
OAUTH_WECHAT_OPEN_APP_SECRET: str = ""
OAUTH_QQ_APP_ID: str = ""
OAUTH_QQ_APP_SECRET: str = ""
# ================================================= # # ================================================= #
# ******************* 外部 HTTPhttpx******************* # # ******************* 外部 HTTPhttpx******************* #
# ================================================= # # ================================================= #
+15
View File
@@ -0,0 +1,15 @@
# http://editorconfig.org
root = true
# 表示所有文件适用
[*]
charset = utf-8 # 设置文件字符集为 utf-8
end_of_line = lf # 控制换行类型(lf | cr | crlf)
indent_style = space # 缩进风格(tab | space
indent_size = 2 # 缩进大小
insert_final_newline = true # 始终在文件末尾插入一个新行
# 表示仅 md 文件适用以下规则
[*.md]
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
+13 -7
View File
@@ -1,22 +1,28 @@
# 【通用】环境变量 # 【通用】环境变量 - 所有环境共享
# 版本号 # 应用版本号
VITE_VERSION = 3.0.2 VITE_VERSION = 3.0.2
# 端口 # 开发服务器端口
VITE_PORT = 3006 VITE_PORT = 5180
# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/
VITE_BASE_URL = / VITE_BASE_URL = /
# 权限模式【 frontend 前端模式 / backend 后端模式 # 权限模式【 frontend 前端路由 / backend 后端菜单 / mixed 后端菜单+前端路由模块合并
VITE_ACCESS_MODE = frontend VITE_ACCESS_MODE = mixed
# 跨域请求时是否携带 Cookie(开启前需确保后端支持) # 跨域请求时是否携带 Cookie(开启前需确保后端支持)
VITE_WITH_CREDENTIALS = false VITE_WITH_CREDENTIALS = false
# 是否打开路由信息 # 是否在控制台输出路由信息
VITE_OPEN_ROUTE_INFO = false VITE_OPEN_ROUTE_INFO = false
# 锁屏加密密钥 # 锁屏加密密钥
VITE_LOCK_ENCRYPT_KEY = s3cur3k3y4adpro VITE_LOCK_ENCRYPT_KEY = s3cur3k3y4adpro
# 网络请求超时时间(毫秒)
VITE_API_TIMEOUT = 60000
# 代理前缀
VITE_APP_BASE_API = /api/v1
+16 -8
View File
@@ -1,13 +1,21 @@
# 【开发】环境变量 # 【开发】环境变量 - 覆盖通用配置
# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/ # 环境标识
VITE_BASE_URL = / VITE_APP_ENV = development
# API 请求基础路径(开发环境设置为 / 使用代理,生产环境设置为完整后端地址) # 项目名称
VITE_APP_TITLE = FastAPI Admin
# 浏览器侧同源前缀(与 Vite 代理配合时用 /,请求走 localhost:端口 + 代理)
VITE_API_URL = / VITE_API_URL = /
# 代理目标地址(开发环境通过 Vite 代理转发请求到此地址,解决跨域问题 # 代理目标:本机后端(若出现 ENOTFOUND,说明上面域名在当前网络/DNS 下不可解析,改用本地或可用地址
VITE_API_PROXY_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default # VITE_API_BASE_URL = http://127.0.0.1:8000
VITE_API_BASE_URL = https://service.fastapiadmin.com
# Delete console # 是否删除控制台输出
VITE_DROP_CONSOLE = false VITE_DROP_CONSOLE = false
# WebSocket 端点(AI对话功能需要配置)
# VITE_APP_WS_ENDPOINT = ws://localhost:8000
VITE_APP_WS_ENDPOINT = wss://service.fastapiadmin.com
+13 -7
View File
@@ -1,10 +1,16 @@
# 【生产】环境变量 # 【生产】环境变量 - 覆盖通用配置
# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/ # 环境标识
VITE_BASE_URL = / VITE_APP_ENV = production
# API 地址前缀 # 项目名称
VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default VITE_APP_TITLE = FastAPI Admin
# Delete console # API 请求基础路径(生产环境使用完整后端地址)
VITE_DROP_CONSOLE = true VITE_API_BASE_URL = https://server.fastapiadmin.com
# 是否删除控制台输出
VITE_DROP_CONSOLE = true
# WebSocket 端点(AI对话功能需要配置,生产环境建议使用 wss)
VITE_APP_WS_ENDPOINT = wss://server.fastapiadmin.com
+17
View File
@@ -4,8 +4,25 @@ dist
dist-ssr dist-ssr
*.local *.local
.cursorrules .cursorrules
.history
# Auto-generated files # Auto-generated files
src/types/import/auto-imports.d.ts src/types/import/auto-imports.d.ts
src/types/import/components.d.ts src/types/import/components.d.ts
.auto-import.json .auto-import.json
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
stats.html
pnpm-lock.yaml
package-lock.json
.stylelintcache
.eslintcache
# docs and app directories are at frontend root, managed by frontend/.gitignore
+12 -3
View File
@@ -1,3 +1,12 @@
/node_modules/* dist
/dist/* node_modules
/src/main.ts public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html
pnpm-lock.yaml
-20
View File
@@ -1,20 +0,0 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"vueIndentScriptAndStyle": true,
"singleQuote": true,
"quoteProps": "as-needed",
"bracketSpacing": true,
"trailingComma": "none",
"bracketSameLine": false,
"jsxSingleQuote": false,
"arrowParens": "always",
"insertPragma": false,
"requirePragma": false,
"proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "auto",
"rangeStart": 0
}
+41
View File
@@ -0,0 +1,41 @@
# 在单参数箭头函数中始终添加括号
arrowParens: "always"
# JSX 多行元素的闭合标签另起一行
bracketSameLine: false
# 对象字面量中的括号之间添加空格
bracketSpacing: true
# 自动格式化嵌入的代码(如 Markdown 和 HTML 内的代码)
embeddedLanguageFormatting: "auto"
# 忽略 HTML 空白敏感度,将空白视为非重要内容
htmlWhitespaceSensitivity: "ignore"
# 不插入 @prettier 的 pragma 注释
insertPragma: false
# 在 JSX 中使用双引号
jsxSingleQuote: false
# 每行代码的最大长度限制为 100 字符
printWidth: 100
# 在 Markdown 中保留原有的换行格式
proseWrap: "preserve"
# 仅在必要时添加对象属性的引号
quoteProps: "as-needed"
# 不要求文件开头插入 @prettier 的 pragma 注释
requirePragma: false
# 在语句末尾添加分号
semi: true
# 使用双引号而不是单引号
singleQuote: false
# 缩进使用 2 个空格
tabWidth: 2
# 在多行元素的末尾添加逗号(ES5 支持的对象、数组等)
trailingComma: "es5"
# 使用空格而不是制表符缩进
useTabs: false
# Vue 文件中的 <script> 和 <style> 不增加额外的缩进
vueIndentScriptAndStyle: false
# 根据系统自动检测换行符
endOfLine: "auto"
# 对 HTML 文件应用特定格式化规则
overrides:
- files: "*.html"
options:
parser: "html"
+4 -2
View File
@@ -3,7 +3,9 @@ node_modules
public public
.husky .husky
.vscode .vscode
.idea
*.sh
*.md
src/components/Layout/MenuLeft/index.vue
src/assets src/assets
stats.html stats.html
+40 -62
View File
@@ -1,82 +1,60 @@
module.exports = { module.exports = {
// 继承推荐规范配置 // 继承推荐规范配置
extends: [ extends: [
'stylelint-config-standard', "stylelint-config-standard",
'stylelint-config-recommended-scss', "stylelint-config-recommended",
'stylelint-config-recommended-vue/scss', "stylelint-config-recommended-scss",
'stylelint-config-html/vue', "stylelint-config-recommended-vue/scss",
'stylelint-config-recess-order' "stylelint-config-html/vue",
"stylelint-config-recess-order",
],
plugins: [
"stylelint-prettier", // 统一代码风格,格式冲突时以 Prettier 规则为准
], ],
// 指定不同文件对应的解析器 // 指定不同文件对应的解析器
overrides: [ overrides: [
{ {
files: ['**/*.{vue,html}'], files: ["**/*.{vue,html}"],
customSyntax: 'postcss-html' customSyntax: "postcss-html",
}, },
{ {
files: ['**/*.{css,scss}'], files: ["**/*.{css,scss}"],
customSyntax: 'postcss-scss' customSyntax: "postcss-scss",
} },
], ],
// 自定义规则 // 自定义规则
rules: { rules: {
'import-notation': 'string', // 指定导入CSS文件的方式("string"|"url") "prettier/prettier": true, // 强制执行 Prettier 格式化规则(需配合 .prettierrc 配置文件)
'selector-class-pattern': null, // 选择器类名命名规则 "declaration-property-value-no-unknown": null, // 允许非常规数值格式 ,如 height: calc(100% - 50)
'custom-property-pattern': null, // 自定义属性命名规则 // Tailwind CSS v4 使用 @reference 引入主题而不注入样式,需放行
'keyframes-name-pattern': null, // 动画帧节点样式命名规则 "scss/at-rule-no-unknown": [
'no-descending-specificity': null, // 允许无降序特异性
'no-empty-source': null, // 允许空样式
'property-no-vendor-prefix': null, // 允许属性前缀
// 允许 global 、export 、deep伪类
'selector-pseudo-class-no-unknown': [
true, true,
{ {
ignorePseudoClasses: ['global', 'export', 'deep'] ignoreAtRules: ["reference"],
} },
],
"import-notation": "string", // 指定导入CSS文件的方式("string"|"url")
"selector-class-pattern": null, // 选择器类名命名规则
"custom-property-pattern": null, // 自定义属性命名规则
"keyframes-name-pattern": null, // 动画帧节点样式命名规则
"no-descending-specificity": null, // 允许无降序特异性
"no-empty-source": null, // 允许空样式
"property-no-vendor-prefix": null, // 允许属性前缀
// 允许 global 、export 、deep伪类
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["global", "export", "deep"],
},
], ],
// 允许未知属性 // 允许未知属性
'property-no-unknown': [ "property-no-unknown": [
true, true,
{ {
ignoreProperties: [] ignoreProperties: [],
} },
], ],
// 允许未知规则 // 允许未知规则
'at-rule-no-unknown': [ "at-rule-no-unknown": null, // 禁用默认的未知 at-rule 检查,
true, },
{ };
ignoreAtRules: [
'apply',
'use',
'mixin',
'include',
'extend',
'each',
'if',
'else',
'for',
'while',
'reference'
]
}
],
'scss/at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'apply',
'use',
'mixin',
'include',
'extend',
'each',
'if',
'else',
'for',
'while',
'reference'
]
}
]
}
}
+44 -97
View File
@@ -1,104 +1,51 @@
<img src="https://www.qiniu.lingchen.kim/github-cover-light6.webp" /> # frontend
<br /> ## 项目结构
<h1 align="center">Art Design Pro</h1>
<p align="center">A backend system template that combines design aesthetics with efficient development, helping you quickly build professional-grade applications</p>
<div align="center">English | <a href="./README.zh-CN.md">简体中文</a></div>
<br /> ```sh
<div align="center"> FastapiAdmin/frontend/new-web
├─ docs # 项目文档工程
├─ public # 静态资源文件
│ └─ docs # 帮助文档模块
├─ src # 源代码
│ ├─ api # 接口文件
│ ├─ assets # 静态资源文件
│ ├─ components # 组件模块
│ ├─ constants # 常量模块
│ ├─ lang # 语言模块
│ ├─ layouts # 布局模块
│ ├─ plugins # 插件模块
│ ├─ router # 路由模块
│ ├─ store # 状态管理模块
│ ├─ styles # 样式模块
│ ├─ types # 类型模块
│ ├─ utils # 工具模块
│ ├─ view # 视图模块
│ ├─ App.vue # 根组件
│ ├─ main.js # 入口文件
│ └─ settings.js # 全局样式文件
├─ .env.development # 项目开发环境配置
├─ .env.production # 项目生产环境配置
├─ index.html # 模板文件
├─ package.json # 项目依赖文件
├─ tsconfig.json # ts配置文件
├─ uno.config.json # uno配置文件
├─ vite.config.js # vite服务配置文件
└─ README.md # 项目说明文档
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) [![github stars](https://img.shields.io/github/stars/Daymychen/art-design-pro)](https://github.com/Daymychen/art-design-pro/stargazers) [![github forks](https://img.shields.io/github/forks/Daymychen/art-design-pro)](https://github.com/Daymychen/art-design-pro/network/members) ```
</div> ## 快速开始
<br />
## What makes this project special? ```sh
# 进入前端工程目录
**Interface Design**: Modern UI design with smooth interactions, focusing on user experience and visual design cd frontend
# 安装依赖
**Quick Start**: Clean architecture + comprehensive documentation, easy for backend developers to use
**Rich Components**: Built-in high-quality components for data display, forms, and more to meet different business scenarios
**Smooth Interactions**: Button clicks, theme switching, page transitions, chart animations - experience comparable to commercial products
**Efficient Development**: Built-in practical APIs like useTable and ArtForm to significantly improve development efficiency
**Clean Scripts**: Built-in one-click cleanup script to quickly remove demo data and get a ready-to-develop base project
## Tech Stack
Development Framework: Vue3, TypeScript, Vite, Element-Plus, Tailwind CSS
Code Standards: Eslint, Prettier, Stylelint, Husky, Lint-staged, cz-git
## Preview
<kbd><img src="https://www.qiniu.lingchen.kim/github-c1.webp" alt="Light Theme"/></kbd>
<kbd><img src="https://www.qiniu.lingchen.kim/github-c2.webp" alt="Light Theme"/></kbd>
<kbd><img src="https://www.qiniu.lingchen.kim/github-c4.webp" alt="Dark Theme"/></kbd>
<kbd><img src="https://www.qiniu.lingchen.kim/github-c5.webp" alt="Dark Theme"/></kbd>
## Quick Access
[Live Demo](https://www.artd.pro) | [Official Documentation](https://www.artd.pro/docs) | [Changelog](./CHANGELOG.en.md)
## Installation & Setup
```bash
# Install dependencies
pnpm install pnpm install
# 启动前端服务
# If pnpm install fails, try using the command below pnpm run dev
pnpm install --ignore-scripts # 构建前端, 生成 `frontend/dist` 目录
pnpm run build
# Start local development environment # 运行命令,查看未用到的依赖
pnpm dev depcheck
# Build for production
pnpm build
``` ```
## Clean Version
The project includes a cleanup script to quickly remove demo data and provide developers with a ready-to-develop base project
```bash
pnpm clean:dev
```
## Technical Support
QQ Group: <a href="https://qm.qq.com/cgi-bin/qm/qr?k=Gg6yzZLFaNgmRhK0T5Qcjf7-XcAFWWXm&jump_from=webapi&authKey=YpRKVJQyFKYbGTiKw0GJ/YQXnNF+GdXNZC5beQQqnGZTvuLlXoMO7nw5fNXvmVhA">1038930070</a> (Click the link to join the group chat)
## Browser Compatibility
Supports modern mainstream browsers including Chrome, Safari, Firefox, and more.
## Contributing
We sincerely welcome and appreciate the support of every contributor! Whether you have new ideas, feature suggestions, or code optimizations, you can participate in the following ways:
Submit Pull Requests: Share your code and help the project grow.
Create GitHub Issues: Provide bug feedback or new feature suggestions to help us improve together.
Every contribution you make takes this project one step further! Come join our open source community!
## Continuous Optimization & Extension
The project maintains active updates, supports the latest frontend tech stack, is compatible with mainstream frameworks, and ensures long-term stability and extensibility. Community-driven feedback mechanisms allow your needs to be quickly integrated into project iterations.
## Donation
If you feel this project has reduced your development costs and solved problems in your work/life, you can support us through the following ways:
<img src="https://www.qiniu.lingchen.kim/%E7%BB%84%202%402x%202.png" alt="Donation QR Code"/>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=Daymychen/art-design-pro&type=Date)](https://www.star-history.com/#Daymychen/art-design-pro&Date)
-104
View File
@@ -1,104 +0,0 @@
<img src="https://www.qiniu.lingchen.kim/github-cover-light6.webp" />
<br />
<h1 align="center">Art Design Pro</h1>
<p align="center">一款兼具设计美学与高效开发的后台系统模版,助你快速构建专业级应用</p>
<div align="center">简体中文 | <a href="./README.md">English</a></div>
<br />
<div align="center">
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) [![github stars](https://img.shields.io/github/stars/Daymychen/art-design-pro)](https://github.com/Daymychen/art-design-pro/stargazers) [![github forks](https://img.shields.io/github/forks/Daymychen/art-design-pro)](https://github.com/Daymychen/art-design-pro/network/members)
</div>
<br />
## 这个项目有什么特别的呢?
**界面设计**:现代化 UI 设计,流畅交互,以用户体验与视觉设计为核心
**极速上手**:简洁架构 + 完整文档,后端开发者也能轻松使用
**丰富组件**:内置数据展示、表单等多种高质量组件,满足不同业务场景的需求
**丝滑交互**:按钮点击、主题切换、页面过渡、图表动画,体验媲美商业产品
**高效开发**:内置 useTable、ArtForm 等实用 API,显著提升开发效率
**精简脚本**:内置一键清理脚本,可快速清理演示数据,立即得到可开发的基础项目
## 技术栈
开发框架:Vue3、TypeScript、Vite、Element-Plus、Tailwind CSS
代码规范:Eslint、Prettier、Stylelint、Husky、Lint-staged、cz-git
## 预览
<kbd><img src="https://www.qiniu.lingchen.kim/github-c1.webp" alt="浅色主题"/></kbd>
<kbd><img src="https://www.qiniu.lingchen.kim/github-c2.webp" alt="浅色主题"/></kbd>
<kbd><img src="https://www.qiniu.lingchen.kim/github-c4.webp" alt="暗黑主题"/></kbd>
<kbd><img src="https://www.qiniu.lingchen.kim/github-c5.webp" alt="暗黑主题"/></kbd>
## 快速访问
[演示地址](https://www.artd.pro) | [官方文档](https://www.artd.pro/docs) | [更新日志](./CHANGELOG.md)
## 安装运行
```bash
# 安装依赖
pnpm install
# 如果 pnpm install 安装失败,尝试使用下面的命令安装依赖
pnpm install --ignore-scripts
# 本地开发环境启动
pnpm dev
# 生产环境打包
pnpm build
```
## 精简版本
项目内置精简脚本,可快速移除项目中的演示数据,让开发者获得一个可快速开发的基础项目
```bash
pnpm clean:dev
```
## 技术支持
QQ群:<a href="https://qm.qq.com/cgi-bin/qm/qr?k=Gg6yzZLFaNgmRhK0T5Qcjf7-XcAFWWXm&jump_from=webapi&authKey=YpRKVJQyFKYbGTiKw0GJ/YQXnNF+GdXNZC5beQQqnGZTvuLlXoMO7nw5fNXvmVhA">1038930070</a>(点击链接加入群聊)
## 兼容性
支持 Chrome、Safari、Firefox 等现代主流浏览器。
## 贡献
我们真诚欢迎并感谢每一位贡献者的支持!无论您有新想法、功能建议还是代码优化,都可以通过以下方式参与:
提交 Pull Request:分享您的代码,助力项目成长。
创建 GitHub Issue:提出 bug 反馈或新功能建议,让我们一起完善。
您的每一点贡献都让这个项目更进一步!快来加入我们的开源社区吧!
## 持续优化与扩展
项目保持活跃更新,支持最新前端技术栈,兼容主流框架,确保长期稳定性和扩展性。社区驱动的反馈机制,让你的需求快速融入项目迭代。
## 捐赠
如果你觉得这个项目为你减少了开发成本、化解了工作 / 生活里的难题,可以通过以下方式支持一下~
<img src="https://www.qiniu.lingchen.kim/%E7%BB%84%202%402x%202.png" alt="捐赠二维码"/>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=Daymychen/art-design-pro&type=Date)](https://www.star-history.com/#Daymychen/art-design-pro&Date)
+59 -63
View File
@@ -1,87 +1,83 @@
/**
* commitlint 配置文件
* 文档
* https://commitlint.js.org/#/reference-rules
* https://cz-git.qbb.sh/zh/guide/
*/
module.exports = { module.exports = {
// 继承的规则 // 继承的规则
extends: ['@commitlint/config-conventional'], extends: ["@commitlint/config-conventional"],
// 自定义规则 // 自定义规则
rules: { rules: {
// @see https://commitlint.js.org/#/reference-rules
// 提交类型枚举,git提交type必须是以下类型 // 提交类型枚举,git提交type必须是以下类型
'type-enum': [ "type-enum": [
2, 2,
'always', "always",
[ [
'feat', // 新增功能 "feat", // 新增功能
'fix', // 修复缺陷 "fix", // 修复缺陷
'docs', // 文档变更 "docs", // 文档变更
'style', // 代码格式(不影响功能,例如空格、分号等格式修正) "style", // 代码格式(不影响功能,例如空格、分号等格式修正)
'refactor', // 代码重构(不包括 bug 修复、功能新增) "refactor", // 代码重构(不包括 bug 修复、功能新增)
'perf', // 性能优化 "perf", // 性能优化
'test', // 添加疏漏测试或已有测试改动 "test", // 添加疏漏测试或已有测试改动
'build', // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等) "build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
'ci', // 修改 CI 配置、脚本 "ci", // 修改 CI 配置、脚本
'revert', // 回滚 commit "revert", // 回滚 commit
'chore', // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例) "chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
'wip' // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例) "wip", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
] ],
], ],
'subject-case': [0] // subject大小写不做校验 "subject-case": [0], // subject大小写不做校验
}, },
prompt: { prompt: {
messages: { messages: {
type: '选择你要提交的类型 :', type: "选择你要提交的类型 :",
scope: '选择一个提交范围(可选):', scope: "选择一个提交范围(可选):",
customScope: '请输入自定义的提交范围 :', customScope: "请输入自定义的提交范围 :",
subject: '填写简短精炼的变更描述 :\n', subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n', body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n', breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: '选择关联issue前缀(可选):', footerPrefixesSelect: "选择关联issue前缀(可选):",
customFooterPrefix: '输入自定义issue前缀 :', customFooterPrefix: "输入自定义issue前缀 :",
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n', footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
generatingByAI: '正在通过 AI 生成你的提交简短描述...', generatingByAI: "正在通过 AI 生成你的提交简短描述...",
generatedSelectByAI: '选择一个 AI 生成的简短描述:', generatedSelectByAI: "选择一个 AI 生成的简短描述:",
confirmCommit: '是否提交或修改commit ?' confirmCommit: "是否提交或修改commit ?",
}, },
// prettier-ignore // prettier-ignore
types: [ types: [
{ value: "feat", name: "feat: 新增功能" }, { value: "feat", name: "特性: 新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "fix: 修复缺陷" }, { value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "docs: 文档变更" }, { value: "docs", name: "文档: 📝 文档变更(更新README文件,或者注释)", emoji: ":memo:" },
{ value: "style", name: "style: 代码格式(不影响功能,例如空格、分号等格式修正)" }, { value: "style", name: "格式: 🌈 代码格式(空格、格式化、缺失的分号等)", emoji: ":lipstick:" },
{ value: "refactor", name: "refactor: 代码重构(不包括 bug 修复、功能新增)" }, { value: "refactor", name: "重构: 🔄 代码重构(不修复错误也不添加特性的代码更改)", emoji: ":recycle:" },
{ value: "perf", name: "perf: 性能优化" }, { value: "perf", name: "性能: 🚀 性能优化", emoji: ":zap:" },
{ value: "test", name: "test: 添加疏漏测试或已有测试改动" }, { value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "build: 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)" }, { value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "ci: 修改 CI 配置、脚本" }, { value: "ci", name: "集成: ⚙️ 修改 CI 配置、脚本", emoji: ":ferris_wheel:"},
{ value: "revert", name: "revert: 回滚 commit" }, { value: "revert", name: "回退: ↩️ 回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "chore: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)" }, { value: "chore", name: "其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
{ value: "wip", name: "开发中: 🚧 开发阶段临时提交", emoji: ":construction:"},
], ],
useEmoji: true, useEmoji: true,
emojiAlign: 'center', emojiAlign: "center",
useAI: false, useAI: false,
aiNumber: 1, aiNumber: 1,
themeColorCode: '', themeColorCode: "",
scopes: [], scopes: [],
allowCustomScopes: true, allowCustomScopes: true,
allowEmptyScopes: true, allowEmptyScopes: true,
customScopesAlign: 'bottom', customScopesAlign: "bottom",
customScopesAlias: 'custom', customScopesAlias: "custom",
emptyScopesAlias: 'empty', emptyScopesAlias: "empty",
upperCaseSubject: false, upperCaseSubject: false,
markBreakingChangeMode: false, markBreakingChangeMode: false,
allowBreakingChanges: ['feat', 'fix'], allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100, breaklineNumber: 100,
breaklineChar: '|', breaklineChar: "|",
skipQuestions: ['breaking', 'footerPrefix', 'footer'], // 跳过的步骤 skipQuestions: [],
issuePrefixes: [{ value: 'closed', name: 'closed: ISSUES has been processed' }], issuePrefixes: [{ value: "closed", name: "closed: ISSUES has been processed" }],
customIssuePrefixAlign: 'top', customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: 'skip', emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: 'custom', customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true, allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true, allowEmptyIssuePrefix: true,
confirmColorize: true, confirmColorize: true,
@@ -89,9 +85,9 @@ module.exports = {
maxSubjectLength: Infinity, maxSubjectLength: Infinity,
minSubjectLength: 0, minSubjectLength: 0,
scopeOverrides: undefined, scopeOverrides: undefined,
defaultBody: '', defaultBody: "",
defaultIssues: '', defaultIssues: "",
defaultScope: '', defaultScope: "",
defaultSubject: '' defaultSubject: "",
} },
} };
+168 -55
View File
@@ -1,73 +1,81 @@
// 从 URL 和路径模块中导入必要的功能 // ESLint 配置文件
import fs from 'fs' import fs from 'fs'
import path, { dirname } from 'path' import path, { dirname } from 'path'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
// 从 ESLint 插件中导入推荐配置
import pluginJs from '@eslint/js' import pluginJs from '@eslint/js'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import pluginVue from 'eslint-plugin-vue' import pluginVue from 'eslint-plugin-vue'
import globals from 'globals' import globals from 'globals'
import tseslint from 'typescript-eslint' import tseslint from 'typescript-eslint'
// 使用 import.meta.url 获取当前模块的路径
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename) const __dirname = dirname(__filename)
// 读取 .auto-import.json 文件的内容,并将其解析为 JSON 对象
const autoImportConfig = JSON.parse( const autoImportConfig = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '.auto-import.json'), 'utf-8') fs.readFileSync(path.resolve(__dirname, '.auto-import.json'), 'utf-8')
) )
export default [ // Element Plus 组件全局配置
// 指定文件匹配规则 const elementPlusComponents = {
{ ElInput: 'readonly',
files: ['**/*.{js,mjs,cjs,ts,tsx,vue}'] ElSelect: 'readonly',
}, ElSwitch: 'readonly',
// 指定全局变量和环境 ElCascader: 'readonly',
{ ElInputNumber: 'readonly',
languageOptions: { ElTimePicker: 'readonly',
globals: { ElTimeSelect: 'readonly',
...globals.browser, ElDatePicker: 'readonly',
...globals.node ElTreeSelect: 'readonly',
} ElText: 'readonly',
} ElRadioGroup: 'readonly',
}, ElCheckboxGroup: 'readonly',
// 扩展配置 ElOption: 'readonly',
pluginJs.configs.recommended, ElRadio: 'readonly',
...tseslint.configs.recommended, ElCheckbox: 'readonly',
...pluginVue.configs['flat/essential'], ElInputTag: 'readonly',
// 自定义规则 ElForm: 'readonly',
{ ElFormItem: 'readonly',
// 针对所有 JavaScript、TypeScript 和 Vue 文件应用以下配置 ElTable: 'readonly',
files: ['**/*.{js,mjs,cjs,ts,tsx,vue}'], ElTableColumn: 'readonly',
ElButton: 'readonly',
ElDialog: 'readonly',
ElPagination: 'readonly',
ElMessage: 'readonly',
ElMessageBox: 'readonly',
ElNotification: 'readonly',
ElTree: 'readonly',
ElDropdown: 'readonly',
ElDropdownMenu: 'readonly',
ElDropdownItem: 'readonly',
ElAvatar: 'readonly',
ElBadge: 'readonly',
ElCard: 'readonly',
ElCol: 'readonly',
ElRow: 'readonly',
ElContainer: 'readonly',
ElHeader: 'readonly',
ElAside: 'readonly',
ElMain: 'readonly',
ElFooter: 'readonly',
ElLink: 'readonly',
ElDivider: 'readonly',
ElImage: 'readonly',
ElProgress: 'readonly',
ElSkeleton: 'readonly',
ElSlider: 'readonly',
ElSwitch: 'readonly',
ElTag: 'readonly',
ElTooltip: 'readonly',
ElPopover: 'readonly',
ElPopconfirm: 'readonly',
ElDrawer: 'readonly',
ElAlert: 'readonly',
ElLoading: 'readonly',
}
languageOptions: { export default [
globals: { // 忽略文件配置
// 合并从 autoImportConfig 中读取的全局变量配置
...autoImportConfig.globals,
// TypeScript 全局命名空间
Api: 'readonly'
}
},
rules: {
quotes: ['error', 'single'], // 使用单引号
semi: ['error', 'never'], // 语句末尾不加分号
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'@typescript-eslint/no-explicit-any': 'off', // 禁用 any 检查
'vue/multi-word-component-names': 'off', // 禁用对 Vue 组件名称的多词要求检查
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
'no-unexpected-multiline': 'error' // 禁止空余的多行
}
},
// vue 规则
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: { parser: tseslint.parser }
}
},
// 忽略文件
{ {
ignores: [ ignores: [
'node_modules', 'node_modules',
@@ -75,9 +83,114 @@ export default [
'public', 'public',
'.vscode/**', '.vscode/**',
'src/assets/**', 'src/assets/**',
'src/utils/console.ts' 'src/utils/console.ts',
] '**/*.min.*',
'**/auto-imports.d.ts',
'**/components.d.ts',
'**/types/**/*.d.ts',
],
}, },
// 基础配置
{
files: ['**/*.{js,mjs,cjs,ts,tsx,vue}'],
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
...globals.es2022,
},
},
},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
...pluginVue.configs['flat/essential'],
// 全局配置
{
files: ['**/*.{js,mjs,cjs,ts,tsx,vue}'],
languageOptions: {
globals: {
...autoImportConfig.globals,
Api: 'readonly',
...elementPlusComponents,
// 全局类型定义
PageQuery: 'readonly',
PageResult: 'readonly',
UserListItem: 'readonly',
UserSearchParams: 'readonly',
RoleListItem: 'readonly',
RoleSearchParams: 'readonly',
OptionType: 'readonly',
ApiResponse: 'readonly',
ExcelResult: 'readonly',
CommonType: 'readonly',
updatorType: 'readonly',
creatorType: 'readonly',
TagView: 'readonly',
AppSettings: 'readonly',
__APP_INFO__: 'readonly',
UploadFilePath: 'readonly',
},
},
rules: {
// 代码风格
quotes: ['error', 'single'],
semi: ['error', 'never'],
'no-var': 'error',
'prefer-const': 'error',
'object-shorthand': 'error',
// 最佳实践
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'eqeqeq': 'off',
'no-multi-spaces': 'error',
'no-multiple-empty-lines': ['warn', { max: 1 }],
'no-unexpected-multiline': 'error',
// TypeScript 规则
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
// Vue 规则
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'error',
'vue/no-unused-vars': 'error',
'vue/no-mutating-props': 'off',
'vue/valid-v-for': 'warn',
'vue/no-template-shadow': 'warn',
'vue/return-in-computed-property': 'warn',
'vue/block-order': ['error', { order: ['template', 'script', 'style'] }],
'vue/html-self-closing': [
'error',
{
html: { void: 'always', normal: 'never', component: 'always' },
svg: 'always',
math: 'always',
},
],
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
},
},
// Vue 文件特定配置
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: { parser: tseslint.parser },
},
},
// prettier 配置 // prettier 配置
eslintPluginPrettierRecommended eslintPluginPrettierRecommended,
] ]
+14 -13
View File
@@ -1,14 +1,15 @@
<!doctype html> <!doctype html>
<html> <html lang="zh-CN">
<head> <head>
<title>Art Design Pro</title> <title>%VITE_APP_TITLE%</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板" />
<meta <meta
name="description" name="keywords"
content="Art Design Pro - A modern admin dashboard template built with Vue 3, TypeScript, and Element Plus." content="vue,element-plus,typescript,vue-element-admin,vue3-element-admin"
/> />
<link rel="shortcut icon" type="image/x-icon" href="src/assets/images/favicon.ico" /> <link rel="shortcut icon" type="image/x-icon" href="src/assets/images/favicon.png" />
<style> <style>
/* 防止页面刷新时白屏的初始样式 */ /* 防止页面刷新时白屏的初始样式 */
@@ -23,20 +24,20 @@
<script> <script>
// 初始化 html class 主题属性 // 初始化 html class 主题属性
;(function () { (function () {
try { try {
if (typeof Storage === 'undefined' || !window.localStorage) { if (typeof Storage === "undefined" || !window.localStorage) {
return return;
} }
const themeType = localStorage.getItem('sys-theme') const themeType = localStorage.getItem("sys-theme");
if (themeType === 'dark') { if (themeType === "dark") {
document.documentElement.classList.add('dark') document.documentElement.classList.add("dark");
} }
} catch (e) { } catch (e) {
console.warn('Failed to apply initial theme:', e) console.warn("Failed to apply initial theme:", e);
} }
})() })();
</script> </script>
</head> </head>
+111 -41
View File
@@ -1,21 +1,38 @@
{ {
"name": "art-design-pro", "name": "fastapiadmin",
"version": "0.0.0", "description": "Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板",
"version": "2.2.0",
"private": true,
"type": "module", "type": "module",
"engines": {
"node": ">=20.19.0",
"pnpm": ">=8.8.0"
},
"scripts": { "scripts": {
"dev": "vite --open", "i": "pnpm install",
"dev": "vite",
"dev:force": "vite --force",
"prod": "vite --mode prod",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
"serve": "vite preview", "build:pro": "pnpm vite build --mode pro",
"lint": "eslint", "build:gitee": "pnpm vite build --mode gitee",
"fix": "eslint --fix", "build:dev": "pnpm vite build --mode dev",
"build:test": "pnpm vite build --mode test",
"serve:pro": "pnpm vite preview --mode pro",
"serve:dev": "pnpm vite preview --mode dev",
"serve:test": "pnpm vite preview --mode test",
"clean": "pnpx rimraf node_modules",
"ts:check": "pnpm vue-tsc --noEmit --skipLibCheck",
"npm:check": "pnpx npm-check-updates -u",
"clean:cache": "pnpx rimraf node_modules/.cache node_modules/.vite",
"prepare": "husky install",
"icon": "esno ./scripts/icon.ts",
"preview": "vite preview",
"type-check": "vue-tsc --noEmit",
"lint": "pnpm run lint:eslint && pnpm run lint:prettier && pnpm run lint:stylelint",
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"",
"lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.cjs",
"lint:eslint": "eslint --cache \"src/**/*.{vue,ts,js}\" --fix",
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"", "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix", "lint:stylelint": "stylelint --cache \"**/*.{css,scss,vue}\" --fix",
"lint:lint-staged": "lint-staged", "fix": "eslint --fix",
"prepare": "husky",
"commit": "git-cz", "commit": "git-cz",
"clean:dev": "tsx scripts/clean-dev.ts" "clean:dev": "tsx scripts/clean-dev.ts"
}, },
@@ -55,61 +72,98 @@
"@element-plus/icons-vue": "^2.3.2", "@element-plus/icons-vue": "^2.3.2",
"@iconify/vue": "^5.0.0", "@iconify/vue": "^5.0.0",
"@tailwindcss/vite": "^4.1.14", "@tailwindcss/vite": "^4.1.14",
"@vue/reactivity": "^3.5.21", "@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.3",
"@vue-flow/core": "^1.48.1",
"@vue-flow/minimap": "^1.5.4",
"@vueuse/core": "^13.9.0", "@vueuse/core": "^13.9.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor-next/editor": "^5.6.49",
"@wangeditor/editor-for-vue": "next", "@wangeditor-next/editor-for-vue": "^5.1.14",
"animate.css": "^4.1.1",
"axios": "^1.12.2", "axios": "^1.12.2",
"clipboard": "^2.0.11",
"codemirror": "^5.65.19",
"codemirror-editor-vue3": "^2.8.0",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dagre": "^0.8.5",
"dayjs": "^1.11.13",
"dompurify": "^3.3.1",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"element-plus": "^2.11.2", "element-plus": "^2.11.2",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"highlight.js": "^11.10.0", "highlight.js": "^11.11.1",
"js-beautify": "^1.15.4",
"markdown-it": "^14.1.0",
"markdown-it-highlightjs": "^4.2.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"ohash": "^2.0.11", "ohash": "^2.0.11",
"path-browserify": "^1.0.1",
"path-to-regexp": "^8.2.0",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.3.0", "pinia-plugin-persistedstate": "^4.4.1",
"qrcode.vue": "^3.6.0", "qrcode.vue": "^3.6.0",
"qs": "^6.14.0",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"vue": "^3.5.21", "vue": "^3.5.21",
"vue-draggable-plus": "^0.6.0", "vue-draggable-plus": "^0.6.0",
"vue-i18n": "^9.14.0", "vue-i18n": "^11.1.10",
"vue-json-pretty": "^2.5.0",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"vue-web-terminal": "^3.4.1",
"vue3-cron-plus": "^0.1.9",
"vuedraggable": "^4.1.0",
"xgplayer": "^3.0.20", "xgplayer": "^3.0.20",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.4.1", "@commitlint/cli": "^19.4.1",
"@commitlint/config-conventional": "^19.4.1", "@commitlint/config-conventional": "^19.4.1",
"@eslint/js": "^9.9.1", "@eslint/js": "^9.32.0",
"@iconify/utils": "^2.3.0",
"@types/codemirror": "^5.60.16",
"@types/dagre": "^0.7.53",
"@types/dompurify": "^3.2.0",
"@types/file-saver": "^2.0.7",
"@types/markdown-it": "^14.1.2",
"@types/node": "^24.0.5", "@types/node": "^24.0.5",
"@typescript-eslint/eslint-plugin": "^8.3.0", "@types/nprogress": "^0.2.3",
"@typescript-eslint/parser": "^8.3.0", "@types/path-browserify": "^1.0.3",
"@types/qs": "^6.14.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.1",
"@vue/compiler-sfc": "^3.0.5", "@vue/compiler-sfc": "^3.0.5",
"commitizen": "^4.3.0", "autoprefixer": "^10.4.21",
"cz-git": "^1.11.1", "commitizen": "^4.3.1",
"eslint": "^9.9.1", "cz-git": "^1.12.0",
"eslint-config-prettier": "^9.1.0", "eslint": "^9.32.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-vue": "^9.27.0", "eslint-plugin-prettier": "^5.5.3",
"globals": "^15.9.0", "eslint-plugin-vue": "^10.4.0",
"husky": "^9.1.5", "fs-extra": "^11.2.0",
"globals": "^15.15.0",
"husky": "^9.1.7",
"lint-staged": "^15.5.2", "lint-staged": "^15.5.2",
"prettier": "^3.5.3", "postcss": "^8.5.6",
"postcss-html": "^1.8.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.6.2",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.81.0", "sass": "^1.89.2",
"stylelint": "^16.20.0", "stylelint": "^16.25.0",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^4.6.0", "stylelint-config-recess-order": "^6.1.0",
"stylelint-config-recommended": "^15.0.0",
"stylelint-config-recommended-scss": "^14.1.0", "stylelint-config-recommended-scss": "^14.1.0",
"stylelint-config-recommended-vue": "^1.5.0", "stylelint-config-recommended-vue": "^1.6.1",
"stylelint-config-standard": "^36.0.1", "stylelint-config-standard": "^36.0.1",
"terser": "^5.36.0", "stylelint-prettier": "^5.0.3",
"terser": "^5.43.1",
"tsx": "^4.20.3", "tsx": "^4.20.3",
"typescript": "~5.6.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.9.0", "typescript-eslint": "^8.38.0",
"unplugin-auto-import": "^20.2.0", "unplugin-auto-import": "^20.2.0",
"unplugin-element-plus": "^0.10.0", "unplugin-element-plus": "^0.10.0",
"unplugin-vue-components": "^29.1.0", "unplugin-vue-components": "^29.1.0",
@@ -117,7 +171,23 @@
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^7.7.6", "vite-plugin-vue-devtools": "^7.7.6",
"vue-demi": "^0.14.9", "vue-demi": "^0.14.9",
"vue-img-cutter": "^3.0.5", "vue-eslint-parser": "^10.2.0",
"vue-tsc": "~2.1.6" "vue-tsc": "^2.2.12"
} },
"packageManager": "pnpm@9.15.3",
"engines": {
"node": ">=20.19.0",
"npm": ">=10.0.0",
"pnpm": ">=8.8.0"
},
"repository": {
"type": "git",
"url": "https://gitee.com/fastapiadmin/FastapiAdmin.git"
},
"bugs": {
"url": "https://gitee.com/fastapiadmin/FastapiAdmin/issues"
},
"author": "fastapiadmin <948080782@qq.com>",
"license": "MIT",
"homepage": "https://gitee.com/fastapiadmin/FastapiAdmin"
} }
+3120 -4373
View File
File diff suppressed because it is too large Load Diff
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1400 800">
<defs>
<style>
@media (prefers-color-scheme: dark) {
#bg-rect { fill: #101a29; }
#blueGlow-rect { fill: url(#blueGlowDark); }
#blueGlow2-rect { fill: url(#blueGlow2Dark); }
#pinkPurpleGlow-rect { fill: url(#pinkPurpleGlowDark); }
}
</style>
<!-- 亮色主题渐变 -->
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#f9fcff" />
<stop offset="100%" stop-color="#f5f9fd" />
</linearGradient>
<!-- 中间区域淡蓝白光晕 -->
<radialGradient id="blueGlow" cx="50%" cy="50%" r="70%" fx="50%" fy="50%">
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.9" />
<stop offset="50%" stop-color="#f0f8ff" stop-opacity="0.5" />
<stop offset="100%" stop-color="#eef7fd" stop-opacity="0" />
</radialGradient>
<!-- 左上角蓝白光晕 -->
<radialGradient id="blueGlow2" cx="15%" cy="15%" r="40%" fx="15%" fy="15%">
<stop offset="0%" stop-color="#d9efff" stop-opacity="0.85" />
<stop offset="40%" stop-color="#e5f4fd" stop-opacity="0.6" />
<stop offset="100%" stop-color="#e9f5fd" stop-opacity="0" />
</radialGradient>
<!-- 右下角粉紫色光晕 -->
<radialGradient id="pinkPurpleGlow" cx="85%" cy="85%" r="40%" fx="85%" fy="85%">
<stop offset="0%" stop-color="#f7e6f9" stop-opacity="0.8" />
<stop offset="35%" stop-color="#f9edf8" stop-opacity="0.6" />
<stop offset="100%" stop-color="#f8f2f8" stop-opacity="0" />
</radialGradient>
<!-- 暗色主题渐变 -->
<radialGradient id="blueGlowDark" cx="50%" cy="50%" r="70%" fx="50%" fy="50%">
<stop offset="0%" stop-color="#1e3a5e" stop-opacity="0.6" />
<stop offset="50%" stop-color="#1c314e" stop-opacity="0.3" />
<stop offset="100%" stop-color="#1a2d47" stop-opacity="0" />
</radialGradient>
<!-- 左上角蓝白光晕 - 暗色模式 -->
<radialGradient id="blueGlow2Dark" cx="15%" cy="15%" r="40%" fx="15%" fy="15%">
<stop offset="0%" stop-color="#1e3858" stop-opacity="0.85" />
<stop offset="40%" stop-color="#1a304f" stop-opacity="0.6" />
<stop offset="100%" stop-color="#172b45" stop-opacity="0" />
</radialGradient>
<!-- 右下角粉紫色光晕 - 暗色模式 -->
<radialGradient id="pinkPurpleGlowDark" cx="85%" cy="85%" r="40%" fx="85%" fy="85%">
<stop offset="0%" stop-color="#2e2335" stop-opacity="0.85" />
<stop offset="35%" stop-color="#2a2035" stop-opacity="0.6" />
<stop offset="100%" stop-color="#2a202d" stop-opacity="0" />
</radialGradient>
</defs>
<!-- 背景层 -->
<rect id="bg-rect" width="100%" height="100%" fill="url(#bgGradient)" />
<!-- 中间淡蓝白光晕 -->
<rect id="blueGlow-rect" width="100%" height="100%" fill="url(#blueGlow)" />
<!-- 左上蓝白光晕 -->
<rect id="blueGlow2-rect" width="100%" height="100%" fill="url(#blueGlow2)" />
<!-- 右下粉紫光晕 -->
<rect id="pinkPurpleGlow-rect" width="100%" height="100%" fill="url(#pinkPurpleGlow)" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+20
View File
@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" width="410" height="404" viewBox="0 0 410 404" fill="none">
<path
d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z"
fill="url(#paint0_linear)" />
<path
d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z"
fill="url(#paint1_linear)" />
<defs>
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF" />
<stop offset="1" stop-color="#BD34FE" />
</linearGradient>
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989"
gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA83" />
<stop offset="0.0833333" stop-color="#FFDD35" />
<stop offset="1" stop-color="#FFA800" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+339 -346
View File
@@ -1,63 +1,63 @@
// scripts/clean-dev.ts // scripts/clean-dev.ts
import fs from 'fs/promises' import fs from "fs/promises";
import path from 'path' import path from "path";
// 现代化颜色主题 // 现代化颜色主题
const theme = { const theme = {
// 基础颜色 // 基础颜色
reset: '\x1b[0m', reset: "\x1b[0m",
bold: '\x1b[1m', bold: "\x1b[1m",
dim: '\x1b[2m', dim: "\x1b[2m",
// 前景色 // 前景色
primary: '\x1b[38;5;75m', // 亮蓝色 primary: "\x1b[38;5;75m", // 亮蓝色
success: '\x1b[38;5;82m', // 亮绿色 success: "\x1b[38;5;82m", // 亮绿色
warning: '\x1b[38;5;220m', // 亮黄色 warning: "\x1b[38;5;220m", // 亮黄色
error: '\x1b[38;5;196m', // 亮红色 error: "\x1b[38;5;196m", // 亮红色
info: '\x1b[38;5;159m', // 青色 info: "\x1b[38;5;159m", // 青色
purple: '\x1b[38;5;141m', // 紫色 purple: "\x1b[38;5;141m", // 紫色
orange: '\x1b[38;5;208m', // 橙色 orange: "\x1b[38;5;208m", // 橙色
gray: '\x1b[38;5;245m', // 灰色 gray: "\x1b[38;5;245m", // 灰色
white: '\x1b[38;5;255m', // 白色 white: "\x1b[38;5;255m", // 白色
// 背景色 // 背景色
bgDark: '\x1b[48;5;235m', // 深灰背景 bgDark: "\x1b[48;5;235m", // 深灰背景
bgBlue: '\x1b[48;5;24m', // 蓝色背景 bgBlue: "\x1b[48;5;24m", // 蓝色背景
bgGreen: '\x1b[48;5;22m', // 绿色背景 bgGreen: "\x1b[48;5;22m", // 绿色背景
bgRed: '\x1b[48;5;52m' // 红色背景 bgRed: "\x1b[48;5;52m", // 红色背景
} };
// 现代化图标集 // 现代化图标集
const icons = { const icons = {
rocket: '🚀', rocket: "🚀",
fire: '🔥', fire: "🔥",
star: '⭐', star: "⭐",
gem: '💎', gem: "💎",
crown: '👑', crown: "👑",
magic: '✨', magic: "✨",
warning: '⚠️', warning: "⚠️",
success: '✅', success: "✅",
error: '❌', error: "❌",
info: '️', info: "️",
folder: '📁', folder: "📁",
file: '📄', file: "📄",
image: '🖼️', image: "🖼️",
code: '💻', code: "💻",
data: '📊', data: "📊",
globe: '🌐', globe: "🌐",
map: '🗺️', map: "🗺️",
chat: '💬', chat: "💬",
bolt: '⚡', bolt: "⚡",
shield: '🛡️', shield: "🛡️",
key: '🔑', key: "🔑",
link: '🔗', link: "🔗",
clean: '🧹', clean: "🧹",
trash: '🗑️', trash: "🗑️",
check: '✓', check: "✓",
cross: '✗', cross: "✗",
arrow: '→', arrow: "→",
loading: '⏳' loading: "⏳",
} };
// 格式化工具 // 格式化工具
const fmt = { const fmt = {
@@ -77,66 +77,66 @@ const fmt = {
// 渐变效果模拟 // 渐变效果模拟
gradient: (text: string) => { gradient: (text: string) => {
const colors = ['\x1b[38;5;75m', '\x1b[38;5;81m', '\x1b[38;5;87m', '\x1b[38;5;159m'] const colors = ["\x1b[38;5;75m", "\x1b[38;5;81m", "\x1b[38;5;87m", "\x1b[38;5;159m"];
const chars = text.split('') const chars = text.split("");
return chars.map((char, i) => `${colors[i % colors.length]}${char}`).join('') + theme.reset return chars.map((char, i) => `${colors[i % colors.length]}${char}`).join("") + theme.reset;
} },
} };
// 创建现代化标题横幅 // 创建现代化标题横幅
function createModernBanner() { function createModernBanner() {
console.log() console.log();
console.log( console.log(
fmt.gradient(' ╔══════════════════════════════════════════════════════════════════╗') fmt.gradient(" ╔══════════════════════════════════════════════════════════════════╗")
) );
console.log( console.log(
fmt.gradient(' ║ ║') fmt.gradient(" ║ ║")
) );
console.log( console.log(
`${icons.rocket} ${fmt.title('ART DESIGN PRO')} ${fmt.subtitle('· 代码精简程序')} ${icons.magic}` `${icons.rocket} ${fmt.title("ART DESIGN PRO")} ${fmt.subtitle("· 代码精简程序")} ${icons.magic}`
) );
console.log( console.log(
`${fmt.dim('为项目移除演示数据,快速切换至开发模式')}` `${fmt.dim("为项目移除演示数据,快速切换至开发模式")}`
) );
console.log( console.log(
fmt.gradient(' ║ ║') fmt.gradient(" ║ ║")
) );
console.log( console.log(
fmt.gradient(' ╚══════════════════════════════════════════════════════════════════╝') fmt.gradient(" ╚══════════════════════════════════════════════════════════════════╝")
) );
console.log() console.log();
} }
// 创建分割线 // 创建分割线
function createDivider(char = '─', color = theme.primary) { function createDivider(char = "─", color = theme.primary) {
console.log(`${color}${' ' + char.repeat(66)}${theme.reset}`) console.log(`${color}${" " + char.repeat(66)}${theme.reset}`);
} }
// 创建卡片样式容器 // 创建卡片样式容器
function createCard(title: string, content: string[]) { function createCard(title: string, content: string[]) {
console.log(` ${fmt.badge('', theme.bgBlue)} ${fmt.title(title)}`) console.log(` ${fmt.badge("", theme.bgBlue)} ${fmt.title(title)}`);
console.log() console.log();
content.forEach((line) => { content.forEach((line) => {
console.log(` ${line}`) console.log(` ${line}`);
}) });
console.log() console.log();
} }
// 进度条动画 // 进度条动画
function createProgressBar(current: number, total: number, text: string, width = 40) { function createProgressBar(current: number, total: number, text: string, width = 40) {
const percentage = Math.round((current / total) * 100) const percentage = Math.round((current / total) * 100);
const filled = Math.round((current / total) * width) const filled = Math.round((current / total) * width);
const empty = width - filled const empty = width - filled;
const filledBar = '█'.repeat(filled) const filledBar = "█".repeat(filled);
const emptyBar = '░'.repeat(empty) const emptyBar = "░".repeat(empty);
process.stdout.write( process.stdout.write(
`\r ${fmt.info('进度')} [${theme.success}${filledBar}${theme.gray}${emptyBar}${theme.reset}] ${fmt.highlight(percentage + '%')})}` `\r ${fmt.info("进度")} [${theme.success}${filledBar}${theme.gray}${emptyBar}${theme.reset}] ${fmt.highlight(percentage + "%")})}`
) );
if (current === total) { if (current === total) {
console.log() console.log();
} }
} }
@@ -146,113 +146,106 @@ const stats = {
deletedPaths: 0, deletedPaths: 0,
failedPaths: 0, failedPaths: 0,
startTime: Date.now(), startTime: Date.now(),
totalFiles: 0 totalFiles: 0,
} };
// 清理目标 // 清理目标
const targets = [ const targets = [
'README.md', "README.md",
'README.zh-CN.md', "README.zh-CN.md",
'CHANGELOG.md', "CHANGELOG.md",
'CHANGELOG.zh-CN.md', "CHANGELOG.zh-CN.md",
'src/views/change', "src/views/change",
'src/views/safeguard', "src/views/safeguard",
'src/views/article', "src/views/article",
'src/views/examples', "src/views/examples",
'src/views/system/nested', "src/views/system/nested",
'src/views/widgets', "src/views/widgets",
'src/views/template', "src/views/template",
'src/views/dashboard/analysis', "src/views/dashboard/analysis",
'src/views/dashboard/ecommerce', "src/views/dashboard/ecommerce",
'src/mock/json', "src/mock/json",
'src/mock/temp/articleList.ts', "src/mock/temp/articleList.ts",
'src/mock/temp/commentDetail.ts', "src/mock/temp/commentDetail.ts",
'src/mock/temp/commentList.ts', "src/mock/temp/commentList.ts",
'src/assets/images/cover', "src/assets/images/cover",
'src/assets/images/safeguard', "src/assets/images/safeguard",
'src/assets/images/3d', "src/assets/images/3d",
'src/components/core/charts/art-map-chart', "src/components/core/charts/art-map-chart",
'src/components/business/comment-widget' "src/components/business/comment-widget",
] ];
// 递归统计文件数量 // 递归统计文件数量
async function countFiles(targetPath: string): Promise<number> { async function countFiles(targetPath: string): Promise<number> {
const fullPath = path.resolve(process.cwd(), targetPath) const fullPath = path.resolve(process.cwd(), targetPath);
try { try {
const stat = await fs.stat(fullPath) const stat = await fs.stat(fullPath);
if (stat.isFile()) { if (stat.isFile()) {
return 1 return 1;
} else if (stat.isDirectory()) { } else if (stat.isDirectory()) {
const entries = await fs.readdir(fullPath) const entries = await fs.readdir(fullPath);
let count = 0 let count = 0;
for (const entry of entries) { for (const entry of entries) {
const entryPath = path.join(targetPath, entry) const entryPath = path.join(targetPath, entry);
count += await countFiles(entryPath) count += await countFiles(entryPath);
} }
return count return count;
} }
} catch { } catch {
return 0 return 0;
} }
return 0 return 0;
} }
// 统计所有目标的文件数量 // 统计所有目标的文件数量
async function countAllFiles(): Promise<number> { async function countAllFiles(): Promise<number> {
let totalCount = 0 let totalCount = 0;
for (const target of targets) { for (const target of targets) {
const count = await countFiles(target) const count = await countFiles(target);
totalCount += count totalCount += count;
} }
return totalCount return totalCount;
} }
// 删除文件和目录 // 删除文件和目录
async function remove(targetPath: string, index: number) { async function remove(targetPath: string, index: number) {
const fullPath = path.resolve(process.cwd(), targetPath) const fullPath = path.resolve(process.cwd(), targetPath);
createProgressBar(index + 1, targets.length, targetPath) createProgressBar(index + 1, targets.length, targetPath);
try { try {
const fileCount = await countFiles(targetPath) const fileCount = await countFiles(targetPath);
await fs.rm(fullPath, { recursive: true, force: true }) await fs.rm(fullPath, { recursive: true, force: true });
stats.deletedFiles += fileCount stats.deletedFiles += fileCount;
stats.deletedPaths++ stats.deletedPaths++;
await new Promise((resolve) => setTimeout(resolve, 50)) await new Promise((resolve) => setTimeout(resolve, 50));
} catch (err) { } catch (err) {
stats.failedPaths++ stats.failedPaths++;
console.log() console.log();
console.log(` ${icons.error} ${fmt.error('删除失败')}: ${fmt.highlight(targetPath)}`) console.log(` ${icons.error} ${fmt.error("删除失败")}: ${fmt.highlight(targetPath)}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
// 清理路由模块 // 清理路由模块
async function cleanRouteModules() { async function cleanRouteModules() {
const modulesPath = path.resolve(process.cwd(), 'src/router/modules') const modulesPath = path.resolve(process.cwd(), "src/router/modules");
try { try {
// 删除演示相关的路由模块 // 删除演示相关的路由模块
const modulesToRemove = [ const modulesToRemove = ["template.ts", "widgets.ts", "examples.ts", "article.ts"];
'template.ts',
'widgets.ts',
'examples.ts',
'article.ts',
'safeguard.ts',
'help.ts'
]
for (const module of modulesToRemove) { for (const module of modulesToRemove) {
const modulePath = path.join(modulesPath, module) const modulePath = path.join(modulesPath, module);
try { try {
await fs.rm(modulePath, { force: true }) await fs.rm(modulePath, { force: true });
} catch { } catch {
// 文件不存在时忽略错误 // 文件不存在时忽略错误
} }
@@ -283,8 +276,8 @@ export const dashboardRoutes: AppRouteRecord = {
} }
] ]
} }
` `;
await fs.writeFile(path.join(modulesPath, 'dashboard.ts'), dashboardContent, 'utf-8') await fs.writeFile(path.join(modulesPath, "dashboard.ts"), dashboardContent, "utf-8");
// 重写 system.ts - 移除 nested 嵌套菜单 // 重写 system.ts - 移除 nested 嵌套菜单
const systemContent = `import { AppRouteRecord } from '@/types/router' const systemContent = `import { AppRouteRecord } from '@/types/router'
@@ -347,8 +340,8 @@ export const systemRoutes: AppRouteRecord = {
} }
] ]
} }
` `;
await fs.writeFile(path.join(modulesPath, 'system.ts'), systemContent, 'utf-8') await fs.writeFile(path.join(modulesPath, "system.ts"), systemContent, "utf-8");
// 重写 index.ts - 只导入保留的模块 // 重写 index.ts - 只导入保留的模块
const indexContent = `import { AppRouteRecord } from '@/types/router' const indexContent = `import { AppRouteRecord } from '@/types/router'
@@ -366,19 +359,19 @@ export const routeModules: AppRouteRecord[] = [
resultRoutes, resultRoutes,
exceptionRoutes exceptionRoutes
] ]
` `;
await fs.writeFile(path.join(modulesPath, 'index.ts'), indexContent, 'utf-8') await fs.writeFile(path.join(modulesPath, "index.ts"), indexContent, "utf-8");
console.log(` ${icons.success} ${fmt.success('清理路由模块完成')}`) console.log(` ${icons.success} ${fmt.success("清理路由模块完成")}`);
} catch (err) { } catch (err) {
console.log(` ${icons.error} ${fmt.error('清理路由模块失败')}`) console.log(` ${icons.error} ${fmt.error("清理路由模块失败")}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
// 清理路由别名 // 清理路由别名
async function cleanRoutesAlias() { async function cleanRoutesAlias() {
const routesAliasPath = path.resolve(process.cwd(), 'src/router/routesAlias.ts') const routesAliasPath = path.resolve(process.cwd(), "src/router/routesAlias.ts");
try { try {
const cleanedAlias = `/** const cleanedAlias = `/**
@@ -389,19 +382,19 @@ export enum RoutesAlias {
Layout = '/index/index', // 布局容器 Layout = '/index/index', // 布局容器
Login = '/auth/login' // 登录页 Login = '/auth/login' // 登录页
} }
` `;
await fs.writeFile(routesAliasPath, cleanedAlias, 'utf-8') await fs.writeFile(routesAliasPath, cleanedAlias, "utf-8");
console.log(` ${icons.success} ${fmt.success('重写路由别名配置完成')}`) console.log(` ${icons.success} ${fmt.success("重写路由别名配置完成")}`);
} catch (err) { } catch (err) {
console.log(` ${icons.error} ${fmt.error('清理路由别名失败')}`) console.log(` ${icons.error} ${fmt.error("清理路由别名失败")}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
// 清理变更日志 // 清理变更日志
async function cleanChangeLog() { async function cleanChangeLog() {
const changeLogPath = path.resolve(process.cwd(), 'src/mock/upgrade/changeLog.ts') const changeLogPath = path.resolve(process.cwd(), "src/mock/upgrade/changeLog.ts");
try { try {
const cleanedChangeLog = `import { ref } from 'vue' const cleanedChangeLog = `import { ref } from 'vue'
@@ -416,86 +409,86 @@ interface UpgradeLog {
} }
export const upgradeLogList = ref<UpgradeLog[]>([]) export const upgradeLogList = ref<UpgradeLog[]>([])
` `;
await fs.writeFile(changeLogPath, cleanedChangeLog, 'utf-8') await fs.writeFile(changeLogPath, cleanedChangeLog, "utf-8");
console.log(` ${icons.success} ${fmt.success('清空变更日志数据完成')}`) console.log(` ${icons.success} ${fmt.success("清空变更日志数据完成")}`);
} catch (err) { } catch (err) {
console.log(` ${icons.error} ${fmt.error('清理变更日志失败')}`) console.log(` ${icons.error} ${fmt.error("清理变更日志失败")}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
// 清理语言文件 // 清理语言文件
async function cleanLanguageFiles() { async function cleanLanguageFiles() {
const languageFiles = [ const languageFiles = [
{ path: 'src/locales/langs/zh.json', name: '中文语言文件' }, { path: "src/locales/langs/zh.json", name: "中文语言文件" },
{ path: 'src/locales/langs/en.json', name: '英文语言文件' } { path: "src/locales/langs/en.json", name: "英文语言文件" },
] ];
for (const { path: langPath, name } of languageFiles) { for (const { path: langPath, name } of languageFiles) {
try { try {
const fullPath = path.resolve(process.cwd(), langPath) const fullPath = path.resolve(process.cwd(), langPath);
const content = await fs.readFile(fullPath, 'utf-8') const content = await fs.readFile(fullPath, "utf-8");
const langData = JSON.parse(content) const langData = JSON.parse(content);
const menusToRemove = [ const menusToRemove = [
'widgets', "widgets",
'template', "template",
'article', "article",
'examples', "examples",
'safeguard', "safeguard",
'plan', "plan",
'help' "help",
] ];
if (langData.menus) { if (langData.menus) {
menusToRemove.forEach((menuKey) => { menusToRemove.forEach((menuKey) => {
if (langData.menus[menuKey]) { if (langData.menus[menuKey]) {
delete langData.menus[menuKey] delete langData.menus[menuKey];
} }
}) });
if (langData.menus.dashboard) { if (langData.menus.dashboard) {
if (langData.menus.dashboard.analysis) { if (langData.menus.dashboard.analysis) {
delete langData.menus.dashboard.analysis delete langData.menus.dashboard.analysis;
} }
if (langData.menus.dashboard.ecommerce) { if (langData.menus.dashboard.ecommerce) {
delete langData.menus.dashboard.ecommerce delete langData.menus.dashboard.ecommerce;
} }
} }
if (langData.menus.system) { if (langData.menus.system) {
const systemKeysToRemove = [ const systemKeysToRemove = [
'nested', "nested",
'menu1', "menu1",
'menu2', "menu2",
'menu21', "menu21",
'menu3', "menu3",
'menu31', "menu31",
'menu32', "menu32",
'menu321' "menu321",
] ];
systemKeysToRemove.forEach((key) => { systemKeysToRemove.forEach((key) => {
if (langData.menus.system[key]) { if (langData.menus.system[key]) {
delete langData.menus.system[key] delete langData.menus.system[key];
} }
}) });
} }
} }
await fs.writeFile(fullPath, JSON.stringify(langData, null, 2), 'utf-8') await fs.writeFile(fullPath, JSON.stringify(langData, null, 2), "utf-8");
console.log(` ${icons.success} ${fmt.success(`清理${name}完成`)}`) console.log(` ${icons.success} ${fmt.success(`清理${name}完成`)}`);
} catch (err) { } catch (err) {
console.log(` ${icons.error} ${fmt.error(`清理${name}失败`)}`) console.log(` ${icons.error} ${fmt.error(`清理${name}失败`)}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
} }
// 清理快速入口组件 // 清理快速入口组件
async function cleanFastEnterComponent() { async function cleanFastEnterComponent() {
const fastEnterPath = path.resolve(process.cwd(), 'src/config/fastEnter.ts') const fastEnterPath = path.resolve(process.cwd(), "src/config/fastEnter.ts");
try { try {
const cleanedFastEnter = `/** const cleanedFastEnter = `/**
@@ -559,13 +552,13 @@ const fastEnterConfig: FastEnterConfig = {
name: '注册', name: '注册',
enabled: true, enabled: true,
order: 2, order: 2,
routeName: 'Register' routeName: 'Login'
}, },
{ {
name: '忘记密码', name: '忘记密码',
enabled: true, enabled: true,
order: 3, order: 3,
routeName: 'ForgetPassword' routeName: 'Login'
}, },
{ {
name: '个人中心', name: '个人中心',
@@ -577,262 +570,262 @@ const fastEnterConfig: FastEnterConfig = {
} }
export default Object.freeze(fastEnterConfig) export default Object.freeze(fastEnterConfig)
` `;
await fs.writeFile(fastEnterPath, cleanedFastEnter, 'utf-8') await fs.writeFile(fastEnterPath, cleanedFastEnter, "utf-8");
console.log(` ${icons.success} ${fmt.success('清理快速入口配置完成')}`) console.log(` ${icons.success} ${fmt.success("清理快速入口配置完成")}`);
} catch (err) { } catch (err) {
console.log(` ${icons.error} ${fmt.error('清理快速入口配置失败')}`) console.log(` ${icons.error} ${fmt.error("清理快速入口配置失败")}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
// 更新菜单接口 // 更新菜单接口
async function updateMenuApi() { async function updateMenuApi() {
const apiPath = path.resolve(process.cwd(), 'src/api/system-manage.ts') const apiPath = path.resolve(process.cwd(), "src/api/system-manage.ts");
try { try {
const content = await fs.readFile(apiPath, 'utf-8') const content = await fs.readFile(apiPath, "utf-8");
const updatedContent = content.replace( const updatedContent = content.replace(
"url: '/api/v3/system/menus'", "url: '/api/v3/system/menus'",
"url: '/api/v3/system/menus/simple'" "url: '/api/v3/system/menus/simple'"
) );
await fs.writeFile(apiPath, updatedContent, 'utf-8') await fs.writeFile(apiPath, updatedContent, "utf-8");
console.log(` ${icons.success} ${fmt.success('更新菜单接口完成')}`) console.log(` ${icons.success} ${fmt.success("更新菜单接口完成")}`);
} catch (err) { } catch (err) {
console.log(` ${icons.error} ${fmt.error('更新菜单接口失败')}`) console.log(` ${icons.error} ${fmt.error("更新菜单接口失败")}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
} }
} }
// 用户确认函数 // 用户确认函数
async function getUserConfirmation(): Promise<boolean> { async function getUserConfirmation(): Promise<boolean> {
const { createInterface } = await import('readline') const { createInterface } = await import("readline");
return new Promise((resolve) => { return new Promise((resolve) => {
const rl = createInterface({ const rl = createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout,
}) });
console.log( console.log(
` ${fmt.highlight('请输入')} ${fmt.success('yes')} ${fmt.highlight('确认执行清理操作,或按 Enter 取消')}` ` ${fmt.highlight("请输入")} ${fmt.success("yes")} ${fmt.highlight("确认执行清理操作,或按 Enter 取消")}`
) );
console.log() console.log();
process.stdout.write(` ${icons.arrow} `) process.stdout.write(` ${icons.arrow} `);
rl.question('', (answer: string) => { rl.question("", (answer: string) => {
rl.close() rl.close();
resolve(answer.toLowerCase().trim() === 'yes') resolve(answer.toLowerCase().trim() === "yes");
}) });
}) });
} }
// 显示清理警告 // 显示清理警告
async function showCleanupWarning() { async function showCleanupWarning() {
createCard('安全警告', [ createCard("安全警告", [
`${fmt.warning('此操作将永久删除以下演示内容,且无法恢复!')}`, `${fmt.warning("此操作将永久删除以下演示内容,且无法恢复!")}`,
`${fmt.dim('请仔细阅读清理列表,确认后再继续操作')}` `${fmt.dim("请仔细阅读清理列表,确认后再继续操作")}`,
]) ]);
const cleanupItems = [ const cleanupItems = [
{ {
icon: icons.image, icon: icons.image,
name: '图片资源', name: "图片资源",
desc: '演示用的封面图片、3D图片、运维图片等', desc: "演示用的封面图片、3D图片、运维图片等",
color: theme.orange color: theme.orange,
}, },
{ {
icon: icons.file, icon: icons.file,
name: '演示页面', name: "演示页面",
desc: 'widgets、template、article、examples、safeguard等页面', desc: "widgets、template、article、examples、safeguard等页面",
color: theme.purple color: theme.purple,
}, },
{ {
icon: icons.code, icon: icons.code,
name: '路由模块文件', name: "路由模块文件",
desc: '删除演示路由模块,只保留核心模块(dashboard、system、result、exception', desc: "删除演示路由模块,只保留核心模块(dashboard、system、result、exception",
color: theme.primary color: theme.primary,
}, },
{ {
icon: icons.link, icon: icons.link,
name: '路由别名', name: "路由别名",
desc: '重写routesAlias.ts,移除演示路由别名', desc: "重写routesAlias.ts,移除演示路由别名",
color: theme.info color: theme.info,
}, },
{ {
icon: icons.data, icon: icons.data,
name: 'Mock数据', name: "Mock数据",
desc: '演示用的JSON数据、文章列表、评论数据等', desc: "演示用的JSON数据、文章列表、评论数据等",
color: theme.success color: theme.success,
}, },
{ {
icon: icons.globe, icon: icons.globe,
name: '多语言文件', name: "多语言文件",
desc: '清理中英文语言包中的演示菜单项', desc: "清理中英文语言包中的演示菜单项",
color: theme.warning color: theme.warning,
}, },
{ icon: icons.map, name: '地图组件', desc: '移除art-map-chart地图组件', color: theme.error }, { icon: icons.map, name: "地图组件", desc: "移除art-map-chart地图组件", color: theme.error },
{ icon: icons.chat, name: '评论组件', desc: '移除comment-widget评论组件', color: theme.orange }, { icon: icons.chat, name: "评论组件", desc: "移除comment-widget评论组件", color: theme.orange },
{ {
icon: icons.bolt, icon: icons.bolt,
name: '快速入口', name: "快速入口",
desc: '移除分析页、礼花效果、聊天、更新日志、定价、留言管理等无效项目', desc: "移除分析页、礼花效果、聊天、更新日志、定价、留言管理等无效项目",
color: theme.purple color: theme.purple,
} },
] ];
console.log(` ${fmt.badge('', theme.bgRed)} ${fmt.title('将要清理的内容')}`) console.log(` ${fmt.badge("", theme.bgRed)} ${fmt.title("将要清理的内容")}`);
console.log() console.log();
cleanupItems.forEach((item, index) => { cleanupItems.forEach((item, index) => {
console.log(` ${item.color}${theme.reset} ${fmt.highlight(`${index + 1}. ${item.name}`)}`) console.log(` ${item.color}${theme.reset} ${fmt.highlight(`${index + 1}. ${item.name}`)}`);
console.log(` ${fmt.dim(item.desc)}`) console.log(` ${fmt.dim(item.desc)}`);
}) });
console.log() console.log();
console.log(` ${fmt.badge('', theme.bgGreen)} ${fmt.title('保留的功能模块')}`) console.log(` ${fmt.badge("", theme.bgGreen)} ${fmt.title("保留的功能模块")}`);
console.log() console.log();
const preservedModules = [ const preservedModules = [
{ name: 'Dashboard', desc: '工作台页面' }, { name: "Dashboard", desc: "工作台页面" },
{ name: 'System', desc: '系统管理模块' }, { name: "System", desc: "系统管理模块" },
{ name: 'Result', desc: '结果页面' }, { name: "Result", desc: "结果页面" },
{ name: 'Exception', desc: '异常页面' }, { name: "Exception", desc: "异常页面" },
{ name: 'Auth', desc: '登录注册功能' }, { name: "Auth", desc: "登录注册功能" },
{ name: 'Core Components', desc: '核心组件库' } { name: "Core Components", desc: "核心组件库" },
] ];
preservedModules.forEach((module) => { preservedModules.forEach((module) => {
console.log(` ${icons.check} ${fmt.success(module.name)} ${fmt.dim(`- ${module.desc}`)}`) console.log(` ${icons.check} ${fmt.success(module.name)} ${fmt.dim(`- ${module.desc}`)}`);
}) });
console.log() console.log();
createDivider() createDivider();
console.log() console.log();
} }
// 显示统计信息 // 显示统计信息
async function showStats() { async function showStats() {
const duration = Date.now() - stats.startTime const duration = Date.now() - stats.startTime;
const seconds = (duration / 1000).toFixed(2) const seconds = (duration / 1000).toFixed(2);
console.log() console.log();
createCard('清理统计', [ createCard("清理统计", [
`${fmt.success('成功删除')}: ${fmt.highlight(stats.deletedFiles.toString())} 个文件`, `${fmt.success("成功删除")}: ${fmt.highlight(stats.deletedFiles.toString())} 个文件`,
`${fmt.info('涉及路径')}: ${fmt.highlight(stats.deletedPaths.toString())} 个目录/文件`, `${fmt.info("涉及路径")}: ${fmt.highlight(stats.deletedPaths.toString())} 个目录/文件`,
...(stats.failedPaths > 0 ...(stats.failedPaths > 0
? [ ? [
`${icons.error} ${fmt.error('删除失败')}: ${fmt.highlight(stats.failedPaths.toString())} 个路径` `${icons.error} ${fmt.error("删除失败")}: ${fmt.highlight(stats.failedPaths.toString())} 个路径`,
] ]
: []), : []),
`${fmt.info('耗时')}: ${fmt.highlight(seconds)}` `${fmt.info("耗时")}: ${fmt.highlight(seconds)}`,
]) ]);
} }
// 创建成功横幅 // 创建成功横幅
function createSuccessBanner() { function createSuccessBanner() {
console.log() console.log();
console.log( console.log(
fmt.gradient(' ╔══════════════════════════════════════════════════════════════════╗') fmt.gradient(" ╔══════════════════════════════════════════════════════════════════╗")
) );
console.log( console.log(
fmt.gradient(' ║ ║') fmt.gradient(" ║ ║")
) );
console.log( console.log(
`${icons.star} ${fmt.success('清理完成!项目已准备就绪')} ${icons.rocket}` `${icons.star} ${fmt.success("清理完成!项目已准备就绪")} ${icons.rocket}`
) );
console.log( console.log(
`${fmt.dim('现在可以开始您的开发之旅了!')}` `${fmt.dim("现在可以开始您的开发之旅了!")}`
) );
console.log( console.log(
fmt.gradient(' ║ ║') fmt.gradient(" ║ ║")
) );
console.log( console.log(
fmt.gradient(' ╚══════════════════════════════════════════════════════════════════╝') fmt.gradient(" ╚══════════════════════════════════════════════════════════════════╝")
) );
console.log() console.log();
} }
// 主函数 // 主函数
async function main() { async function main() {
// 清屏并显示横幅 // 清屏并显示横幅
console.clear() console.clear();
createModernBanner() createModernBanner();
// 显示清理警告 // 显示清理警告
await showCleanupWarning() await showCleanupWarning();
// 统计文件数量 // 统计文件数量
console.log(` ${fmt.info('正在统计文件数量...')}`) console.log(` ${fmt.info("正在统计文件数量...")}`);
stats.totalFiles = await countAllFiles() stats.totalFiles = await countAllFiles();
console.log(` ${fmt.info('即将清理')}: ${fmt.highlight(stats.totalFiles.toString())} 个文件`) console.log(` ${fmt.info("即将清理")}: ${fmt.highlight(stats.totalFiles.toString())} 个文件`);
console.log(` ${fmt.dim(`涉及 ${targets.length} 个目录/文件路径`)}`) console.log(` ${fmt.dim(`涉及 ${targets.length} 个目录/文件路径`)}`);
console.log() console.log();
// 用户确认 // 用户确认
const confirmed = await getUserConfirmation() const confirmed = await getUserConfirmation();
if (!confirmed) { if (!confirmed) {
console.log(` ${fmt.warning('操作已取消,清理中止')}`) console.log(` ${fmt.warning("操作已取消,清理中止")}`);
console.log() console.log();
return return;
} }
console.log() console.log();
console.log(` ${icons.check} ${fmt.success('确认成功,开始清理...')}`) console.log(` ${icons.check} ${fmt.success("确认成功,开始清理...")}`);
console.log() console.log();
// 开始清理过程 // 开始清理过程
console.log(` ${fmt.badge('步骤 1/6', theme.bgBlue)} ${fmt.title('删除演示文件')}`) console.log(` ${fmt.badge("步骤 1/6", theme.bgBlue)} ${fmt.title("删除演示文件")}`);
console.log() console.log();
for (let i = 0; i < targets.length; i++) { for (let i = 0; i < targets.length; i++) {
await remove(targets[i], i) await remove(targets[i], i);
} }
console.log() console.log();
console.log(` ${fmt.badge('步骤 2/6', theme.bgBlue)} ${fmt.title('清理路由模块')}`) console.log(` ${fmt.badge("步骤 2/6", theme.bgBlue)} ${fmt.title("清理路由模块")}`);
console.log() console.log();
await cleanRouteModules() await cleanRouteModules();
console.log() console.log();
console.log(` ${fmt.badge('步骤 3/6', theme.bgBlue)} ${fmt.title('重写路由别名')}`) console.log(` ${fmt.badge("步骤 3/6", theme.bgBlue)} ${fmt.title("重写路由别名")}`);
console.log() console.log();
await cleanRoutesAlias() await cleanRoutesAlias();
console.log() console.log();
console.log(` ${fmt.badge('步骤 4/6', theme.bgBlue)} ${fmt.title('清空变更日志')}`) console.log(` ${fmt.badge("步骤 4/6", theme.bgBlue)} ${fmt.title("清空变更日志")}`);
console.log() console.log();
await cleanChangeLog() await cleanChangeLog();
console.log() console.log();
console.log(` ${fmt.badge('步骤 5/6', theme.bgBlue)} ${fmt.title('清理语言文件')}`) console.log(` ${fmt.badge("步骤 5/6", theme.bgBlue)} ${fmt.title("清理语言文件")}`);
console.log() console.log();
await cleanLanguageFiles() await cleanLanguageFiles();
console.log() console.log();
console.log(` ${fmt.badge('步骤 6/7', theme.bgBlue)} ${fmt.title('清理快速入口')}`) console.log(` ${fmt.badge("步骤 6/7", theme.bgBlue)} ${fmt.title("清理快速入口")}`);
console.log() console.log();
await cleanFastEnterComponent() await cleanFastEnterComponent();
console.log() console.log();
console.log(` ${fmt.badge('步骤 7/7', theme.bgBlue)} ${fmt.title('更新菜单接口')}`) console.log(` ${fmt.badge("步骤 7/7", theme.bgBlue)} ${fmt.title("更新菜单接口")}`);
console.log() console.log();
await updateMenuApi() await updateMenuApi();
// 显示统计信息 // 显示统计信息
await showStats() await showStats();
// 显示成功横幅 // 显示成功横幅
createSuccessBanner() createSuccessBanner();
} }
main().catch((err) => { main().catch((err) => {
console.log() console.log();
console.log(` ${icons.error} ${fmt.error('清理脚本执行出错')}`) console.log(` ${icons.error} ${fmt.error("清理脚本执行出错")}`);
console.log(` ${fmt.dim('错误详情: ' + err)}`) console.log(` ${fmt.dim("错误详情: " + err)}`);
console.log() console.log();
process.exit(1) process.exit(1);
}) });
+59 -26
View File
@@ -1,41 +1,74 @@
<template> <template>
<ElConfigProvider <ElConfigProvider
size="default" :size="size"
:locale="locales[language]" :locale="locale"
:z-index="3000" :z-index="3000"
:card="{ :card="{
shadow: 'never' shadow: 'never',
}" }"
> >
<RouterView></RouterView> <el-watermark
:font="{ color: fontColor }"
:content="showWatermark ? watermarkContent : ''"
:z-index="9999"
class="wh-full"
>
<RouterView></RouterView>
<!-- AI 助手 -->
<AiAssistant v-if="enableAiAssistant" />
</el-watermark>
</ElConfigProvider> </ElConfigProvider>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useUserStore } from './store/modules/user' import { computed, onBeforeMount, onMounted } from "vue";
import zh from 'element-plus/es/locale/lang/zh-cn' import { useAppStore, useUserStore } from "./store";
import en from 'element-plus/es/locale/lang/en' import { useSettingsStore } from "./store/modules/setting.store";
import { systemUpgrade } from './utils/sys' import { defaultSettings } from "./config/setting";
import { toggleTransition } from './utils/ui/animation' import { ComponentSize } from "./enums/settings/layout.enum";
import { checkStorageCompatibility } from './utils/storage' import AiAssistant from "./components/AiAssistant/index.vue";
import { initializeTheme } from './hooks/core/useTheme' import { toggleTransition } from "./utils/ui/animation";
import { checkStorageCompatibility } from "./utils/storage";
import { initializeTheme } from "./hooks/core/useTheme";
import { systemUpgrade } from "./utils/sys";
import { ThemeMode } from "./enums";
import en from "element-plus/es/locale/lang/en";
import zhCn from "element-plus/es/locale/lang/zh-cn";
const userStore = useUserStore() const appStore = useAppStore();
const { language } = storeToRefs(userStore) const settingsStore = useSettingsStore();
const userStore = useUserStore();
const locales = { const size = computed(() => appStore.size as ComponentSize);
zh: zh, const showWatermark = computed(() => settingsStore.showWatermark);
en: en const watermarkContent = defaultSettings.watermarkContent;
}
onBeforeMount(() => { // 根据语言设置返回对应的语言包
toggleTransition(true) const locale = computed(() => {
initializeTheme() return appStore.language === "en" ? en : zhCn;
}) });
onMounted(() => { // 只有在启用 AI 助手且用户已登录时才显示
checkStorageCompatibility() const enableAiAssistant = computed(() => {
toggleTransition(false) const isEnabled = settingsStore.userEnableAi;
systemUpgrade() const isLoggedIn = userStore.basicInfo && Object.keys(userStore.basicInfo).length > 0;
}) return isEnabled && isLoggedIn;
});
// 明亮/暗黑主题水印字体颜色适配
const fontColor = computed(() => {
return settingsStore.theme === ThemeMode.DARK ? "rgba(255, 255, 255, .15)" : "rgba(0, 0, 0, .15)";
});
onBeforeMount(() => {
toggleTransition(true);
initializeTheme();
});
onMounted(() => {
checkStorageCompatibility();
toggleTransition(false);
systemUpgrade();
});
</script> </script>
+48 -24
View File
@@ -1,29 +1,53 @@
import request from '@/utils/http'
/** /**
* * 便使 `@/api/auth`
* @param params * `@/api/module_system/auth` / `user`
* @returns
*/ */
export function fetchLogin(params: Api.Auth.LoginParams) { import AuthAPI, { type LoginFormData } from "@/api/module_system/auth";
return request.post<Api.Auth.LoginResponse>({ import UserAPI, { type UserInfo } from "@/api/module_system/user";
url: '/api/auth/login', import { ResultEnum } from "@/enums/api/result.enum";
params
// showSuccessMessage: true // 显示成功消息 export interface FetchLoginParams {
// showErrorMessage: false // 不显示错误消息 userName: string;
}) password: string;
} }
/** export async function fetchLogin(params: FetchLoginParams): Promise<{
* token: string;
* @returns refreshToken: string;
*/ }> {
export function fetchGetUserInfo() { const captchaRes = await AuthAPI.getCaptcha();
return request.get<Api.Auth.UserInfo>({ const captchaInfo = captchaRes.data?.data;
url: '/api/user/info'
// 自定义请求头 const loginForm: LoginFormData = {
// headers: { username: params.userName,
// 'X-Custom-Header': 'your-custom-value' password: params.password,
// } captcha_key: captchaInfo?.key ?? "",
}) captcha: "",
remember: false,
login_type: "PC端",
};
const response = await AuthAPI.login(loginForm);
if (response.data.code !== ResultEnum.SUCCESS || !response.data.data) {
throw new Error(response.data.msg || "登录失败");
}
const data = response.data.data;
return {
token: data.access_token,
refreshToken: data.refresh_token,
};
}
/** 使用当前请求上下文中的 token 拉取用户信息(与 store.getUserInfo 数据来源一致) */
export async function fetchGetUserInfo(): Promise<UserInfo> {
const response = await UserAPI.getCurrentUserInfo();
if (response.data.code !== ResultEnum.SUCCESS || response.data.data == null) {
throw new Error(response.data.msg || "获取用户信息失败");
}
const raw = response.data.data;
const next = { ...raw };
delete next.menus;
return next as UserInfo;
} }
+140
View File
@@ -0,0 +1,140 @@
import request from "@/utils/http";
const API_PATH = "/ai/chat";
export const AiChatAPI = {
getSessionList(query: {
page_no: number;
page_size: number;
title?: string;
created_at?: string[];
updated_at?: string[];
}) {
return request<ApiResponse<PageResult<ChatSession[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
createSession(body: { title: string }) {
return request<ApiResponse<ChatSession>>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateSession(id: string, body: { title: string }) {
return request<ApiResponse<ChatSession>>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteSession(body: string[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
chat(body: { message: string; session_id?: string | null }) {
return request<ApiResponse<AiChatResponse>>({
url: `${API_PATH}/ai-chat`,
method: "post",
data: body,
});
},
getSessionDetail(sessionId: string) {
return request<ApiResponse<ChatSessionDetail>>({
url: `${API_PATH}/detail/${sessionId}`,
method: "get",
});
},
};
export default AiChatAPI;
export interface ChatSessionMessage {
id: string;
role: string;
content: string;
created_at: number | null;
}
export interface ChatSession {
session_id: string;
agent_id: string | null;
team_id: string | null;
team_name: string | null;
workflow_id: string | null;
user_id: string | null;
session_data: Record<string, any> | null;
agent_data: Record<string, any> | null;
team_data: Record<string, any> | null;
workflow_data: Record<string, any> | null;
metadata: Record<string, any> | null;
runs: Array<Record<string, any>> | null;
summary: Record<string, any> | null;
created_at: number | null;
updated_at: number | null;
id: string;
title: string | null;
created_time: string | null;
updated_time: string | null;
message_count: number;
messages: ChatSessionMessage[];
}
export interface SessionGroup {
id: string;
title: string;
sessions: ChatSession[];
}
export interface UserInfo {
id: number;
name: string;
username: string;
avatar: string;
email: string;
}
export interface AiChatResponse {
response: string;
session_id: string;
function_calls: Array<{
name: string;
arguments: Record<string, any>;
}> | null;
}
export interface ChatSessionDetail {
session_id: string;
agent_id: string | null;
team_id: string | null;
team_name: string | null;
workflow_id: string | null;
user_id: string | null;
session_data: Record<string, any> | null;
agent_data: Record<string, any> | null;
team_data: Record<string, any> | null;
workflow_data: Record<string, any> | null;
metadata: Record<string, any> | null;
runs: Array<Record<string, any>> | null;
summary: Record<string, any> | null;
created_at: number | null;
updated_at: number | null;
id: string;
title: string | null;
created_time: string | null;
updated_time: string | null;
message_count: number;
messages: ChatSessionMessage[];
}
@@ -0,0 +1,114 @@
import request from "@/utils/http";
const API_PATH = "/application/portal";
export const ApplicationAPI = {
/**
*
* @param id ID
*/
detailApp(id: number) {
return request<ApiResponse<ApplicationInfo>>({
url: `${API_PATH}/detail/${id}`,
method: "get",
});
},
/**
*
* @param query
*/
listApp(query: ApplicationPageQuery) {
return request<ApiResponse<PageResult<ApplicationInfo[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
/**
*
* @param body
*/
createApp(body: ApplicationForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
/**
*
* @param id ID
* @param body
*/
updateApp(id: number, body: ApplicationForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
/**
*
* @param body ID数组
*/
deleteApp(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
/**
*
* @param body
*/
batchApp(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
};
export default ApplicationAPI;
/**
*
*/
export interface ApplicationPageQuery extends PageQuery {
name?: string;
tenant_id?: number;
status?: string;
created_id?: number;
created_time?: string[];
updated_id?: number;
updated_time?: string[];
}
/**
*
*/
export interface ApplicationInfo extends BaseType {
name?: string;
access_url?: string;
icon_url?: string;
tenant?: TenantType;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
/**
*
*/
export interface ApplicationForm extends BaseFormType {
name: string;
access_url: string;
icon_url: string;
}
@@ -0,0 +1,124 @@
import request from "@/utils/http";
const API_PATH = "/example/demo";
const DemoAPI = {
getDemoList(query: DemoPageQuery) {
return request<ApiResponse<PageResult<DemoTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
getDemoDetail(query: number) {
return request<ApiResponse<DemoTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createDemo(body: DemoForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateDemo(id: number, body: DemoForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteDemo(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchDemo(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
exportDemo(body: DemoPageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
downloadTemplateDemo() {
return request<ApiResponse>({
url: `${API_PATH}/download/template`,
method: "post",
responseType: "blob",
});
},
importDemo(body: FormData) {
return request<ApiResponse>({
url: `${API_PATH}/import`,
method: "post",
data: body,
headers: {
"Content-Type": "multipart/form-data",
},
});
},
};
export default DemoAPI;
export interface DemoPageQuery extends PageQuery {
name?: string;
status?: string;
created_time?: string[];
updated_time?: string[];
created_id?: number;
updated_id?: number;
}
export interface DemoTable extends BaseType {
name?: string;
status?: string;
description?: string;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
a?: number;
b?: number;
c?: number;
d?: boolean;
e?: string;
f?: string;
g?: string;
h?: string;
i?: Record<string, any>;
}
export interface DemoForm extends BaseFormType {
name?: string;
status?: string;
description?: string;
a?: number;
b?: number;
c?: number;
d?: boolean;
e?: string;
f?: Date | string;
g?: Date | string;
h?: string;
i?: Record<string, any>;
}
@@ -0,0 +1,116 @@
import request from "@/utils/http";
const API_PATH = "/example/demo01";
const Demo01API = {
getDemo01List(query: Demo01PageQuery) {
return request<ApiResponse<PageResult<Demo01Table[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
getDemo01Detail(id: number) {
return request<ApiResponse<Demo01Table>>({
url: `${API_PATH}/detail/${id}`,
method: "get",
});
},
createDemo01(body: Demo01Form) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateDemo01(id: number, body: Demo01Form) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteDemo01(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchDemo01(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
exportDemo01(body: Demo01PageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
downloadDemo01Template() {
return request<ApiResponse>({
url: `${API_PATH}/download/template`,
method: "post",
responseType: "blob",
});
},
importDemo01(body: FormData) {
return request<ApiResponse>({
url: `${API_PATH}/import`,
method: "post",
data: body,
headers: {
"Content-Type": "multipart/form-data",
},
});
},
};
export default Demo01API;
export interface Demo01PageQuery extends PageQuery {
/** 与后端 Demo01QueryParam 一致 */
name?: string;
description?: string;
/** 是否启用:0 启用 / 1 禁用(字符串) */
status?: string;
created_time?: string[];
updated_time?: string[];
created_id?: number;
updated_id?: number;
}
/**
* Demo01OutSchema
* Demo01CreateSchema(name,status,description) + BaseSchema + UserBySchema
*/
export interface Demo01Table extends BaseType {
name?: string;
status?: string;
description?: string;
created_id?: number;
updated_id?: number;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
/** 与后端 Demo01CreateSchema / Demo01UpdateSchema 一致(不含 id,更新走 URL) */
export interface Demo01Form extends BaseFormType {
name?: string;
status?: string;
description?: string;
}
@@ -0,0 +1,199 @@
import request from "@/utils/http";
const API_PATH = "/generator/gencode";
const GencodeAPI = {
// 查询生成表数据
listTable(query: GenTablePageQuery) {
return request<ApiResponse<PageResult<GenTableSchema[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
// 查询db数据库列表
listDbTable(query: DBTablePageQuery) {
return request<ApiResponse<PageResult<DBTableSchema[]>>>({
url: `${API_PATH}/db/list`,
method: "get",
params: query,
});
},
// 导入表
importTable(table_names: string[]) {
return request<ApiResponse>({
url: `${API_PATH}/import`,
method: "post",
data: table_names,
});
},
// 查询表详细信息
detailTable(table_id: number) {
return request<ApiResponse<GenTableSchema>>({
url: `${API_PATH}/detail/${table_id}`,
method: "get",
});
},
// 创建表(与后端 GenCreateTableSqlBody 一致)
createTable(sql: string) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: { sql },
});
},
// 更新表信息
updateTable(data: GenTableSchema, table_id: number) {
return request<ApiResponse>({
url: `${API_PATH}/update/${table_id}`,
method: "put",
data,
});
},
// 删除表数据
deleteTable(table_ids: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: table_ids,
});
},
// 批量生成代码
batchGenCode(table_names: string[]) {
return request<Blob>({
url: `${API_PATH}/batch/output`,
method: "patch",
data: table_names,
responseType: "blob",
});
},
// 生成代码到指定路径
genCodeToPath(table_name: string) {
return request<ApiResponse>({
url: `${API_PATH}/output/${table_name}`,
method: "post",
});
},
// 预览生成代码
previewTable(id: number) {
return request<ApiResponse<Record<string, string>>>({
url: `${API_PATH}/preview/${id}`,
method: "get",
});
},
// 同步数据库
syncDb(table_name: string) {
return request<ApiResponse>({
url: `${API_PATH}/sync_db/${table_name}`,
method: "post",
});
},
// 同步数据库差异预览(不落库)
syncDbPreview(table_name: string) {
return request<ApiResponse<GenSyncPreviewSchema>>({
url: `${API_PATH}/sync_db/preview/${table_name}`,
method: "get",
});
},
};
export default GencodeAPI;
/** 代码生成预览对象 */
export interface GeneratorPreviewVO {
/** 文件生成路径 */
path: string;
/** 文件名称 */
file_name: string;
/** 文件内容 */
content: string;
}
/** 业务表结构 */
export interface GenTableSchema extends BaseType {
table_name?: string;
table_comment?: string;
class_name?: string;
package_name?: string;
module_name?: string;
business_name?: string;
function_name?: string;
description?: string;
parent_menu_id?: number;
sub?: boolean;
sub_table_name?: string;
sub_table_fk_name?: string;
master_sub_hint?: string | null;
pk_column?: GenTableColumnSchema;
columns: GenTableColumnSchema[];
sub_table?: GenTableSchema;
}
export interface GenTableColumnSchema extends BaseType {
table_id?: number;
column_name?: string;
column_comment?: string;
column_type?: string;
column_length?: string;
column_default?: string;
is_pk?: boolean;
is_increment?: boolean;
is_nullable?: boolean;
is_unique?: boolean;
sort?: number;
python_type?: string;
python_field?: string;
html_type?: string | null;
dict_type?: string;
is_insert?: boolean | null;
is_edit?: boolean | null;
is_list?: boolean | null;
is_query?: boolean | null;
query_type?: string | null;
}
/** 查询参数:生成表 */
export interface GenTablePageQuery extends PageQuery {
table_name?: string;
table_comment?: string;
}
/** 查询参数:DB 表 */
export interface DBTablePageQuery extends PageQuery {
table_name?: string;
table_comment?: string;
}
export interface DBTableSchema {
table_name: string;
table_comment?: string;
create_time?: string;
update_time?: string;
}
export interface GenSyncColumnChange {
column_name: string;
before?: Record<string, any> | null;
after?: Record<string, any> | null;
changed_keys?: string[];
}
export interface GenSyncPreviewSchema {
table_name: string;
added: string[];
removed: string[];
unchanged: string[];
changed: GenSyncColumnChange[];
sub_tables?: GenSyncPreviewSchema[];
}
@@ -0,0 +1,95 @@
import request from "@/utils/http";
const API_PATH = "/monitor/cache";
const CacheAPI = {
getCacheInfo() {
return request<ApiResponse>({
url: `${API_PATH}/info`,
method: "get",
});
},
getCacheNames() {
return request<ApiResponse>({
url: `${API_PATH}/get/names`,
method: "get",
});
},
getCacheKeys(cacheName: string) {
return request<ApiResponse>({
url: `${API_PATH}/get/keys/${cacheName}`,
method: "get",
});
},
getCacheValue(cacheName: string, cacheKey: string) {
return request<ApiResponse>({
url: `${API_PATH}/get/value/${cacheName}/${cacheKey}`,
method: "get",
});
},
deleteCacheName(cacheName: string) {
return request<ApiResponse>({
url: `${API_PATH}/delete/name/${cacheName}`,
method: "delete",
});
},
deleteCacheKey(cacheKey: string) {
return request<ApiResponse>({
url: `${API_PATH}/delete/key/${cacheKey}`,
method: "delete",
});
},
deleteCacheAll() {
return request<ApiResponse>({
url: `${API_PATH}/delete/all`,
method: "delete",
});
},
};
export default CacheAPI;
export interface CacheForm {
cache_name: string;
cache_key: string;
cache_value: string;
}
export interface CacheInfo {
cache_key: string;
cache_name: string;
cache_value: string;
remark: string;
}
export interface CommandStats {
name: string;
value: string;
}
export interface RedisInfo {
redis_version: string;
redis_mode: string;
tcp_port: number;
connected_clients: number;
uptime_in_days: number;
used_memory_human: string;
used_cpu_user_children: string;
maxmemory_human: string;
aof_enabled: string;
rdb_last_bgsave_status: string;
instantaneous_input_kbps: number;
instantaneous_output_kbps: number;
}
export interface CacheMonitor {
command_stats: CommandStats[];
db_size: number;
info: RedisInfo;
}
@@ -0,0 +1,52 @@
import request from "@/utils/http";
const API_PATH = "/monitor/online";
const OnlineAPI = {
// 查询在线用户列表
listOnline(query: OnlineUserPageQuery) {
return request<ApiResponse<PageResult<OnlineUserTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
// 强退用户
deleteOnline(body: string) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
// 强退用户
clearOnline() {
return request<ApiResponse>({
url: `${API_PATH}/clear`,
method: "delete",
});
},
};
export default OnlineAPI;
export interface OnlineUserPageQuery extends PageQuery {
ipaddr?: string;
name?: string;
login_location?: string;
}
export interface OnlineUserTable {
session_id: string;
user_id: number;
name: string;
user_name: string;
ipaddr: string;
login_location: string;
os: string;
browser: string;
login_time: string;
login_type: string;
}
@@ -0,0 +1,248 @@
import request from "@/utils/http";
const API_PATH = "/monitor/resource";
export const ResourceAPI = {
/**
*
* @param query
*/
listResource(query: ResourcePageQuery) {
return request<ApiResponse<PageResult<ResourceItem[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
/**
*
* @param formData
*/
uploadFile(formData: FormData) {
return request<ApiResponse<ResourceUploadSchema>>({
url: `${API_PATH}/upload`,
method: "post",
data: formData,
headers: { "Content-Type": "multipart/form-data" },
});
},
/**
*
* @param path
*/
downloadFile(path: string) {
return request<Blob>({
url: `${API_PATH}/download`,
method: "get",
params: { path },
responseType: "blob",
});
},
/**
*
* @param body
*/
deleteResource(body: string[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
/**
*
* @param body
*/
moveResource(body: ResourceMoveQuery) {
return request<ApiResponse>({
url: `${API_PATH}/move`,
method: "post",
data: body,
});
},
/**
*
* @param body
*/
copyResource(body: ResourceCopyQuery) {
return request<ApiResponse>({
url: `${API_PATH}/copy`,
method: "post",
data: body,
});
},
/**
*
* @param body
*/
renameResource(body: ResourceRenameQuery) {
return request<ApiResponse>({
url: `${API_PATH}/rename`,
method: "post",
data: body,
});
},
/**
*
* @param body
*/
createDirectory(body: ResourceCreateDirQuery) {
return request<ApiResponse>({
url: `${API_PATH}/create-dir`,
method: "post",
data: body,
});
},
/**
*
* @param body
*/
exportResource(body: ResourcePageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
};
export default ResourceAPI;
/**
*
*/
export interface ResourceListQuery {
/** 目录路径 */
path?: string;
/** 包含隐藏文件 */
include_hidden?: boolean;
}
/**
*
*/
export interface ResourceDirectorySchema {
/** 目录路径 */
path: string;
/** 目录名称 */
name: string;
/** 目录项 */
items: ResourceItem[];
/** 文件总数 */
total_files: number;
/** 目录总数 */
total_dirs: number;
/** 总大小 */
total_size: number;
}
/**
*
*/
export interface ResourceSearchQuery {
/** 关键词 */
name?: string;
}
/**
*
*/
export interface ResourcePageQuery extends PageQuery {
/** 关键词 */
name?: string;
/** 目录路径 */
path?: string;
/** 包含隐藏文件 */
include_hidden?: boolean;
}
/**
*
*/
export interface ResourceUploadSchema {
/** 文件名 */
filename: string;
/** 访问URL */
file_url: string;
/** 文件大小 */
file_size: number;
/** 上传时间 */
upload_time: string;
}
/**
*
*/
export interface ResourceItem {
/** 文件/目录名称 */
name: string;
/** 文件URL路径 */
file_url: string;
/** 相对路径 */
relative_path?: string;
/** 是否为文件 */
is_file?: boolean;
/** 是否为目录 */
is_dir?: boolean;
/** 文件大小(字节) */
size?: number | null;
/** 创建时间 */
created_time: string;
/** 修改时间 */
modified_time: string;
/** 是否为隐藏文件 */
is_hidden?: boolean;
}
/**
*
*/
export interface ResourceMoveQuery {
/** 源路径 */
source_path: string;
/** 目标路径 */
target_path: string;
/** 是否覆盖 */
overwrite?: boolean;
}
/**
*
*/
export interface ResourceCopyQuery {
/** 源路径 */
source_path: string;
/** 目标路径 */
target_path: string;
/** 是否覆盖 */
overwrite?: boolean;
}
/**
*
*/
export interface ResourceRenameQuery {
/** 原路径 */
old_path: string;
/** 新名称 */
new_name: string;
}
/**
*
*/
export interface ResourceCreateDirQuery {
/** 父目录路径 */
parent_path: string;
/** 目录名称 */
dir_name: string;
}
@@ -0,0 +1,67 @@
import request from "@/utils/http";
const API_PATH = "/monitor/server";
const ServerAPI = {
// 获取服务信息
getServer() {
return request<ApiResponse>({
url: `${API_PATH}/info`,
method: "get",
});
},
};
export default ServerAPI;
export interface Cpu {
cpu_num: number;
used: number;
sys: number;
free: number;
}
export interface Memory {
total: string;
used: string;
free: string;
usage: number;
}
export interface System {
computer_name: string;
os_name: string;
computer_ip: string;
os_arch: string;
user_dir: string;
}
export interface Python {
name: string;
version: string;
start_time: string;
run_time: string;
home: string;
memory_total: string;
memory_used: string;
memory_free: string;
memory_usage: number;
}
export interface SysFile {
dirName: string;
sysTypeName: string;
typeName: string;
total: string;
free: string;
used: string;
usage: number;
}
export interface ServerInfo {
cpu: Cpu;
mem: Memory;
sys: System;
py: Python;
disks: SysFile[];
}
@@ -0,0 +1,125 @@
import request from "@/utils/http";
// import request from '@/utils/http'
const API_PATH = "/system/auth";
/** 第三方 OAuth 登录渠道(与后端 `/system/auth/oauth/{provider}` 一致) */
export type OAuthProvider = "wechat" | "qq" | "github" | "gitee";
const AuthAPI = {
/**
*
* @param body
* @returns
*/
login(body: LoginFormData) {
return request<ApiResponse<LoginResult>>({
url: `${API_PATH}/login`,
method: "post",
headers: {
"Content-Type": "multipart/form-data",
},
data: body,
});
},
refreshToken(body: RefreshToekenBody) {
return request<ApiResponse<LoginResult>>({
url: `${API_PATH}/token/refresh`,
method: "post",
data: body,
});
},
getCaptcha() {
return request<ApiResponse<CaptchaInfo>>({
url: `${API_PATH}/captcha/get`,
method: "get",
});
},
logout(body: LogoutBody) {
return request<ApiResponse>({
url: `${API_PATH}/logout`,
method: "post",
data: body,
});
},
/** 获取免登录用户列表 */
getAutoLoginUsers() {
return request<ApiResponse<AutoLoginUser[]>>({
url: `${API_PATH}/auto-login/users`,
method: "get",
});
},
/** 获取免登录Token */
getAutoLoginToken(userId: number) {
return request<ApiResponse<AutoLoginToken>>({
url: `${API_PATH}/auto-login/token`,
method: "post",
params: { user_id: userId },
});
},
/** 免登录 */
autoLogin(token: string) {
return request<ApiResponse<LoginResult>>({
url: `${API_PATH}/auto-login`,
method: "post",
params: { token },
});
},
};
export default AuthAPI;
/** 登录表单数据 */
export interface LoginFormData {
username: string;
password: string;
captcha_key: string;
captcha: string;
remember: boolean;
login_type: string;
}
// 刷新令牌
export interface RefreshToekenBody {
refresh_token: string;
}
/** 登录响应 */
export interface LoginResult {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
}
/** 验证码信息 */
export interface CaptchaInfo {
enable: boolean;
key: string;
img_base: string;
}
/** 退出登录操作 */
export interface LogoutBody {
token: string;
}
/** 免登录用户信息 */
export interface AutoLoginUser {
id: number;
username: string;
name: string;
avatar: string | null;
}
/** 免登录Token响应 */
export interface AutoLoginToken {
token: string;
user: AutoLoginUser;
}
@@ -0,0 +1,82 @@
import request from "@/utils/http";
const API_PATH = "/system/dept";
const DeptAPI = {
listDept(query?: DeptPageQuery) {
return request<ApiResponse<DeptTable[]>>({
url: `${API_PATH}/tree`,
method: "get",
params: query,
});
},
detailDept(query: number) {
return request<ApiResponse<DeptTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createDept(body: DeptForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateDept(id: number, body: DeptForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteDept(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchDept(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
};
export default DeptAPI;
export interface DeptPageQuery {
name?: string;
status?: string;
created_time?: string[];
}
export interface DeptTable extends BaseType {
name?: string;
order?: number;
code: string;
leader?: string;
phone?: string;
email?: string;
parent_id?: number;
parent_name?: string;
children?: DeptTable[];
}
export interface DeptForm extends BaseFormType {
name?: string;
order?: number;
code: string;
leader?: string;
phone?: string;
email?: string;
parent_id?: number;
}
@@ -0,0 +1,182 @@
import request from "@/utils/http";
const API_PATH = "/system/dict";
const DictAPI = {
listDictType(query: DictPageQuery) {
return request<ApiResponse<PageResult<DictTable[]>>>({
url: `${API_PATH}/type/list`,
method: "get",
params: query,
});
},
optionDictType() {
return request<ApiResponse>({
url: `${API_PATH}/type/optionselect`,
method: "get",
});
},
detailDictType(query: number) {
return request<ApiResponse<DictTable>>({
url: `${API_PATH}/type/detail/${query}`,
method: "get",
});
},
createDictType(body: DictForm) {
return request<ApiResponse>({
url: `${API_PATH}/type/create`,
method: "post",
data: body,
});
},
updateDictType(id: number, body: DictForm) {
return request<ApiResponse>({
url: `${API_PATH}/type/update/${id}`,
method: "put",
data: body,
});
},
deleteDictType(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/type/delete`,
method: "delete",
data: body,
});
},
batchDictType(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/type/available/setting`,
method: "patch",
data: body,
});
},
exportDictType(body: DictPageQuery) {
return request<Blob>({
url: `${API_PATH}/type/export`,
method: "post",
data: body,
responseType: "blob",
});
},
listDictData(query: DictDataPageQuery) {
return request<ApiResponse<PageResult<DictDataTable[]>>>({
url: `${API_PATH}/data/list`,
method: "get",
params: query,
});
},
detailDictData(query: number) {
return request<ApiResponse<DictDataTable>>({
url: `${API_PATH}/data/detail/${query}`,
method: "get",
});
},
createDictData(body: DictDataForm) {
return request<ApiResponse>({
url: `${API_PATH}/data/create`,
method: "post",
data: body,
});
},
updateDictData(id: number, body: DictDataForm) {
return request<ApiResponse>({
url: `${API_PATH}/data/update/${id}`,
method: "put",
data: body,
});
},
deleteDictData(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/data/delete`,
method: "delete",
data: body,
});
},
batchDictData(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/data/available/setting`,
method: "patch",
data: body,
});
},
exportDictData(body: DictDataPageQuery) {
return request<Blob>({
url: `${API_PATH}/data/export`,
method: "post",
data: body,
responseType: "blob",
});
},
getInitDict(dict_type: string) {
return request<ApiResponse<DictDataTable[]>>({
url: `${API_PATH}/data/info/${dict_type}`,
method: "get",
});
},
};
export default DictAPI;
export interface DictPageQuery extends PageQuery {
dict_name?: string;
dict_type?: string;
status?: string;
created_time?: string[];
updated_time?: string[];
}
export interface DictDataPageQuery extends PageQuery {
dict_label?: string;
dict_type?: string;
dict_type_id?: number;
status?: string;
created_time?: string[];
updated_time?: string[];
}
export interface DictTable extends BaseType {
dict_name?: string;
dict_type?: string;
}
export interface DictForm extends BaseFormType {
dict_name?: string;
dict_type?: string;
}
export interface DictDataTable extends BaseType {
dict_sort?: number;
dict_label?: string;
dict_value?: string;
dict_type_id?: number;
dict_type?: string;
css_class?: string;
list_class?: string;
is_default?: boolean;
}
export interface DictDataForm extends BaseFormType {
dict_sort?: number;
dict_label?: string;
dict_value?: string;
dict_type_id?: number;
dict_type?: string;
css_class?: string;
list_class?: string;
is_default?: boolean;
}
@@ -0,0 +1,66 @@
import request from "@/utils/http";
const API_PATH = "/system/log";
const LogAPI = {
listLog(query: LogPageQuery) {
return request<ApiResponse<PageResult<LogTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailLog(query: number) {
return request<ApiResponse<LogTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
deleteLog(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
exportLog(body: LogPageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
};
export default LogAPI;
export interface LogPageQuery extends PageQuery {
type?: number;
request_path?: string;
creator_name?: string;
created_time?: string[];
updated_time?: string[];
created_id?: number;
updated_id?: number;
}
export interface LogTable extends BaseType {
type?: number; // 1 登录日志 2 操作日志
request_path?: string;
request_method?: string;
request_ip?: string;
login_location?: string;
request_browser?: string;
request_os?: string;
response_code?: number;
request_payload?: string;
response_json?: string;
process_time?: string;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
@@ -0,0 +1,110 @@
import request from "@/utils/http";
const API_PATH = "/system/menu";
const MenuAPI = {
listMenu(query?: MenuPageQuery) {
return request<ApiResponse<MenuTable[]>>({
url: `${API_PATH}/tree`,
method: "get",
params: query,
});
},
detailMenu(query: number) {
return request<ApiResponse<MenuTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createMenu(body: MenuForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateMenu(id: number, body: MenuForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteMenu(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchMenu(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
};
export default MenuAPI;
export interface MenuPageQuery {
name?: string;
status?: string;
created_time?: string[];
updated_time?: string[];
/** 与后端 Query `menu_client` 一致:pc | app;菜单管理 Tab 切换时传入 */
menu_client?: "pc" | "app";
}
export interface MenuTable extends BaseType {
name?: string;
type?: number;
icon?: string;
order?: number;
permission?: string;
route_name?: string;
route_path?: string;
component_path?: string;
redirect?: string;
parent_id?: number;
parent_name?: string;
keep_alive?: boolean;
hidden?: boolean;
always_show?: boolean;
title?: string;
params?: { key: string; value: string }[];
affix?: boolean;
children?: MenuTable[];
client?: "pc" | "app";
}
export interface MenuForm extends BaseFormType {
name?: string;
type?: number;
icon?: string;
order?: number;
permission?: string;
route_name?: string;
route_path?: string;
component_path?: string;
redirect?: string;
parent_id?: number;
keep_alive?: boolean;
hidden?: boolean;
always_show?: boolean;
title?: string;
params?: KeyValue[];
affix?: boolean;
client?: "pc" | "app";
}
export interface KeyValue {
key: string;
value: string;
}
@@ -0,0 +1,95 @@
import request from "@/utils/http";
const API_PATH = "/system/notice";
const NoticeAPI = {
listNotice(query: NoticePageQuery) {
return request<ApiResponse<PageResult<NoticeTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
listNoticeAvailable() {
return request<ApiResponse<PageResult<NoticeTable[]>>>({
url: `${API_PATH}/available`,
method: "get",
});
},
detailNotice(query: number) {
return request<ApiResponse<NoticeTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createNotice(body: NoticeForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateNotice(id: number, body: NoticeForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteNotice(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchNotice(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
exportNotice(body: NoticePageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
};
export default NoticeAPI;
export interface NoticePageQuery extends PageQuery {
notice_title?: string;
notice_type?: string;
status?: string;
created_time?: string[];
updated_time?: string[];
created_id?: number;
updated_id?: number;
}
export interface NoticeTable extends BaseType {
notice_title?: string;
notice_type?: string;
notice_content?: string;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
export interface NoticeForm extends BaseFormType {
notice_title?: string;
notice_type?: string;
notice_content?: string;
}
@@ -0,0 +1,98 @@
import request from "@/utils/http";
import { NO_AUTH_FLAG } from "@/utils/http/config";
const API_PATH = "/system/param";
const ParamsAPI = {
uploadFile(body: any) {
return request<ApiResponse<UploadFilePath>>({
url: `${API_PATH}/upload`,
method: "post",
data: body,
headers: { "Content-Type": "multipart/form-data" },
});
},
/** 登录前拉取站点参数:不带 Token,避免过期 JWT 导致 401 无法展示底部备案等 */
getInitConfig() {
return request<ApiResponse<ConfigTable[]>>({
url: `${API_PATH}/info`,
method: "get",
headers: {
Authorization: NO_AUTH_FLAG,
},
});
},
listParams(query: ConfigPageQuery) {
return request<ApiResponse<PageResult<ConfigTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailParams(query: number) {
return request<ApiResponse<ConfigTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createParams(body: ConfigForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateParams(id: number, body: ConfigForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteParams(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
exportParams(body: ConfigPageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
};
export default ParamsAPI;
export interface ConfigPageQuery extends PageQuery {
config_name?: string;
config_key?: string;
config_type?: boolean;
created_time?: string[];
updated_time?: string[];
}
export interface ConfigTable extends BaseType {
config_name?: string;
config_key?: string;
config_value?: string;
config_type?: boolean;
}
export interface ConfigForm extends BaseFormType {
config_name?: string;
config_key?: string;
config_value?: string;
config_type?: boolean;
}
@@ -0,0 +1,84 @@
import request from "@/utils/http";
const API_PATH = "/system/position";
const PositionAPI = {
listPosition(query?: PositionPageQuery) {
return request<ApiResponse<PageResult<PositionTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailPosition(query: number) {
return request<ApiResponse<PositionTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createPosition(body: PositionForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updatePosition(id: number, body: PositionForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deletePosition(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchPosition(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
exportPosition(body: PositionPageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
};
export default PositionAPI;
export interface PositionPageQuery extends PageQuery {
name?: string;
status?: string;
created_id?: number;
created_time?: string[];
updated_time?: string[];
}
export interface PositionTable extends BaseType {
name?: string;
order?: number;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
export interface PositionForm extends BaseFormType {
name?: string;
order?: number;
}
@@ -0,0 +1,119 @@
import request from "@/utils/http";
const API_PATH = "/system/role";
const RoleAPI = {
listRole(query?: TablePageQuery) {
return request<ApiResponse<PageResult<RoleTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailRole(query: number) {
return request<ApiResponse<RoleTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createRole(body: RoleForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateRole(id: number, body: RoleForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteRole(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchRole(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
setPermission(body: permissionDataType) {
return request<ApiResponse>({
url: `${API_PATH}/permission/setting`,
method: "patch",
data: body,
});
},
exportRole(body: TablePageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
};
export default RoleAPI;
export interface TablePageQuery extends PageQuery {
name?: string;
status?: string;
created_time?: string[];
updated_time?: string[];
}
export interface RoleTable extends BaseType {
id: number;
name: string;
order?: number;
code: string;
data_scope?: number;
menus?: permissionMenuType[];
depts?: permissionDeptType[];
}
export interface RoleForm extends BaseFormType {
name?: string;
order?: number;
code: string;
}
export interface permissionDataType {
data_scope: number;
role_ids: RoleTable["id"][];
menu_ids: permissionMenuType["id"][];
dept_ids: permissionDeptType["id"][];
}
export interface permissionDeptType {
id: number;
name: string;
parent_id: number;
children: permissionDeptType[];
}
export interface permissionMenuType {
id: number;
name: string;
type: number;
permission: string;
parent_id?: number;
status: string;
description?: string;
children?: permissionMenuType[];
}
@@ -0,0 +1,100 @@
import request from "@/utils/http";
const API_PATH = "/system/tenant";
const TenantAPI = {
listTenant(query?: TenantPageQuery) {
return request<ApiResponse<PageResult<TenantTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailTenant(id: number) {
return request<ApiResponse<TenantTable>>({
url: `${API_PATH}/detail/${id}`,
method: "get",
});
},
createTenant(body: TenantCreateForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateTenant(id: number, body: TenantUpdateForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteTenant(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchTenant(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
};
export default TenantAPI;
export interface TenantPageQuery extends PageQuery {
name?: string;
code?: string;
status?: string;
created_time?: string[];
}
export interface TenantTable extends BaseType {
name: string;
code: string;
start_time?: string;
end_time?: string;
}
export interface TenantForm {
id?: number;
name?: string;
code?: string;
status?: string;
description?: string;
start_time?: string;
end_time?: string;
}
export interface TenantCreateForm {
name: string;
code: string;
status?: string;
description?: string;
start_time?: string;
end_time?: string;
}
export interface TenantUpdateForm {
name?: string;
status?: string;
description?: string;
start_time?: string;
end_time?: string;
}
export interface BatchType {
ids: number[];
status: string;
}
@@ -0,0 +1,268 @@
import request from "@/utils/http";
import { MenuTable, MenuForm } from "@/api/module_system/menu";
const API_PATH = "/system/user";
export const UserAPI = {
getCurrentUserInfo() {
return request<ApiResponse<UserInfo>>({
url: `${API_PATH}/current/info`,
method: "get",
});
},
uploadCurrentUserAvatar(body: any) {
return request<ApiResponse<UploadFilePath>>({
url: `${API_PATH}/current/avatar/upload`,
method: "post",
data: body,
headers: { "Content-Type": "multipart/form-data" },
});
},
updateCurrentUserInfo(body: InfoFormState) {
return request<ApiResponse<UserInfo>>({
url: `${API_PATH}/current/info/update`,
method: "put",
data: body,
});
},
changeCurrentUserPassword(body: PasswordFormState) {
return request<ApiResponse>({
url: `${API_PATH}/current/password/change`,
method: "put",
data: body,
});
},
resetUserPassword(body: ResetPasswordForm) {
return request<ApiResponse>({
url: `${API_PATH}/reset/password`,
method: "put",
data: body,
});
},
registerUser(body: RegisterForm) {
return request<ApiResponse>({
url: `${API_PATH}/register`,
method: "post",
data: body,
});
},
forgetPassword(body: ForgetPasswordForm) {
return request<ApiResponse>({
url: `${API_PATH}/forget/password`,
method: "post",
data: body,
});
},
listUser(query: UserPageQuery) {
return request<ApiResponse<PageResult<UserInfo[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailUser(query: number) {
return request<ApiResponse<UserInfo>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createUser(body: UserForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateUser(id: number, body: UserForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteUser(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchUser(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/available/setting`,
method: "patch",
data: body,
});
},
exportUser(body: UserPageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
downloadTemplateUser() {
return request<ApiResponse>({
url: `${API_PATH}/import/template`,
method: "post",
responseType: "blob",
});
},
importUser(body: any) {
return request<ApiResponse>({
url: `${API_PATH}/import/data`,
method: "post",
data: body,
headers: {
"Content-Type": "multipart/form-data",
},
});
},
};
export default UserAPI;
export interface ForgetPasswordForm {
username: string;
new_password: string;
mobile?: string;
confirmPassword: string;
}
export interface RegisterForm {
username: string;
password: string;
confirmPassword: string;
}
export interface UserPageQuery extends PageQuery {
username?: string;
name?: string;
mobile?: string;
email?: string;
dept_id?: number;
status?: string;
created_time?: string[];
created_id?: number;
updated_id?: number;
}
export interface searchSelectDataType {
name?: string;
status?: string;
}
export interface UserInfo extends BaseType {
username?: string;
name?: string;
avatar?: string;
email?: string;
mobile?: string;
gender?: string;
password?: string;
menus?: MenuTable[];
dept?: deptTreeType;
dept_id?: deptTreeType["id"];
dept_name?: deptTreeType["name"];
roles?: roleSelectorType[];
role_names?: roleSelectorType["name"][];
role_ids?: roleSelectorType["id"][];
positions?: positionSelectorType[];
position_names?: positionSelectorType["name"][];
position_ids?: positionSelectorType["id"][];
is_superuser?: boolean;
last_login?: string;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
export interface deptTreeType {
id?: number;
name?: string;
parent_id?: number;
children?: deptTreeType[];
}
export interface roleSelectorType {
id?: number;
name?: string;
code?: string;
status?: string;
description?: string;
menus?: MenuForm[];
}
export interface positionSelectorType {
id?: number;
name?: string;
status?: string;
description?: string;
}
export interface InfoFormState {
id?: number;
name?: string;
gender?: number;
mobile?: string;
email?: string;
username?: string;
dept_name?: string;
dept?: deptTreeType;
positions?: positionSelectorType[];
roles?: roleSelectorType[];
avatar?: string;
created_time?: string;
updated_time?: string;
}
export interface PasswordFormState {
old_password: string;
new_password: string;
confirm_password: string;
}
export interface ResetPasswordForm {
id: number;
password: string;
}
export interface UserForm extends BaseFormType {
username?: string;
name?: string;
dept_id?: number;
dept_name?: string;
role_ids?: number[];
role_names?: string[];
position_ids?: number[];
position_names?: string[];
password?: string;
gender?: number;
email?: string;
mobile?: string;
is_superuser?: boolean;
}
export interface CurrentUserFormState {
name?: string;
gender?: number;
mobile?: string;
email?: string;
avatar?: string;
}
@@ -0,0 +1,155 @@
import request from "@/utils/http";
const API_PATH = "/task/cronjob/job";
const JobAPI = {
getSchedulerStatus() {
return request<ApiResponse<SchedulerStatus>>({
url: `${API_PATH}/scheduler/status`,
method: "get",
});
},
getSchedulerJobs() {
return request<ApiResponse<SchedulerJob[]>>({
url: `${API_PATH}/scheduler/jobs`,
method: "get",
});
},
startScheduler() {
return request<ApiResponse>({
url: `${API_PATH}/scheduler/start`,
method: "post",
});
},
pauseScheduler() {
return request<ApiResponse>({
url: `${API_PATH}/scheduler/pause`,
method: "post",
});
},
resumeScheduler() {
return request<ApiResponse>({
url: `${API_PATH}/scheduler/resume`,
method: "post",
});
},
shutdownScheduler() {
return request<ApiResponse>({
url: `${API_PATH}/scheduler/shutdown`,
method: "post",
});
},
clearAllJobs() {
return request<ApiResponse>({
url: `${API_PATH}/scheduler/jobs/clear`,
method: "delete",
});
},
getSchedulerConsole() {
return request<ApiResponse<string>>({
url: `${API_PATH}/scheduler/console`,
method: "get",
});
},
syncJobsToDb() {
return request<ApiResponse<number>>({
url: `${API_PATH}/scheduler/sync`,
method: "post",
});
},
pauseJob(jobId: string) {
return request<ApiResponse>({
url: `${API_PATH}/task/pause/${jobId}`,
method: "post",
});
},
resumeJob(jobId: string) {
return request<ApiResponse>({
url: `${API_PATH}/task/resume/${jobId}`,
method: "post",
});
},
runJobNow(jobId: string) {
return request<ApiResponse>({
url: `${API_PATH}/task/run/${jobId}`,
method: "post",
});
},
removeJob(jobId: string) {
return request<ApiResponse>({
url: `${API_PATH}/task/remove/${jobId}`,
method: "delete",
});
},
getJobLogList(query: JobLogPageQuery) {
return request<ApiResponse<PageResult<JobLogTable[]>>>({
url: `${API_PATH}/log/list`,
method: "get",
params: query,
});
},
getJobLogDetail(id: number) {
return request<ApiResponse<JobLogTable>>({
url: `${API_PATH}/log/detail/${id}`,
method: "get",
});
},
deleteJobLog(ids: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/log/delete`,
method: "delete",
data: ids,
});
},
};
export default JobAPI;
export interface SchedulerStatus {
status: string;
is_running: boolean;
job_count: number;
}
export interface SchedulerJob {
id: string;
name: string;
trigger: string;
next_run_time?: string;
status: string;
}
export interface JobLogPageQuery extends PageQuery {
job_id?: string;
job_name?: string;
status?: string;
trigger_type?: string;
}
export interface JobLogTable extends BaseType {
job_id: string;
job_name?: string;
trigger_type?: string;
status: string;
next_run_time?: string;
job_state?: string;
result?: string;
error?: string;
created_time?: string;
updated_time?: string;
}
@@ -0,0 +1,135 @@
import request from "@/utils/http";
const API_PATH = "/task/cronjob/node";
const NodeAPI = {
getNodeTypeOptions() {
return request<ApiResponse<NodeType[]>>({
url: `${API_PATH}/options`,
method: "get",
});
},
listNode(query: NodePageQuery) {
return request<ApiResponse<PageResult<NodeTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailNode(query: number) {
return request<ApiResponse<NodeTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createNode(body: NodeForm) {
return request<ApiResponse>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateNode(id: number, body: NodeForm) {
return request<ApiResponse>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteNode(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
clearNode() {
return request<ApiResponse>({
url: `${API_PATH}/clear`,
method: "delete",
});
},
executeNode(id: number, params: ExecuteNodeParams = { trigger: "now" }) {
return request<ApiResponse<ExecuteNodeResult>>({
url: `${API_PATH}/execute/${id}`,
method: "post",
data: params,
});
},
};
export default NodeAPI;
export interface NodePageQuery extends PageQuery {
name?: string;
code?: string;
created_id?: number;
updated_id?: number;
created_time?: string[];
updated_time?: string[];
}
export type TriggerType = "now" | "cron" | "interval" | "date";
export interface ExecuteNodeParams {
trigger: TriggerType;
trigger_args?: string;
start_date?: string;
end_date?: string;
}
export interface ExecuteNodeResult {
job_id: number;
status: string;
trigger: TriggerType;
}
export interface NodeTable extends BaseType {
name: string;
code: string;
jobstore?: string;
executor?: string;
trigger?: TriggerType;
trigger_args?: string;
func?: string;
args?: string;
kwargs?: string;
coalesce?: boolean;
max_instances?: number;
start_date?: string;
end_date?: string;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
export interface NodeForm {
id?: number;
name: string;
code?: string;
jobstore?: string;
executor?: string;
func?: string;
args?: string;
kwargs?: string;
coalesce?: boolean;
max_instances?: number;
start_date?: string;
end_date?: string;
}
export interface NodeType {
id: number;
name: string;
code: string;
func?: string;
args?: string;
kwargs?: string;
}
@@ -0,0 +1,117 @@
import request from "@/utils/http";
/** 对应后端 `plugin.module_task.workflow.definition` */
const API_PATH = "/task/workflow/definition";
const WorkflowDefinitionAPI = {
getWorkflowList(query: WorkflowPageQuery) {
return request<ApiResponse<PageResult<WorkflowTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
getWorkflowDetail(query: number) {
return request<ApiResponse<WorkflowTable>>({
url: `${API_PATH}/detail/${query}`,
method: "get",
});
},
createWorkflow(body: WorkflowForm) {
return request<ApiResponse<WorkflowTable>>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateWorkflow(id: number, body: WorkflowForm) {
return request<ApiResponse<WorkflowTable>>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteWorkflow(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
publishWorkflow(id: number, body: WorkflowPublishForm) {
return request<ApiResponse<WorkflowTable>>({
url: `${API_PATH}/publish/${id}`,
method: "post",
data: body,
});
},
executeWorkflow(body: WorkflowExecuteForm) {
return request<ApiResponse<WorkflowExecuteResult>>({
url: `${API_PATH}/execute`,
method: "post",
data: body,
});
},
};
export default WorkflowDefinitionAPI;
export { WorkflowDefinitionAPI };
export interface WorkflowPageQuery extends PageQuery {
name?: string;
code?: string;
status?: string;
created_time?: string[];
updated_time?: string[];
created_id?: number;
updated_id?: number;
}
export interface WorkflowTable extends BaseType {
name?: string;
code?: string;
status?: string;
description?: string;
nodes?: any[];
edges?: any[];
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
export interface WorkflowForm extends BaseFormType {
name?: string;
code?: string;
status?: string;
description?: string;
nodes?: any[];
edges?: any[];
}
export interface WorkflowPublishForm {
remark?: string;
}
export interface WorkflowExecuteForm {
workflow_id: number;
variables?: Record<string, any>;
business_key?: string;
job_id?: number;
}
export interface WorkflowExecuteResult {
workflow_id: number;
workflow_name: string;
status: string;
start_time?: string;
end_time?: string;
variables?: Record<string, any>;
node_results?: Record<string, any>;
error?: string;
}
@@ -0,0 +1,101 @@
import request from "@/utils/http";
/** 对应后端 `plugin.module_task.workflow.node_type` */
const API_PATH = "/task/workflow/node-type";
const WorkflowNodeTypeAPI = {
getWorkflowNodeTypeOptions() {
return request<ApiResponse<WorkflowNodeTypeOption[]>>({
url: `${API_PATH}/options`,
method: "get",
});
},
getWorkflowNodeTypeList(query: WorkflowNodeTypePageQuery) {
return request<ApiResponse<PageResult<WorkflowNodeTypeTable[]>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
getWorkflowNodeTypeDetail(id: number) {
return request<ApiResponse<WorkflowNodeTypeTable>>({
url: `${API_PATH}/detail/${id}`,
method: "get",
});
},
createWorkflowNodeType(body: WorkflowNodeTypeForm) {
return request<ApiResponse<WorkflowNodeTypeTable>>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updateWorkflowNodeType(id: number, body: WorkflowNodeTypeForm) {
return request<ApiResponse<WorkflowNodeTypeTable>>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deleteWorkflowNodeType(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
};
export default WorkflowNodeTypeAPI;
export { WorkflowNodeTypeAPI };
/** 编排节点类型选项(对应后端 task_workflow_node_type */
export interface WorkflowNodeTypeOption {
id: number;
code: string;
name: string;
category: string;
args?: string;
kwargs?: string;
}
export interface WorkflowNodeTypePageQuery extends PageQuery {
name?: string;
code?: string;
category?: string;
is_active?: boolean;
created_time?: string[];
updated_time?: string[];
created_id?: number;
updated_id?: number;
}
export interface WorkflowNodeTypeTable extends BaseType {
name?: string;
code?: string;
category?: string;
func?: string;
args?: string;
kwargs?: string;
sort_order?: number;
is_active?: boolean;
created_by?: CommonType;
updated_by?: CommonType;
deleted_by?: CommonType;
}
export interface WorkflowNodeTypeForm {
name: string;
code: string;
category?: string;
func: string;
args?: string;
kwargs?: string;
sort_order?: number;
is_active?: boolean;
}
+109 -19
View File
@@ -1,25 +1,115 @@
import request from '@/utils/http' /**
import { AppRouteRecord } from '@/types/router' * views/system/* 使 `@/api/system-manage`
* `module_system` API
*/
import MenuAPI, { type MenuTable } from "@/api/module_system/menu";
import RoleAPI, { type RoleTable, type TablePageQuery } from "@/api/module_system/role";
import UserAPI, { type UserInfo, type UserPageQuery } from "@/api/module_system/user";
import type { AppRouteRecord } from "@/types/router";
import { ResultEnum } from "@/enums/api/result.enum";
// 获取用户列表 function assertSuccess<T>(res: { data: ApiResponse<T> }, fallbackMsg: string): T {
export function fetchGetUserList(params: Api.SystemManage.UserSearchParams) { if (res.data.code !== ResultEnum.SUCCESS || res.data.data == null) {
return request.get<Api.SystemManage.UserList>({ throw new Error(res.data.msg || fallbackMsg);
url: '/api/user/list', }
params return res.data.data;
})
} }
// 获取角色列表 /** useTable 传入 current / size 及演示页自定义筛选字段 */
export function fetchGetRoleList(params: Api.SystemManage.RoleSearchParams) { export async function fetchGetUserList(params: Record<string, unknown>) {
return request.get<Api.SystemManage.RoleList>({ const q: UserPageQuery = {
url: '/api/role/list', page_no: Number(params.current) || 1,
params page_size: Number(params.size) || 20,
}) username: (params.userName ?? params.name) as string | undefined,
name: params.name as string | undefined,
mobile: (params.userPhone ?? params.phone) as string | undefined,
email: (params.userEmail ?? params.email) as string | undefined,
status: params.status as string | undefined,
};
const res = await UserAPI.listUser(q);
const page = assertSuccess(res, "获取用户列表失败");
const items = (page.items || []).map((u) => mapUserToListRow(u));
return {
items,
total: page.total,
page_no: page.page_no,
page_size: page.page_size,
has_next: page.has_next,
};
} }
// 获取菜单列表 function mapUserToListRow(u: UserInfo) {
export function fetchGetMenuList() { return {
return request.get<AppRouteRecord[]>({ id: u.id,
url: '/api/v3/system/menus' nickName: u.name ?? u.username,
}) userName: u.username,
userGender: u.gender,
userPhone: u.mobile,
userEmail: u.email,
avatar: u.avatar,
status: u.status,
};
}
export async function fetchGetRoleList(params: Record<string, unknown>) {
const q: TablePageQuery = {
page_no: Number(params.current) || 1,
page_size: Number(params.size) || 20,
name: params.roleName as string | undefined,
status:
params.enabled !== undefined && params.enabled !== null && params.enabled !== ""
? String(params.enabled)
: undefined,
};
const res = await RoleAPI.listRole(q);
const page = assertSuccess(res, "获取角色列表失败");
const items = (page.items || []).map((r) => mapRoleToListRow(r));
return {
items,
total: page.total,
page_no: page.page_no,
page_size: page.page_size,
has_next: page.has_next,
};
}
function mapRoleToListRow(r: RoleTable) {
return {
roleId: r.id,
roleName: r.name,
roleCode: r.code,
description: r.description,
enabled: r.status === "0",
createTime: r.created_time,
};
}
function mapMenuTableToRoute(m: MenuTable): AppRouteRecord {
const childrenRaw = m.children?.filter(Boolean) ?? [];
const children = childrenRaw.length ? childrenRaw.map(mapMenuTableToRoute) : undefined;
return {
path: (m.route_path ?? "").trim(),
name: m.route_name,
meta: {
title: m.title ?? m.name ?? "",
icon: m.icon,
hidden: m.hidden,
keepAlive: m.keep_alive,
},
children,
};
}
/** 菜单管理演示页:树形表格数据 */
export async function fetchGetMenuList(): Promise<AppRouteRecord[]> {
const res = await MenuAPI.listMenu({});
const tree = assertSuccess(res, "获取菜单失败");
return (tree || []).map(mapMenuTableToRoute);
} }
@@ -0,0 +1 @@
<svg t="1765953898889" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6018" width="200" height="200"><path d="M603.904 244.992c134.4 0 243.3536 108.9536 243.3536 243.3536v202.8032c0 134.4-108.9536 243.3536-243.3536 243.3536H320c-134.4 0-243.3536-108.9536-243.3536-243.3536V488.3456c0-134.4 108.9536-243.3536 243.3536-243.3536h283.904z m0 81.1008H320c-89.6 0-162.2528 72.6528-162.2528 162.2528v202.8032c0 89.6 72.6528 162.2528 162.2528 162.2528h283.904c89.6 0 162.2528-72.6528 162.2528-162.2528V488.3456c0-89.6-72.6528-162.2528-162.2528-162.2528z" p-id="6019"></path><path d="M340.224 508.6208c27.0336 0 40.5504 13.5168 40.5504 40.5504v81.1008c0 27.0336-13.5168 40.5504-40.5504 40.5504-27.0336 0-40.5504-13.5168-40.5504-40.5504v-81.1008c0-27.0336 13.5168-40.5504 40.5504-40.5504zM583.2192 501.3504c15.2576-11.4176 36.864-8.3456 48.2816 6.912a34.4832 34.4832 0 0 1-6.912 48.2816l-44.3904 33.28 44.3904 33.28a34.53952 34.53952 0 0 1 9.472 44.3392l-2.56 3.9424c-11.4176 15.2576-33.024 18.3296-48.2816 6.912l-59.4944-44.5952c-13.7728-10.3424-21.9136-26.5728-21.9136-43.8272s8.0896-33.4848 21.9136-43.8272l59.4944-44.5952zM883.5072 261.12l-19.7632 47.3088c-2.7648 6.656-9.2672 10.9568-16.4864 10.9568s-13.6704-4.3008-16.4864-10.9568L811.008 261.12a44.416 44.416 0 0 0-19.7632-21.9648l-34.8672-19.0976c-5.7344-3.1232-9.2672-9.1136-9.2672-15.6672s3.5328-12.544 9.2672-15.6672l34.8672-19.0464a44.89728 44.89728 0 0 0 19.7632-21.9648l19.7632-47.3088c2.7648-6.656 9.2672-10.9568 16.4864-10.9568s13.6704 4.3008 16.4864 10.9568l19.7632 47.3088a44.416 44.416 0 0 0 19.7632 21.9648l34.8672 19.0976c5.7344 3.1232 9.2672 9.1136 9.2672 15.6672s-3.584 12.544-9.2672 15.6672l-34.8672 19.0464c-8.9088 4.864-15.872 12.6464-19.7632 21.9648z" p-id="6020"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

+6
View File
@@ -0,0 +1,6 @@
<svg t="1655172500569" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4063"
width="200" height="200">
<path
d="M230.4 576.512c-12.288 9.728-25.088 24.064-28.672 41.984-5.12 24.576-1.024 55.296 22.528 79.872 28.672 29.184 72.704 37.376 91.648 38.912 51.2 3.584 105.984-22.016 147.456-50.688 16.384-11.264 44.032-34.304 70.144-69.632-59.392-30.72-133.632-64.512-212.48-61.44-40.448 1.536-69.632 9.728-90.624 20.992z m752.64 135.68c26.112-61.44 40.96-129.024 40.96-200.192C1024 229.888 794.112 0 512 0S0 229.888 0 512s229.888 512 512 512c170.496 0 321.536-83.968 414.72-211.968-88.064-43.52-232.96-115.712-322.56-159.232-42.496 48.64-105.472 97.28-176.64 118.272-44.544 13.312-84.992 18.432-126.976 9.728-41.984-8.704-72.704-28.16-90.624-47.616-9.216-10.24-19.456-22.528-27.136-37.888 0.512 1.024 1.024 2.048 1.024 3.072 0 0-4.608-7.68-7.68-19.456-1.536-6.144-3.072-11.776-3.584-17.92-0.512-4.096-0.512-8.704 0-12.8-0.512-7.68 0-15.872 1.536-24.064 4.096-20.48 12.8-44.032 35.328-65.536 49.152-48.128 114.688-50.688 148.992-50.176 50.176 0.512 138.24 22.528 211.968 48.64 20.48-43.52 33.792-90.112 41.984-121.344h-307.2v-33.28h157.696v-66.56H272.384V302.08h190.464V235.52c0-9.216 2.048-16.384 16.384-16.384h74.752V302.08h207.36v33.28h-207.36v66.56h165.888s-16.896 92.672-68.608 184.32c115.2 40.96 278.016 104.448 331.776 125.952z"
fill="#06B4FD" p-id="4064"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M499.2 671.232v-261.12h102.4c16.384 0 28.672 1.024 37.888 2.56 13.312 2.048 24.576 6.656 34.816 13.312 9.728 6.656 17.92 16.384 23.552 28.16 6.144 12.288 8.704 25.6 8.192 38.4 0 23.552-7.68 44.032-23.04 59.904-15.36 16.896-40.96 25.088-78.848 25.088h-43.52v93.184l-61.44.512zm281.6 0h-61.952v-261.12H780.8v261.12zm-287.744 0h-69.12L396.8 601.6h-73.728l-25.088 69.632h-66.56l100.352-261.12h54.272l107.008 261.12zM343.552 545.28h32.256l-15.872-42.496c0-.512-.512-1.024-.512-1.536l-15.872 44.032zm217.6-26.112h43.52c20.48 0 28.16-4.608 31.232-7.168 4.608-4.096 7.168-10.752 7.168-18.944 0-6.656-1.536-11.776-4.096-15.36-2.56-3.584-6.144-6.144-10.752-7.68-1.536-.512-6.656-1.536-24.064-1.536h-43.008v50.688z"/><path d="M747.52 842.752H512c-8.704 0-16.384-3.584-22.016-9.728-6.144-6.144-9.216-14.336-8.704-22.528.512-16.896 14.336-30.72 31.232-31.232H747.52c115.712 0 209.408-94.208 209.408-209.408 0-104.96-78.848-194.56-183.296-207.872l-22.528-3.072-4.608-22.016C724.992 231.936 631.808 156.16 524.288 156.16c-124.928 0-226.304 101.376-226.304 226.304v8.704l1.536 36.352-36.352-4.096c-6.144-1.024-12.288-1.024-18.432-1.024-98.304 0-178.176 79.872-178.176 178.176 0 98.304 79.872 178.176 178.176 178.176h63.488c8.704 0 16.384 3.584 22.016 9.728 6.144 6.144 9.216 14.336 8.704 22.528-.512 16.896-14.336 30.72-31.232 31.232h-64c-64 0-123.904-25.088-169.472-70.144C28.16 726.528 3.072 665.6 3.072 601.088c0-129.536 103.936-236.544 232.448-241.152 12.288-157.184 149.504-276.48 307.2-266.24 59.904 3.584 118.784 27.136 165.888 65.536 45.568 37.376 77.824 87.04 94.208 143.872 125.952 26.112 217.088 137.728 217.088 266.752.512 151.04-121.856 272.896-272.384 272.896z"/><path d="M572.416 930.816c-8.192 0-15.872-3.072-21.504-8.704L431.616 812.544l113.152-117.76c6.144-6.144 13.824-9.216 22.528-9.216 8.704 0 16.384 3.072 22.528 9.216 11.776 11.776 12.288 31.232 1.024 44.032l-68.608 70.656 71.68 66.048c6.144 5.632 9.728 13.312 10.24 22.016.512 8.704-2.56 16.384-8.192 23.04-6.656 6.656-14.848 10.24-23.552 10.24z"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.72938 13.2709C1.31587 11.8234 1.31587 9.51246 2.72938 8.06503L7.9367 2.73274C9.39842 1.23594 11.8058 1.23594 13.2675 2.73274C14.681 4.18017 14.681 6.49116 13.2675 7.93859L8.06017 13.2709C6.59845 14.7677 4.19111 14.7677 2.72938 13.2709Z" fill="#12D2AC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.94084 2.7323C9.40256 1.23549 11.8099 1.23549 13.2716 2.7323L18.4789 8.06459C19.8925 9.51202 19.8925 11.823 18.4789 13.2704C17.0172 14.7672 14.6099 14.7672 13.1482 13.2704L7.94084 7.93815C6.52733 6.49071 6.52733 4.17973 7.94084 2.7323Z" fill="#307AF2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.7384 2.93044C9.30781 1.32337 11.8925 1.32337 13.4619 2.93045L15.8075 5.33229L10.6002 10.6646L5.39285 5.33229L7.7384 2.93044Z" fill="#0057FE"/>
</svg>

After

Width:  |  Height:  |  Size: 909 B

+29
View File
@@ -0,0 +1,29 @@
<svg t="1648864366083" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="43365" width="200" height="200">
<path d="M0 512c0 281.6 230.4 512 512 512s512-230.4 512-512S793.6 0 512 0 0 230.4 0 512z" fill="#D7D1D1" p-id="43366">
</path>
<path
d="M512 1024c140.8 0 264.541091-55.458909 358.4-145.058909-8.541091-29.882182-17.058909-55.482182-25.6-68.282182-42.658909-51.2-221.858909-72.517818-221.858909-72.517818L512 768l-106.658909-29.858909s-179.2 25.6-221.882182 72.517818c-8.517818 12.8-17.058909 38.4-25.6 68.282182C247.458909 968.541091 371.2 1024 512 1024z"
fill="#6B8E9D" p-id="43367"></path>
<path
d="M281.6 456.541091c0-29.882182 12.8-55.482182 34.141091-55.482182 21.317818 0 42.658909 17.082182 46.917818 46.941091 4.282182 29.858909-12.8 55.458909-34.117818 55.458909-21.341091 0-46.941091-17.058909-46.941091-46.917818z m145.058909 170.658909v149.341091h170.682182V627.2h-170.682182z m273.082182-123.741091c-21.341091 0-38.4-29.858909-34.141091-55.458909 4.258909-29.858909 25.6-51.2 46.941091-46.941091 21.317818 0 38.4 29.882182 34.117818 55.482182-4.258909 29.858909-25.6 51.2-46.917818 46.917818z"
fill="#FFE5C5" p-id="43368"></path>
<path
d="M512 213.341091s-183.458909 8.517818-183.458909 106.658909v200.541091c0 38.4 59.717818 149.317818 119.458909 153.6 29.858909 0 64 4.258909 64 4.258909s38.4-4.258909 64-4.258909c55.458909-4.282182 115.2-115.2 115.2-153.6v-196.282182c4.258909-98.117818-179.2-110.917818-179.2-110.917818"
fill="#FEE1B9" p-id="43369"></path>
<path
d="M622.941091 243.2C571.741091 217.6 512 213.341091 512 213.341091c-8.541091 0-183.458909 8.517818-183.458909 106.658909v200.541091c0 38.4 59.717818 149.317818 119.458909 153.6H512s38.4-4.282182 64-4.282182c55.458909-4.258909 119.458909-115.2 119.458909-153.6v-192c0-38.4-29.858909-68.258909-72.517818-81.058909"
fill="#FFF0DA" p-id="43370"></path>
<path d="M401.058909 729.6L499.2 1024h29.858909l98.141091-294.4-115.2 25.6z" fill="#FFFFFF" p-id="43371"></path>
<path d="M524.8 819.2l25.6-25.6L512 755.2l-38.4 38.4 25.6 25.6-29.858909 149.341091L512 1011.2l46.941091-42.658909z"
fill="#515151" p-id="43372"></path>
<path
d="M512 755.2l89.6-46.941091 46.941091 25.6-76.8 106.682182L512 755.2z m0 0l-89.6-46.941091-46.941091 25.6 93.882182 106.682182L512 755.2z"
fill="#FFFFFF" p-id="43373"></path>
<path
d="M704 439.458909h-17.058909l-21.341091-162.117818s51.2-21.341091 64 42.658909c12.8 64-25.6 119.458909-25.6 119.458909m-392.541091 0h17.082182l21.317818-162.117818s-51.2-21.341091-64 42.658909c-8.517818 64 25.6 119.458909 25.6 119.458909"
fill="#46382E" p-id="43374"></path>
<path
d="M512 349.858909c102.4 0 174.941091-12.8 221.858909-102.4 0 0-46.917818-12.8-153.6-55.458909-166.4-68.258909-302.917818 42.658909-256 119.458909 12.8 25.6 85.341091 38.4 187.741091 38.4z"
fill="#46382E" p-id="43375"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg t="1648864059094" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="34164" width="200" height="200">
<path
d="M510.002302 510.002302m-510.002302 0a510.002302 510.002302 0 1 0 1020.004604 0 510.002302 510.002302 0 1 0-1020.004604 0Z"
fill="#F9E9E6" p-id="34165"></path>
<path
d="M511.532309 1023.999622c126.952795 0.151112 249.608349-44.738535 344.893779-126.216125l-7.09281-43.54853C849.031055 778.934072 786.206327 717.969908 708.676532 717.781018H318.043102c-77.539239 0.245557-140.326189 61.22861-140.571745 136.529505L170.000767 900.465731c94.813206 79.777582 216.08042 123.628336 341.531542 123.533891"
fill="#FCA183" p-id="34166"></path>
<path
d="M523.668475 806.200306H511.607865c-46.183542 0-96.050434-38.967954-96.050434-86.577613l12.117277-160.820726c0.160556-47.713549 37.636281-86.369834 83.942601-86.577613h11.966166c46.362987 0.122778 83.933157 38.797953 84.093712 86.577613l11.966166 160.820726c0 47.675771-49.781891 86.577613-95.984323 86.577613"
fill="#FFFFFF" p-id="34167"></path>
<path
d="M523.668475 806.200306H511.607865c-46.183542 0-96.050434-38.967954-96.050434-86.577613l12.117277-160.820726c0.160556-47.713549 37.636281-86.369834 83.942601-86.577613h11.966166c46.362987 0.122778 83.933157 38.797953 84.093712 86.577613l11.966166 160.820726c0 47.675771-49.781891 86.577613-95.984323 86.577613"
fill="#FFFFFF" p-id="34168"></path>
<path
d="M610.236643 651.46183l-5.987804-77.973686c-0.198334-45.437427-37.041278-82.21426-82.516484-82.374816h-11.833942c-45.475205 0.198334-82.280371 37.0035-82.440928 82.440928L415.557431 726.696614a67.490305 67.490305 0 0 0 8.575595 32.432368c5.940582 0.670559 11.918943 1.001116 17.887858 1.001116h17.604524a159.252941 159.252941 0 0 0 150.611235-108.668268"
fill="#DCE6EA" p-id="34169"></path>
<path
d="M647.070143 302.223586a45.48465 45.48465 0 0 1 45.522428 45.446872v79.73036A52.643571 52.643571 0 0 1 708.336531 425.001918c31.29903 0 56.666922 27.483457 56.666922 61.389166S739.635561 547.78025 708.336531 547.78025a52.700238 52.700238 0 0 1-19.956202-3.910017 166.383529 166.383529 0 0 1-44.672423 80.646475 167.025754 167.025754 0 0 1-118.027755 48.809109h-18.747307a167.035198 167.035198 0 0 1-118.0372-48.809109 166.383529 166.383529 0 0 1-44.219088-78.644244c-4.514465 1.237228-9.246153 1.907786-14.119508 1.907786-31.29903 0-56.666922-27.483457-56.666923-61.389166s25.367892-61.389166 56.666923-61.389166c3.22057 0 6.375029 0.283335 9.444487 0.850004v-78.181464A45.475205 45.475205 0 0 1 385.523962 302.223586h261.546181z"
fill="#FFFFFF" p-id="34170"></path>
<path
d="M703.727621 832.097089l-85.652053-80.202584C672.63637 763.426224 687.369769 848.039384 739.19167 868.892811c1.095561 0.54778 13.099504-2.200565 50.188004-10.993383 1.643341-1.086116-3.815573-49.432445-2.720012-47.222435 7.650035 13.72284 13.637839 28.002904 19.644533 42.292413 22.912326-29.117354 40.375182-51.085231 45.824651-85.689831l-25.641782-32.96126 31.648476 21.977321 20.173425-48.346329s-75.272562-45.040759-95.47432-99.969896c-13.628395-34.62349-19.096753-224.117678-19.096753-224.117678s-3.815573-28.361795-26.727899-80.542586c-1.633896-3.296126-2.729457-6.592252-4.363353-9.888378-62.739728-174.128008-225.874353-139.523408-225.874352-139.523408-108.026043-9.340598-157.675712 38.448507-194.773657 101.074901-8.726706 12.627279-16.905632 27.464568-24.007887 43.397418-26.727898 60.964164-40.365738 129.965587-28.919019 256.323379 0 0 12.003943 173.570783 80.750364 255.964489 22.912326-9.878933 37.097945-47.241324 60.010271-56.024697 46.939101 0.538336 27.832903 2.191121 35.473494-18.681196 10.360602-22.515657 7.631146-58.234707 1.624452-84.59427-15.271736-10.983938-28.90013-23.611218-30.543472-25.273448-9.822267-8.783373-70.389762-65.355851-76.934791-98.31711l-1.633897-3.296126c-0.54778-2.200565-0.54778-3.853351-1.086116-6.044472a29.051242 29.051242 0 0 1-21.278429-3.296126c-11.994499-7.130588-16.905632-21.420097-18.548973-34.614045-1.643341-11.522274-1.095561-25.811783 5.987805-36.247941C522.053468 412.969642 506.224507 266.296758 506.224507 266.296758 517.13289 382.756728 658.99853 426.701926 722.824374 441.529771c13.637839 25.273447 2.738901 74.715337-28.361795 76.349233-1.643341 0-2.738901 0-3.834462-0.538335-1.086116 4.391686-2.172232 8.235593-3.258348 12.636723-13.656728 50.528006-47.477437 78.011463-87.304838 112.60662-8.178926 8.235593-14.18562 8.783373-22.364546 14.827844 1.633896 21.977321-2.729457 58.225263 6.54503 76.897014 10.370047 1.652785 29.4668 7.149477 33.830153 17.585635"
fill="#3E435C" p-id="34171"></path>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg t="1658914025483" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3426" width="200" height="200"><path d="M390 679.988v-35.864c0-111.8-70.278-134.488-70.278-134.488S250 534.068 250 644.126v35.864h140z" fill="#4988FD" p-id="3427"></path><path d="M390 699.988h-140c-11.046 0-20-8.956-20-20v-35.864c0-122.828 79.714-152.174 83.108-153.364a20.012 20.012 0 0 1 12.758-0.158c3.434 1.11 84.132 28.598 84.132 153.522v35.864c0.002 11.046-8.952 20-19.998 20z m-120-40h100v-15.864c0-76.286-35.178-104.09-49.982-112.408-14.948 8.808-50.018 37.46-50.018 112.41v15.862z" fill="#4988FD" p-id="3428"></path><path d="M776 679.988v-35.864c0-111.8-70.278-134.488-70.278-134.488S636 534.068 636 644.126v35.864h140z" fill="#4988FD" p-id="3429"></path><path d="M776 699.988h-140c-11.044 0-20-8.956-20-20v-35.864c0-122.828 79.714-152.174 83.11-153.364a20.026 20.026 0 0 1 12.758-0.158c3.436 1.11 84.132 28.598 84.132 153.522v35.864c0 11.046-8.956 20-20 20z m-120-40h100v-15.864c0-76.286-35.178-104.09-49.982-112.408-14.948 8.808-50.018 37.46-50.018 112.41v15.862z" fill="#4988FD" p-id="3430"></path><path d="M693.146 651.988v-92.516c0-288.398-181.292-346.924-181.292-346.924S332 275.57 332 559.476c0.012 56.328 0 92.514 0 92.514l361.146-0.002z" fill="#4988FD" p-id="3431"></path><path d="M332 671.99a19.996 19.996 0 0 1-20-20.006c0-0.004 0.012-36.184 0-92.504 0-152.884 51.604-243.788 94.894-293.124 47.876-54.564 96.308-71.968 98.346-72.684a20.012 20.012 0 0 1 12.758-0.158c2.06 0.666 51.034 16.918 99.41 71.014 43.676 48.838 95.738 139.654 95.738 294.944v92.516c0 11.044-8.956 20-20 20L332 671.99z m180.098-437.812c-13.256 6.236-45.196 23.814-76.73 60.39C380.828 357.83 352 449.434 352 559.476c0.006 31.314 0.006 56.404 0.004 72.514l321.142-0.002v-72.516c0-111.762-29.016-203.89-83.91-266.424-31.678-36.086-63.762-52.93-77.138-58.87z" fill="#4988FD" p-id="3432"></path><path d="M497.858 864.994l-42.426-42.426c-31.244-31.244-31.244-81.894 0-113.138 31.242-31.242 81.894-31.242 113.136 0 31.242 31.24 31.244 81.894 0 113.138l-42.426 42.426c-7.81 7.81-20.474 7.81-28.284 0z" fill="#DFECFD" p-id="3433"></path><path d="M512 870.852a19.868 19.868 0 0 1-14.142-5.86l-42.426-42.424c-15.11-15.11-23.432-35.2-23.432-56.57s8.322-41.46 23.432-56.568S490.632 686 512 685.996c21.37 0.002 41.46 8.324 56.568 23.434S592 744.63 592 766s-8.322 41.46-23.432 56.57l-42.428 42.428a19.864 19.864 0 0 1-14.14 5.856zM512 688c-20.832-0.002-40.42 8.112-55.154 22.844S434 745.164 434 766c0 20.834 8.112 40.422 22.846 55.156l42.426 42.426a17.88 17.88 0 0 0 12.728 5.272 17.88 17.88 0 0 0 12.726-5.27l42.428-42.428c14.732-14.732 22.846-34.32 22.846-55.156 0-20.834-8.114-40.42-22.846-55.154S532.834 688 512 688z" fill="#DFECFD" p-id="3434"></path><path d="M312.576 803.972l-22.274-22.274c-16.402-16.402-16.402-42.996 0-59.398 16.4-16.4 42.994-16.4 59.396 0 16.402 16.402 16.404 42.996 0 59.398l-22.272 22.274a10.5 10.5 0 0 1-14.85 0z" fill="#DFECFD" p-id="3435"></path><path d="M320 806.896a10.784 10.784 0 0 1-7.68-3.18l-22.02-22.02c-16.374-16.374-16.374-43.02 0-59.396A41.728 41.728 0 0 1 320 710c11.22 0 21.764 4.366 29.7 12.3 16.374 16.376 16.374 43.022 0 59.398l-22.02 22.02a10.784 10.784 0 0 1-7.68 3.178zM320 712a39.744 39.744 0 0 0-28.284 11.714c-15.596 15.596-15.596 40.974 0 56.57l22.02 22.02c1.674 1.674 3.898 2.594 6.264 2.592s4.59-0.92 6.264-2.594l22.02-22.02c15.596-15.594 15.596-40.972 0-56.568A39.744 39.744 0 0 0 320 712z" fill="#DFECFD" p-id="3436"></path><path d="M698.576 803.972l-22.274-22.274c-16.4-16.4-16.4-42.994 0-59.398 16.4-16.4 42.994-16.4 59.396 0 16.402 16.402 16.4 42.996 0 59.398l-22.274 22.274a10.498 10.498 0 0 1-14.848 0z" fill="#DFECFD" p-id="3437"></path><path d="M706 806.896a10.784 10.784 0 0 1-7.68-3.18l-22.02-22.02c-16.37-16.374-16.37-43.02 0-59.396 7.936-7.934 18.482-12.3 29.7-12.3s21.764 4.366 29.7 12.3c16.374 16.376 16.372 43.02 0 59.398l-22.02 22.02a10.784 10.784 0 0 1-7.68 3.178zM706 712a39.736 39.736 0 0 0-28.284 11.714c-15.594 15.596-15.594 40.974 0 56.57l22.02 22.02c1.672 1.672 3.896 2.592 6.264 2.592s4.592-0.92 6.264-2.594l22.02-22.02c15.596-15.594 15.594-40.972 0-56.568A39.736 39.736 0 0 0 706 712z" fill="#DFECFD" p-id="3438"></path><path d="M512 352c22.092 0 40 17.908 40 40v80c0 22.092-17.908 40-40 40s-40-17.908-40-40v-80c0-22.092 17.908-40 40-40z" fill="#DFECFD" p-id="3439"></path><path d="M512 512c-22.056 0-40-17.944-40-40v-80c0-22.056 17.944-40 40-40s40 17.944 40 40v80c0 22.056-17.944 40-40 40z m0-158c-20.954 0-38 17.046-38 38v80c0 20.954 17.046 38 38 38s38-17.046 38-38v-80c0-20.954-17.046-38-38-38z" fill="#DFECFD" p-id="3440"></path></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M2.88 18.054a35.9 35.9 0 0 1 8.531-16.32.8.8 0 0 1 1.178 0q.25.27.413.455a35.9 35.9 0 0 1 8.118 15.865c-2.141.451-4.34.747-6.584.874l-2.089 4.178a.5.5 0 0 1-.894 0l-2.089-4.178a44 44 0 0 1-6.584-.874m6.698-1.123 1.157.066L12 19.527l1.265-2.53 1.157-.066a42 42 0 0 0 4.227-.454A33.9 33.9 0 0 0 12 4.09a33.9 33.9 0 0 0-6.649 12.387q2.093.334 4.227.454M12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/></svg>

After

Width:  |  Height:  |  Size: 533 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 18H19V11.0314C19 7.14806 15.866 4 12 4C8.13401 4 5 7.14806 5 11.0314V18ZM12 2C16.9706 2 21 6.04348 21 11.0314V20H3V11.0314C3 6.04348 7.02944 2 12 2ZM9.5 21H14.5C14.5 22.3807 13.3807 23.5 12 23.5C10.6193 23.5 9.5 22.3807 9.5 21Z"></path></svg>

After

Width:  |  Height:  |  Size: 334 B

@@ -0,0 +1 @@
<svg t="1733556119022" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="60026" width="128" height="128"><path d="M0 0m184.32 0l655.36 0q184.32 0 184.32 184.32l0 655.36q0 184.32-184.32 184.32l-655.36 0q-184.32 0-184.32-184.32l0-655.36q0-184.32 184.32-184.32Z" fill="#EC5D85" p-id="60027"></path><path d="M512 241.96096h52.224l65.06496-96.31744c49.63328-50.31936 89.64096 0.43008 63.85664 45.71136l-34.31424 51.5072c257.64864 5.02784 257.64864 43.008 257.64864 325.03808 0 325.94944 0 336.46592-404.48 336.46592S107.52 893.8496 107.52 567.90016c0-277.69856 0-318.80192 253.14304-324.95616l-39.43424-58.368c-31.26272-54.90688 37.33504-90.40896 64.68608-42.37312l60.416 99.80928c18.18624-0.0512 41.18528-0.0512 65.66912-0.0512z" fill="#EF85A7" p-id="60028"></path><path d="M512 338.5856c332.8 0 332.8 0 332.8 240.64s0 248.39168-332.8 248.39168-332.8-7.75168-332.8-248.39168 0-240.64 332.8-240.64z" fill="#EC5D85" p-id="60029"></path><path d="M281.6 558.08a30.72 30.72 0 0 1-27.47392-16.97792 30.72 30.72 0 0 1 13.73184-41.216l122.88-61.44a30.72 30.72 0 0 1 41.216 13.74208 30.72 30.72 0 0 1-13.74208 41.216l-122.88 61.44a30.59712 30.59712 0 0 1-13.73184 3.23584zM752.64 558.08a30.60736 30.60736 0 0 1-12.8512-2.83648l-133.12-61.44a30.72 30.72 0 0 1-15.04256-40.7552 30.72 30.72 0 0 1 40.76544-15.02208l133.12 61.44A30.72 30.72 0 0 1 752.64 558.08zM454.656 666.88a15.36 15.36 0 0 1-12.288-6.1952 15.36 15.36 0 0 1 3.072-21.49376l68.5056-50.91328 50.35008 52.62336a15.36 15.36 0 0 1-22.20032 21.23776l-31.5904-33.024-46.71488 34.72384a15.28832 15.28832 0 0 1-9.13408 3.04128z" fill="#EF85A7" p-id="60030"></path><path d="M65.536 369.31584c15.03232 101.90848 32.84992 147.17952 44.544 355.328 14.63296 2.18112 177.70496 10.04544 204.05248-74.62912a16.14848 16.14848 0 0 0 1.64864-10.87488c-30.60736-80.3328-169.216-60.416-169.216-60.416s-10.36288-146.50368-11.49952-238.83776zM362.25024 383.03744l34.816 303.17568h34.64192L405.23776 381.1328zM309.52448 536.28928h45.48608l16.09728 158.6176-31.82592 1.85344zM446.86336 542.98624h45.80352V705.3312h-33.87392zM296.6016 457.97376h21.39136l5.2736 58.99264-18.91328 2.26304zM326.99392 457.97376h21.39136l2.53952 55.808-17.408 1.61792zM470.62016 459.88864h19.456v62.27968h-19.456zM440.23808 459.88864h22.20032v62.27968h-16.62976z" fill="#FFFFFF" p-id="60031"></path><path d="M243.56864 645.51936a275.456 275.456 0 0 1-28.4672 23.74656 242.688 242.688 0 0 1-29.53216 17.52064 2.70336 2.70336 0 0 1-4.4032-1.95584 258.60096 258.60096 0 0 1-5.12-29.57312c-1.41312-12.1856-1.95584-25.68192-2.16064-36.36224 0-0.3072 0-2.5088 3.01056-1.90464a245.92384 245.92384 0 0 1 34.22208 9.5744 257.024 257.024 0 0 1 32.3584 15.17568c0.52224 0.256 2.51904 1.4848 0.09216 3.77856z" fill="#EB5480" p-id="60032"></path><path d="M513.29024 369.31584c15.03232 101.90848 32.84992 147.17952 44.544 355.328 14.63296 2.18112 177.70496 10.04544 204.05248-74.62912a16.14848 16.14848 0 0 0 1.64864-10.87488c-30.60736-80.3328-169.216-60.416-169.216-60.416s-10.36288-146.50368-11.49952-238.83776zM810.00448 383.03744l34.816 303.17568h34.64192L852.992 381.1328zM757.27872 536.28928h45.48608l16.09728 158.6176-31.82592 1.85344zM894.6176 542.98624h45.80352V705.3312H906.5472zM744.35584 457.97376h21.39136l5.2736 58.99264-18.91328 2.26304zM774.74816 457.97376h21.39136l2.53952 55.808-17.408 1.61792zM918.3744 459.88864h19.456v62.27968h-19.456zM887.99232 459.88864h22.20032v62.27968h-16.62976z" fill="#FFFFFF" p-id="60033"></path><path d="M691.32288 645.51936a275.456 275.456 0 0 1-28.4672 23.74656 242.688 242.688 0 0 1-29.53216 17.52064 2.70336 2.70336 0 0 1-4.4032-1.95584 258.60096 258.60096 0 0 1-5.12-29.57312c-1.41312-12.1856-1.95584-25.68192-2.16064-36.36224 0-0.3072 0-2.5088 3.01056-1.90464a245.92384 245.92384 0 0 1 34.22208 9.5744 257.024 257.024 0 0 1 32.3584 15.17568c0.52224 0.256 2.51904 1.4848 0.09216 3.77856z" fill="#EB5480" p-id="60034"></path></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -0,0 +1 @@
<svg t="1733620744216" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13366" width="128" height="128"><path d="M512.956 741.549c24.671-8.545 73.221-25.811 120.072-46.486 12.131-5.394 24.087-12.036 47.412-25.31a109.078 109.078 0 0 0 37.94-29.11 83.344 83.344 0 0 0 12.227-23.053V219.281c-0.096-24.831-20.211-44.936-45.045-45.043-9.34 1.573-22.76 4.209-38.757 8.543 24.439 62.406 18.146 132.708-17.021 189.792-34.498 55.993-92.88 92.712-158.098 99.992-1.275 0.137-2.499 0.463-3.776 0.585v223.354c0.11 24.835 20.213 44.934 45.046 45.045zM378.672 741.549c24.833-0.108 44.939-20.21 45.046-45.047V472.779a213.556 213.556 0 0 1-158.787-101.461c-34.357-56.592-40.495-125.929-16.655-187.697a423.782 423.782 0 0 0-42.873-9.482c-24.833 0.098-44.938 20.214-45.047 45.047v398.308a82.748 82.748 0 0 0 12.242 23.039c10.5 12.524 23.704 22.495 38.591 29.219 23.801 13.273 35.756 20.011 47.424 25.311 46.84 20.673 95.405 37.942 120.059 46.486z" fill="#FFBA00" opacity=".4" p-id="13367"></path><path d="M744.827 708.729a57 57 0 0 0 32.919-11.275 53.974 53.974 0 0 0 17.343-27.226V271.907c-0.066-23.054-17.548-42.33-40.493-44.667 0.528 2.635 0.815 5.297 0.855 7.974v398.309a82.915 82.915 0 0 1-12.227 23.037 108.602 108.602 0 0 1-37.941 29.126c-22.944 12.799-34.805 19.428-46.568 24.752 12.528 0.003 49.706 0.761 86.112-1.709zM141.202 227.432c-22.861 2.335-40.317 21.49-40.495 44.475V670.23a53.882 53.882 0 0 0 17.362 27.4 56.9 56.9 0 0 0 32.902 11.291c36.421 2.471 73.585 1.984 86.112 1.711-11.764-5.324-23.61-11.958-46.565-24.751a108.597 108.597 0 0 1-37.928-29.128 82.787 82.787 0 0 1-12.242-23.036V235.404c0.041-2.676 0.327-5.338 0.854-7.972z" fill="#FFBA00" opacity=".4" p-id="13368"></path><path d="M629.782 372.569c35.172-57.083 41.463-127.383 17.023-189.792-0.007-0.026-0.016-0.066-0.029-0.096-51.497 13.939-127.844 45.508-165.674 117.207a189.132 189.132 0 0 0-13.288 31.87v141.388c1.307-0.123 2.569-0.447 3.872-0.585 65.218-7.28 123.598-44 158.096-99.992zM248.658 183.62c-23.842 61.769-17.701 131.105 16.639 187.697 34.357 56.608 93.013 94.084 158.8 101.461h0.099v-141.02a190.385 190.385 0 0 0-13.273-31.87c-36.989-70.079-110.861-101.855-162.265-116.268z" fill="#FEC744" opacity=".4" p-id="13369"></path><path d="M593.104 570.52v223.357c0.105 24.83 20.215 44.938 45.05 45.046 24.668-8.544 73.218-25.811 120.071-46.488 12.127-5.392 24.086-12.036 47.409-25.306a109.116 109.116 0 0 0 37.941-29.114 83.408 83.408 0 0 0 12.225-23.051v-398.31c-0.092-24.833-20.21-44.938-45.045-45.047-9.481 1.602-23.134 4.265-39.446 8.723 24.355 62.297 18.091 132.42-16.915 189.423a213.388 213.388 0 0 1-161.29 100.767zM330.601 271.513c-24.833 0.094-44.939 20.214-45.048 45.045v398.31a82.678 82.678 0 0 0 12.24 23.039c10.502 12.524 23.708 22.493 38.595 29.22 23.799 13.271 35.753 20.013 47.422 25.307 46.841 20.677 95.404 37.944 120.062 46.488 24.831-0.109 44.938-20.216 45.045-45.046v-223.72c-65.791-7.377-124.448-44.86-158.79-101.463-34.354-56.591-40.496-125.93-16.655-187.696a423.057 423.057 0 0 0-42.871-9.484z" fill="#FFBA00" p-id="13370"></path><path d="M868.422 753.937a108.626 108.626 0 0 1-37.943 29.126c-22.944 12.796-34.805 19.428-46.567 24.75 12.526 0 49.702 0.765 86.112-1.71a56.988 56.988 0 0 0 32.916-11.274 54.009 54.009 0 0 0 17.346-27.228v-398.32c-0.069-23.053-17.552-42.328-40.496-44.667 0.529 2.635 0.815 5.299 0.855 7.974v398.31a83.035 83.035 0 0 1-12.223 23.039zM266.399 324.804c-22.863 2.338-40.319 21.491-40.496 44.477v398.323a53.886 53.886 0 0 0 17.361 27.4 56.879 56.879 0 0 0 32.901 11.291c36.419 2.471 73.587 1.983 86.111 1.708-11.764-5.323-23.608-11.953-46.566-24.75a108.578 108.578 0 0 1-37.928-29.124 82.698 82.698 0 0 1-12.238-23.039V332.779c0.039-2.675 0.326-5.338 0.855-7.975z" fill="#FFBA00" p-id="13371"></path><path d="M771.97 280.058c-51.497 13.939-127.844 45.508-165.675 117.207a188.947 188.947 0 0 0-13.289 31.871v141.386c66.797-6.206 126.786-43.472 161.969-100.58 35.184-57.11 41.473-127.45 16.995-189.884z" fill="#FFBA00" p-id="13372"></path><path d="M771.97 280.058c-51.497 13.939-127.844 45.508-165.675 117.207a188.947 188.947 0 0 0-13.289 31.871v141.386c66.797-6.206 126.786-43.472 161.969-100.58 35.184-57.11 41.473-127.45 16.995-189.884z" fill="#FEC744" p-id="13373"></path><path d="M549.294 570.155h0.095V429.132a189.948 189.948 0 0 0-13.271-31.871c-36.992-70.081-110.863-101.856-162.266-116.268-23.839 61.769-17.701 131.105 16.642 187.696 34.352 56.608 93.012 94.089 158.8 101.466z" fill="#FFBA00" p-id="13374"></path><path d="M549.294 570.155h0.095V429.132a189.948 189.948 0 0 0-13.271-31.871c-36.992-70.081-110.863-101.856-162.266-116.268-23.839 61.769-17.701 131.105 16.642 187.696 34.352 56.608 93.012 94.089 158.8 101.466z" fill="#FEC744" p-id="13375"></path></svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M917.6 267.2c-36.1-2.5-72.4-9.3-103.6-19.3-10.1-3-20.2-6.4-30.3-10-21.4-6.3-50.5-18.8-83.6-36.6-.4-.2-.7-.4-1.1-.6-7.8-4.2-15.7-8.7-23.8-13.4-10.9-6.3-21.7-12.9-32.5-19.9-.4-.3-.8-.5-1.2-.8-7.7-5-15.5-10.2-23.1-15.5-5-3.4-10-7.1-15-10.7-3.8-2.8-7.5-5.3-11.3-8.2-27.4-20.5-54.5-43.5-79.9-68.3-25.4 24.8-52.5 47.8-79.9 68.3-3.7 2.8-7.5 5.4-11.3 8.2-5 3.6-10 7.3-15 10.7-7.7 5.4-15.4 10.5-23.1 15.5-.4.3-.8.5-1.2.8-10.8 6.9-21.6 13.6-32.5 19.9-8.1 4.7-16 9.2-23.8 13.4-.3.2-.7.4-1 .6-33 17.8-62.2 30.3-83.6 36.6-10.1 3.6-20.2 7-30.3 10-31.1 10-67.4 16.8-103.6 19.3h.1c1.1 16.2 2.1 37.7 3.4 60.9h.7c6.1 86.8 23.5 210.2 49.7 282.8 1.2 3.2 2.2 6.5 3.3 9.6.6 1.5 1.2 2.8 1.8 4.3 62.8 162.1 171.9 280.1 303 323.4v.4c17.3 5.7 31.9 9.3 43.5 11.5 11.5-2.2 26.1-5.8 43.5-11.5v-.4C687 905 796.1 787 858.9 624.8c.6-1.5 1.2-2.8 1.8-4.3 1.2-3.1 2.2-6.4 3.3-9.6 26.2-72.5 43.6-196 49.7-282.8h.7c1.1-23.3 2.2-44.7 3.2-60.9zm-47.4 41.9-.5 9.5c-.5 2.2-.9 4.4-1 6.6C863 406 847 525.7 821.3 596.7c-.7 1.9-1.4 3.9-2 5.8-.4 1.2-.8 2.5-1.4 4.1-.5 1.2-1 2.5-1.4 3.4C758.1 760.8 657.7 869.3 541 907.8c-1.9.6-3.7 1.4-5.5 2.2-7.9 2.5-15.7 4.6-23.2 6.3-7.5-1.7-15.2-3.8-23.1-6.3-1.8-.9-3.6-1.6-5.5-2.2-116.7-38.5-217.1-147-275.4-297.5-.5-1.2-.9-2.4-1.7-4.1-.4-1.2-.8-2.4-1.3-3.6-.7-2-1.3-3.9-1.9-5.6-25.8-71.2-41.7-191-47.4-271.7-.2-2.3-.5-4.5-1-6.6l-.5-9.3c-.1-1.5-.2-3-.2-4.5 24.6-3.8 48.4-9.3 70-16.2 10.1-3 20.4-6.4 31.4-10.4 25.2-7.6 56.5-21.2 90.5-39.6.6-.3 1.2-.6 1.7-.9 8.2-4.4 16.7-9.2 24.8-14 10.7-6.1 22-13 34.5-21.1.4-.2 1-.6 1.3-.8 8.2-5.3 16.4-10.8 24.1-16.2 4.5-3.1 9.1-6.4 13.7-9.7l2.4-1.8 4-2.9c2.6-1.9 5.2-3.7 7.5-5.5 17.9-13.4 35.3-27.5 52-42.1 16.7 14.7 34 28.7 51.8 42 2.6 1.9 5.1 3.8 7.7 5.6l4.3 3.1 1.5 1.1c4.8 3.5 9.6 6.9 14 9.9 8.1 5.7 16.3 11.2 23.7 16l2.1 1.3c12.4 8 23.7 14.9 34.1 20.8 8.6 5 17 9.8 25 14.1.4.2 1 .5 1.5.8 34.2 18.4 65.6 32.1 90.9 39.7 11 3.9 21.3 7.3 30.6 10.1 22.1 7.1 46.1 12.6 70.8 16.5.1 1.5.1 3 0 4.4z"/><path d="M710.6 411.2 476.1 651.6l-120-123c-8.3-8.5-21.8-8.5-30.1 0s-8.3 22.3 0 30.9L461.1 698c4.2 4.3 9.6 6.4 15.1 6.4 5.4 0 10.9-2.1 15-6.4l249.5-255.7c8.3-8.5 8.3-22.3 0-30.9-8.3-8.7-21.8-8.7-30.1-.2z"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M832.128 768c33.195 0 60.501 25.173 63.573 57.813L896 832a64 64 0 0 1-63.872 64H533.205a63.787 63.787 0 0 1-63.872-64 64 64 0 0 1 63.872-64h298.923zM213.333 874.667c-23.722 0-42.666-19.072-42.666-42.624V362.667A42.667 42.667 0 0 1 213.333 320l4.992.299C239.66 322.73 256 340.779 256 362.624l-.043 128.043h128.299c21.248 0 39.595 16.469 42.112 37.674l.299 4.992-.299 4.992A42.368 42.368 0 0 1 384.256 576H256l.043 213.333h128.256c22.869 0 42.41 19.115 42.41 42.667l-.298 4.992a42.368 42.368 0 0 1-42.112 37.675zm618.795-405.334c33.195 0 60.501 25.174 63.573 57.814l.299 6.186a64 64 0 0 1-63.872 64H533.205a63.787 63.787 0 0 1-63.872-64 64 64 0 0 1 63.872-64h298.923zM576.171 128c33.194 0 60.458 25.173 63.573 57.813L640 192c0 35.328-29.013 64-63.83 64H191.83A63.744 63.744 0 0 1 128 192c0-35.328 29.013-64 63.83-64h384.34z"/></svg>

After

Width:  |  Height:  |  Size: 941 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M962.184 55.874H61.818C27.732 55.874 0 83.606 0 117.692v621.64c0 34.086 27.732 61.818 61.818 61.818h308.52v44.98c0 41.234-33.547 74.782-74.781 74.782h-67.995c-13.036 0-23.606 10.568-23.606 23.606 0 13.038 10.57 23.606 23.606 23.606h568.874c13.036 0 23.606-10.568 23.606-23.606 0-13.038-10.57-23.606-23.606-23.606h-67.997c-41.234 0-74.782-33.548-74.782-74.782v-44.978h308.52c34.087 0 61.821-27.732 61.821-61.819v-621.64c.004-34.087-27.728-61.819-61.814-61.819zM391.84 920.916c16.092-20.672 25.714-46.616 25.714-74.782v-44.98h188.894v44.98c0 28.166 9.622 54.112 25.714 74.782H391.841zm584.95-181.583c0 8.054-6.552 14.608-14.608 14.608H61.818c-8.054 0-14.608-6.552-14.608-14.608V615.267h929.58v124.066zm0-171.28H47.212v-450.36c0-8.055 6.552-14.609 14.608-14.609h900.362c8.054 0 14.61 6.552 14.61 14.608v450.361z"/><path d="M486.531 684.611a25.476 25.476 0 1 0 50.952 0 25.476 25.476 0 1 0-50.952 0zm65.946-466.103c-9.22-9.218-24.162-9.218-33.386 0L352.263 385.337c-9.218 9.218-9.218 24.166 0 33.386a23.534 23.534 0 0 0 16.694 6.914 23.526 23.526 0 0 0 16.692-6.914l166.828-166.829c9.218-9.218 9.218-24.166 0-33.386zm98.88 96.679c-9.216-9.218-24.158-9.218-33.384-.002l-66.46 66.456c-9.218 9.22-9.218 24.168 0 33.386a23.53 23.53 0 0 0 16.692 6.914c6.04 0 12.082-2.304 16.692-6.914l66.46-66.456c9.218-9.218 9.218-24.166 0-33.384z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 36 36"><path d="m19.41 18 8.29-8.29a1 1 0 0 0-1.41-1.41L18 16.59l-8.29-8.3a1 1 0 0 0-1.42 1.42l8.3 8.29-8.3 8.29A1 1 0 1 0 9.7 27.7l8.3-8.29 8.29 8.29a1 1 0 0 0 1.41-1.41z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 297 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 36 36"><path d="M26 17H10a1 1 0 0 0 0 2h16a1 1 0 0 0 0-2z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 183 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m7 12 7 7m-7-7 7-7" stroke-linejoin="round"/><path d="M21 12H7.5"/><path d="M3 3v18" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 310 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 20 20"><path d="M3 5h14V3H3v2zm12 8V7H5v6h10zM3 17h14v-2H3v2z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 187 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m17 12-7 7m7-7-7-7" stroke-linejoin="round"/><path d="M3 12h13.5"/><path d="M21 3v18" stroke-linejoin="round"/></g></svg>

After

Width:  |  Height:  |  Size: 311 B

@@ -0,0 +1 @@
<svg t="1733555747788" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10924" width="128" height="128"><path d="M851.404 172.596c-187.462-187.461-491.346-187.461-678.808 0-187.461 187.462-187.461 491.346 0 678.808 187.462 187.461 491.346 187.461 678.808 0 187.461-187.462 187.461-491.346 0-678.808zM387.33 728.087a47.084 47.084 0 1 1-66.633-66.502 47.084 47.084 0 0 1 66.633 66.502z m205.527 1.397a38.75 38.75 0 0 1-76.625-11.52h-0.044a6.545 6.545 0 0 0-0.044 0.305v-0.349c0.306-2.618 2.051-20.727-2.967-44.99a174.24 174.24 0 0 0-48.567-89.28 172.102 172.102 0 0 0-88.8-48.305 156.698 156.698 0 0 0-42.458-2.923 38.662 38.662 0 0 1-35.39-65.324 38.618 38.618 0 0 1 21.12-10.822v-0.218c4.452-0.742 111.142-16.45 200.335 72.742 89.018 89.018 74.182 196.145 73.44 200.727z m175.2 7.592a38.75 38.75 0 0 1-65.673 21.382 39.49 39.49 0 0 1-11.65-33.73c0.087-0.35 5.105-37.484-5.062-88.975-13.31-67.375-45.295-126.895-94.953-176.902-50.007-49.702-109.527-81.644-176.945-94.953-51.491-10.167-88.582-5.193-89.019-5.149h0.219-0.044a39.927 39.927 0 0 1-44.684-32.902 38.836 38.836 0 0 1 32.204-44.378c1.92-0.305 47.869-7.33 111.273 4.364a411.753 411.753 0 0 1 106.254 34.952 425.76 425.76 0 0 1 114.633 82.255l0.916 0.96 0.96 0.873a425.89 425.89 0 0 1 82.255 114.72c16.407 33.6 28.145 69.294 34.996 106.21 11.651 63.404 4.67 109.353 4.32 111.273z" fill="#1296DB" p-id="10925"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg t="1720831003829" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5159" width="200" height="200"><path d="M438.4 849.1l222.7-646.7c0.2-0.5 0.3-1.1 0.4-1.6L438.4 849.1z" opacity=".224" p-id="5160"></path><path d="M661.2 168.7h-67.5c-3.4 0-6.5 2.2-7.6 5.4L354.7 846c-0.3 0.8-0.4 1.7-0.4 2.6 0 4.4 3.6 8 8 8h67.8c3.4 0 6.5-2.2 7.6-5.4l0.7-2.1 223.1-648.3 7.4-21.4c0.3-0.8 0.4-1.7 0.4-2.6-0.1-4.5-3.6-8.1-8.1-8.1zM954.6 502.1c-0.8-1-1.7-1.9-2.7-2.7l-219-171.3c-3.5-2.7-8.5-2.1-11.2 1.4-1.1 1.4-1.7 3.1-1.7 4.9v81.3c0 2.5 1.1 4.8 3.1 6.3l115 90-115 90c-1.9 1.5-3.1 3.8-3.1 6.3v81.3c0 4.4 3.6 8 8 8 1.8 0 3.5-0.6 4.9-1.7l219-171.3c6.9-5.4 8.2-15.5 2.7-22.5zM291.1 328.1l-219 171.3c-1 0.8-1.9 1.7-2.7 2.7-5.4 7-4.2 17 2.7 22.5l219 171.3c1.4 1.1 3.1 1.7 4.9 1.7 4.4 0 8-3.6 8-8v-81.3c0-2.5-1.1-4.8-3.1-6.3l-115-90 115-90c1.9-1.5 3.1-3.8 3.1-6.3v-81.3c0-1.8-0.6-3.5-1.7-4.9-2.7-3.5-7.7-4.1-11.2-1.4z" p-id="5161"></path></svg>

After

Width:  |  Height:  |  Size: 967 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 4h18v2H3V4zm0 15h18v2H3v-2zm8-5h10v2H11v-2zm0-5h10v2H11V9zm-8 3.5L7 9v7l-4-3.5z"/></svg>

After

Width:  |  Height:  |  Size: 180 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="30px" height="30px" viewBox="0 0 30 30" version="1.1">
<title>ic/csdn</title>
<g id="ic/csdn" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M24.7385612,21.818791 C24.4728206,21.5662432 24.1090763,21.4267765 23.7575825,21.4343152 C23.3966653,21.4399693 23.0696724,21.5907441 22.8378562,21.8555424 C20.7581061,24.2349574 17.1347988,24.4922169 15.6732254,24.4922169 C12.9611634,24.4922169 10.886125,23.8043069 9.50559315,22.4501605 C8.19385225,21.1638629 7.50594216,19.2678696 7.46070972,16.8168365 C7.35516735,11.1345107 10.5732673,5.25806226 16.139685,5.25806226 C18.7980335,5.25806226 20.8627061,7.14368979 21.6260036,7.95410443 C21.8917442,8.23586486 22.2583155,8.39794779 22.6352525,8.39983247 C23.0131319,8.4092559 23.362741,8.24151892 23.599269,7.96164317 L23.8160078,7.70532598 C24.2607935,7.18232584 24.4605701,6.50572386 24.380471,5.80179394 C24.2984872,5.09126763 23.9422817,4.44293592 23.3778185,3.97459165 C22.0133064,2.84472288 19.6951436,1.5 16.3969445,1.5 C12.9715292,1.5 9.58757695,3.07465447 7.1129853,5.82441016 C4.51306208,8.71269021 3.1240491,12.6441435 3.20320588,16.895051 C3.26634283,20.3063311 4.38490349,23.1729373 6.44015269,25.1886081 C8.64806138,27.3550537 11.8821812,28.5 15.7947876,28.5 C20.3849384,28.5 23.2289283,27.1401996 24.8082945,26.0009074 C25.4198749,25.5608334 25.7845615,24.8644423 25.8128317,24.093606 C25.8392173,23.3190004 25.5225902,22.5604146 24.9430495,22.0119712 L24.7385612,21.818791 Z" id="Fill-1" fill="#FC5533"/>
</g>
<script xmlns=""/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M449.6 116.2H303.8c-14.2 0-25.7-11.5-25.7-25.7s11.5-25.7 25.7-25.7h145.8c14.2 0 25.7 11.5 25.7 25.7s-11.5 25.7-25.7 25.7zm0 0"/><path d="M160.1 859.3c-14.2 0-25.7-11.5-25.7-25.7V167.4c0-56.6 46-102.6 102.6-102.6h66.8c14.2 0 25.7 11.5 25.7 25.7s-11.5 25.7-25.7 25.7H237c-28.2 0-51.1 22.9-51.1 51.1v666.2c-.1 14.3-11.6 25.8-25.8 25.8zm373.5-512.6c-6.3 0-12.4-1.3-17.6-3.5-13.5-5.8-21.9-17.9-21.9-31.6v-221c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v189l27.7-26.6c14.1-13.5 36.1-13.5 50.1 0l22.1 21.3V90.5c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v219.6c0 14.5-8.6 27.5-22 33.2-13.3 5.7-28.7 2.9-39.2-7.2l-37.5-36-37.5 36c-7.6 7.6-17.5 10.6-27 10.6zm0 0"/><path d="M846.1 958.9H236.9c-56.6 0-102.6-46-102.6-102.6v-22.8c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v22.8c0 28.2 22.9 51.1 51.1 51.1H846c14.2 0 25.7 11.5 25.7 25.7.1 14.3-11.4 25.8-25.6 25.8zm0 0"/><path d="M160.1 876h-.9c-14.2-.5-25.3-12.4-24.8-26.6 1-28.2 6.3-48.5 16.7-63.6 13.8-20.1 35.4-30.3 64.3-30.3h615c3.2-2.7 6.4-6.1 8.6-8.6V133.1c-1.8-5.1-11.7-15-16.8-16.8H449.6c-14.2 0-25.7-11.5-25.7-25.7s11.5-25.7 25.7-25.7h373.6c19.8 0 36.7 13.9 45 22.2 8.3 8.3 22.2 25.2 22.2 45v621.6c0 10.8-6.2 19.6-12.3 26.7-4.6 5.4-10.3 11-15.6 15.4-1 .9-2.1 1.7-3.2 2.5-5.4 4.1-12.9 8.8-22.3 8.8H215.3c-15 0-28 0-29.5 44.2-.5 13.8-11.9 24.7-25.7 24.7zm0 0"/><path d="M284.4 806.4c-14.2 0-25.7-11.5-25.7-25.7V90.5c0-14.2 11.5-25.7 25.7-25.7s25.7 11.5 25.7 25.7v690.1c0 14.3-11.5 25.8-25.7 25.8zM844.9 959h-1.6c-6.6-.3-30-2.3-52.2-16.9-19.5-12.7-42.6-38-42.6-86.3 0-62.3 35.7-101 93.1-101 14.2 0 25.7 11.5 25.7 25.7s-11.5 25.7-25.7 25.7c-12.5 0-41.7 0-41.7 49.6 0 21 6.6 35.3 20.1 43.8 10.6 6.6 22.1 7.8 25 8 1.4-.1 2.9 0 4.4.2 13.7 1.7 23.6 14 22.5 27.7-.9 9.5-8.8 23.5-27 23.5zm-1.8-51.3c-1.1.1-2.3.3-3.4.6 1.1-.3 2.2-.5 3.4-.6zm0 0"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M832.1 185.1H609.4l-17.1-62c-9.6-34.6-40.5-58.8-75.3-58.8H196c-43.2 0-78.3 36.4-78.3 81.1V897c0 35.3 28.7 64 64 64H832c35.3 0 64-28.7 64-64V249c.1-35.2-28.6-63.9-63.9-63.9zm-644.4-39.7c0-6.6 4.4-11.1 8.3-11.1h321c3.4 0 6.6 3.1 7.8 7.4l12 43.4H187.7v-39.7zm638.4 745.8H187.7V255.1h638.4v636.1z"/><path d="M311.1 415.1a35 35 0 1 0 70 0 35 35 0 1 0-70 0zm151.2-35h257.8v70H462.3zM311.1 582.3a35 35 0 1 0 70 0 35 35 0 1 0-70 0zm151.2-35h257.8v70H462.3zM311.1 749.5a35 35 0 1 0 70 0 35 35 0 1 0-70 0zm151.2-35h257.8v70H462.3z"/></svg>

After

Width:  |  Height:  |  Size: 640 B

@@ -0,0 +1 @@
<svg width="15" height="15" aria-label="向下键" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3"></path></g></svg>

After

Width:  |  Height:  |  Size: 222 B

@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M624 706.3h-74.1V464c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v242.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.7c3.2 4.1 9.4 4.1 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9z"/><path d="M811.4 366.7C765.6 245.9 648.9 160 512.2 160S258.8 245.8 213 366.6C127.3 389.1 64 467.2 64 560c0 110.5 89.5 200 199.9 200H304c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8h-40.1c-33.7 0-65.4-13.4-89-37.7-23.5-24.2-36-56.8-34.9-90.6.9-26.4 9.9-51.2 26.2-72.1 16.7-21.3 40.1-36.8 66.1-43.7l37.9-9.9 13.9-36.6c8.6-22.8 20.6-44.1 35.7-63.4 14.9-19.2 32.6-35.9 52.4-49.9 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.2c19.9 14 37.5 30.8 52.4 49.9 15.1 19.3 27.1 40.7 35.7 63.4l13.8 36.5 37.8 10C846.1 454.5 884 503.8 884 560c0 33.1-12.9 64.3-36.3 87.7-23.4 23.4-54.5 36.3-87.6 36.3H720c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h40.1C870.5 760 960 670.5 960 560c0-92.7-63.1-170.7-148.6-193.3z"/></svg>

After

Width:  |  Height:  |  Size: 962 B

@@ -0,0 +1 @@
<svg width="15" height="15" aria-label="回车键" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3"></path></g></svg>

After

Width:  |  Height:  |  Size: 241 B

@@ -0,0 +1 @@
<svg width="15" height="15" aria-label="Esc 键" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956"></path></g></svg>

After

Width:  |  Height:  |  Size: 691 B

+12
View File
@@ -0,0 +1,12 @@
<svg t="1640574422482" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3140"
width="128" height="128">
<path
d="M913.29536 941.04064c0.0256 24.82688-16.54784 44.96384-37.0176 44.98432l-708.23936 0.6912c-20.46464 0.02048-37.07904-20.08576-37.10464-44.91264l-0.83968-859.02848c-0.0256-24.82688 16.54784-44.96384 37.0176-44.98432l521.10848-0.50688 224.39424 210.50368 0.68096 693.25312z"
fill="#e6e6e6" p-id="3141"></path>
<path
d="M913.29536 253.26592l-189.11744 0.18432c-20.46464 0.02048-37.07904-20.08576-37.10464-44.91264l-0.16384-165.77024 226.38592 210.49856z"
fill="#C4BCB1" p-id="3142"></path>
<path
d="M720.72192 396.84096a22.54848 22.54848 0 0 1-22.54848 22.54848H326.13376a22.54848 22.54848 0 0 1 0-45.09696h372.0448a22.54848 22.54848 0 0 1 22.54336 22.54848zM720.72192 565.95456a22.54848 22.54848 0 0 1-22.54848 22.54848H326.13376a22.54848 22.54848 0 0 1 0-45.09696h372.0448a22.54848 22.54848 0 0 1 22.54336 22.54848zM720.72192 746.33728a22.54848 22.54848 0 0 1-22.54848 22.54848H326.13376a22.54848 22.54848 0 0 1 0-45.09696h372.0448a22.54848 22.54848 0 0 1 22.54336 22.54848z"
fill="#8a8a8a" p-id="3143"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
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="1627885889837" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17560" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M0 139.636364a46.545455 46.545455 0 0 1 46.545455-46.545455h354.897454a51.2 51.2 0 0 1 47.057455 31.034182L473.6 182.679273 977.454545 182.690909a46.545455 46.545455 0 0 1 46.545455 46.545455v546.909091a46.545455 46.545455 0 0 1-46.545455 46.545454H46.545455a46.545455 46.545455 0 0 1-46.545455-46.545454V139.636364z" fill="#FFA000" p-id="17561"></path><path d="M0 276.945455m46.545455 0l930.90909 0q46.545455 0 46.545455 46.545454l0 558.545455q0 46.545455-46.545455 46.545454l-930.90909 0q-46.545455 0-46.545455-46.545454l0-558.545455q0-46.545455 46.545455-46.545454Z" fill="#FFCA28" p-id="17562"></path></svg>

After

Width:  |  Height:  |  Size: 989 B

+14
View File
@@ -0,0 +1,14 @@
<svg t="1642407662269" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="19932" width="200" height="200">
<path d="M847.872 240.128v688c0 26.56-21.408 48-48 48h-576c-26.56 0-48-21.44-48-48v-832c0-26.592 21.44-48 48-48h432z"
fill="#E9EDED" p-id="19933"></path>
<path d="M160 768.128v160c0 35.456 28.544 64 64 64h576c35.456 0 64-28.544 64-64v-160H160z" fill="#4BBFEB"
p-id="19934"></path>
<path d="M847.872 240.128h-144c-26.56 0-48-21.44-48-48v-144" fill="#4BBFEB" p-id="19935"></path>
<path
d="M432.256 320.128c-35.2 0-64 28.8-64 64v32c0 18.016-14.016 32-32 32a16 16 0 0 0-15.936 12.992 16 16 0 0 0 0 0.064 16 16 0 0 0 4.736 14.56 16 16 0 0 0 2.496 1.92 16 16 0 0 0 4.448 1.92 16 16 0 0 0 3.136 0.48 16 16 0 0 0 1.12 0.064c17.984 0 32 13.984 32 32v32c0 35.2 28.8 64 64 64a16 16 0 1 0 0-32c-18.016 0-32-13.984-32-32v-32c0-19.136-8.736-36.256-22.208-48a63.68 63.68 0 0 0 22.208-48v-32c0-18.016 13.984-32 32-32a16 16 0 1 0 0-32z m157.856 0a16 16 0 0 0 1.632 32c18.016 0 32 13.984 32 32v32c0 19.168 8.736 36.224 22.208 48-13.44 11.744-22.208 28.864-22.208 48v32c0 18.016-13.984 32-32 32a16 16 0 1 0 0 32c35.2 0 64-28.8 64-64v-32c0-18.016 14.016-32 32-32a16 16 0 0 0 10.368-3.616 16 16 0 0 0 1.216-1.152 16 16 0 0 0-11.584-27.232c-17.984 0-32-13.984-32-32v-32c0-35.2-28.8-64-64-64a16 16 0 0 0-1.6 0z"
fill="#4BBFEB" p-id="19936"></path>
<path
d="M334.496 800.128a16 16 0 0 0-3.36 0.672c-41.664 3.712-75.008 37.44-75.008 79.328 0 42.08 33.696 75.904 75.616 79.296a16 16 0 0 0 4.64 0.704h32a16 16 0 1 0 0-32h-29.824c-28.544 0-50.432-21.44-50.432-48s21.888-48 50.432-48h29.568a16 16 0 1 0 0-32h-32a16 16 0 0 0-1.6 0z m128 0a16 16 0 0 0-1.984 0.384c-24.64 1.92-44.384 22.528-44.384 47.616 0 25.28 20.064 46.08 44.992 47.68a16 16 0 0 0 3.008 0.32h32c9.152 0 16 6.848 16 16 0 9.152-6.848 16-16 16h-64a16 16 0 1 0 0 32h64a16 16 0 0 0 3.296-0.384 48.096 48.096 0 0 0 44.704-47.616c0-25.152-19.84-45.888-44.576-47.68a16 16 0 0 0-3.424-0.32h-32a15.616 15.616 0 0 1-16-16c0-9.152 6.848-16 16-16h64a16 16 0 1 0 0-32h-62.88a16 16 0 0 0-1.12 0 16 16 0 0 0-1.6 0z m159.744 0a16 16 0 0 0-1.984 0.384c-24.64 1.92-44.384 22.528-44.384 47.616 0 25.28 20.096 46.08 44.992 47.68a16 16 0 0 0 3.008 0.32h32c9.152 0 16 6.848 16 16 0 9.152-6.848 16-16 16h-64a16 16 0 1 0 0 32h64a16 16 0 0 0 3.328-0.384 48.096 48.096 0 0 0 44.672-47.616c0-25.152-19.84-45.888-44.544-47.68a16 16 0 0 0-3.456-0.32h-32a15.616 15.616 0 0 1-16-16c0-9.152 6.848-16 16-16h64a16 16 0 1 0 0-32h-62.88a16 16 0 0 0-1.12 0 16 16 0 0 0-1.6 0z"
fill="#E9EDED" p-id="19937"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+1
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="1627885889837" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17560" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M0 139.636364a46.545455 46.545455 0 0 1 46.545455-46.545455h354.897454a51.2 51.2 0 0 1 47.057455 31.034182L473.6 182.679273 977.454545 182.690909a46.545455 46.545455 0 0 1 46.545455 46.545455v546.909091a46.545455 46.545455 0 0 1-46.545455 46.545454H46.545455a46.545455 46.545455 0 0 1-46.545455-46.545454V139.636364z" fill="#FFA000" p-id="17561"></path><path d="M0 276.945455m46.545455 0l930.90909 0q46.545455 0 46.545455 46.545454l0 558.545455q0 46.545455-46.545455 46.545454l-930.90909 0q-46.545455 0-46.545455-46.545454l0-558.545455q0-46.545455 46.545455-46.545454Z" fill="#FFCA28" p-id="17562"></path></svg>

After

Width:  |  Height:  |  Size: 989 B

+11
View File
@@ -0,0 +1,11 @@
<svg t="1642407332637" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6055"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.577778-51.2 113.777778-113.777778 113.777778H170.666667c-62.577778 0-113.777778-51.2-113.777778-113.777778V113.777778c0-62.577778 51.2-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#62C558" p-id="6056"></path>
<path d="M685.511111 224.711111V0L967.111111 281.6H742.4c-31.288889 0-56.888889-25.6-56.888889-56.888889"
fill="#2A8121" p-id="6057"></path>
<path
d="M682.666667 724.024889L638.691556 768 341.333333 470.670222 385.308444 426.666667zM454.087111 611.128889l44.088889 44.088889L385.422222 768 341.333333 723.911111zM682.666667 470.755556l-113.066667 113.066666-44.088889-44.088889L638.577778 426.666667z"
fill="#FFFFFF" p-id="6058"></path>
</svg>

After

Width:  |  Height:  |  Size: 894 B

+20
View File
@@ -0,0 +1,20 @@
<svg t="1642408099555" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="53361" width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#BABABA" p-id="53362"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#979797" p-id="53363"></path>
<path
d="M586.865778 521.671111a148.650667 148.650667 0 0 1-3.754667 49.265778l44.629333 22.556444a164.664889 164.664889 0 0 1-10.154666 26.254223l-4.266667 8.448a162.986667 162.986667 0 0 1-15.104 23.751111l-44.657778-22.528a149.048889 149.048889 0 0 1-37.404444 32.312889l15.587555 47.388444a192.910222 192.910222 0 0 1-62.179555 20.48l-15.587556-47.416889a148.053333 148.053333 0 0 1-49.322666-3.783111l-22.528 44.657778a163.612444 163.612444 0 0 1-26.254223-10.154667l-8.448-4.266667a158.350222 158.350222 0 0 1-23.751111-15.132444l22.528-44.600889a147.569778 147.569778 0 0 1-32.312889-37.461333l-47.416888 15.644444a195.868444 195.868444 0 0 1-12.856889-30.264889l-7.594667-31.943111 47.416889-15.616a148.650667 148.650667 0 0 1 3.754667-49.294222l-44.600889-22.528c2.190222-7.452444 4.892444-14.904889 8.078222-21.959111l8.533333-16.952889c3.84-6.769778 8.220444-13.368889 12.885334-19.569778l44.629333 22.528c10.353778-12.515556 23.04-23.608889 37.432889-32.284444l-15.587556-47.416889c9.557333-5.034667 19.626667-9.386667 30.236445-12.885333L410.737778 341.333333l15.587555 47.416889a147.370667 147.370667 0 0 1 49.322667 3.754667l22.528-44.629333c7.452444 2.190222 14.876444 4.920889 21.959111 8.106666l4.266667 2.048 8.504889 4.266667 4.181333 2.275555c6.741333 3.754667 13.368889 8.135111 19.569778 12.828445l-22.528 44.657778a147.911111 147.911111 0 0 1 32.284444 37.404444l47.416889-15.587555a195.527111 195.527111 0 0 1 20.451556 62.179555l-47.416889 15.587556z"
fill="#FFFFFF" p-id="53364"></path>
<path
d="M520.618667 508.984889a84.707556 84.707556 0 1 1-160.938667 52.963555 84.707556 84.707556 0 0 1 160.938667-52.963555"
fill="#BABABA" p-id="53365"></path>
<path
d="M742.371556 771.84c-4.864 6.826667-10.865778 12.743111-17.521778 17.436444l9.557333 23.096889c-3.896889 2.56-8.106667 4.807111-12.401778 6.656l-4.408889 1.792a79.644444 79.644444 0 0 1-13.454222 4.067556l-9.557333-23.096889c-7.992889 1.365333-16.440889 1.422222-24.746667 0l-9.557333 23.04a97.792 97.792 0 0 1-30.208-12.515556l9.557333-23.04a74.524444 74.524444 0 0 1-17.464889-17.521777l-23.096889 9.557333a82.488889 82.488889 0 0 1-6.627555-12.430222l-1.792-4.380445a80.554667 80.554667 0 0 1-4.096-13.454222l23.096889-9.557333a75.264 75.264 0 0 1 0-24.746667l-23.068445-9.557333a98.986667 98.986667 0 0 1 5.006223-15.644445l7.566222-14.592 23.04 9.557334c4.835556-6.826667 10.894222-12.743111 17.521778-17.436445l-9.557334-23.096889c3.271111-2.104889 6.741333-4.039111 10.24-5.688889l8.789334-3.612444c3.640889-1.308444 7.452444-2.389333 11.235555-3.214222l9.557333 23.096889c8.021333-1.365333 16.440889-1.422222 24.718223 0l9.585777-23.068445c5.233778 1.223111 10.496 2.844444 15.644445 5.006222l14.592 7.537778-9.585778 23.04c6.826667 4.892444 12.771556 10.894222 17.436445 17.521778l23.096888-9.528889c2.133333 3.271111 4.067556 6.712889 5.688889 10.24l0.967111 2.161778 1.792 4.380444 0.853334 2.218667c1.336889 3.640889 2.417778 7.480889 3.214222 11.264l-23.096889 9.557333c1.393778 7.964444 1.422222 16.440889 0 24.718223l23.04 9.557333a96 96 0 0 1-12.515555 30.236444l-23.04-9.557333z"
fill="#FFFFFF" p-id="53366"></path>
<path
d="M721.408 745.415111a42.382222 42.382222 0 1 1-78.250667-32.455111 42.382222 42.382222 0 0 1 78.222223 32.426667"
fill="#BABABA" p-id="53367"></path>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg t="1642407647664" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="19787" width="200" height="200">
<path d="M847.872 240.128v688c0 26.56-21.408 48-48 48h-576c-26.56 0-48-21.44-48-48v-832c0-26.592 21.44-48 48-48h432z"
fill="#E9EDED" p-id="19788"></path>
<path d="M160 768.128v160c0 35.456 28.544 64 64 64h576c35.456 0 64-28.544 64-64v-160H160z" fill="#F05542"
p-id="19789"></path>
<path d="M847.872 240.128h-144c-26.56 0-48-21.44-48-48v-144" fill="#F05542" p-id="19790"></path>
<path
d="M432.736 384.064a16 16 0 0 0-10.976 4.8l-96.96 95.776a16 16 0 0 0-0.416 0.416 16 16 0 0 0 1.664 23.808l95.68 94.56a16 16 0 1 0 22.528-22.72l-85.568-84.576 85.568-84.48a16 16 0 0 0-11.52-27.584z m157.824 0a16 16 0 0 0-11.008 27.552l85.504 84.48-85.504 84.576a16 16 0 1 0 22.496 22.752l95.648-94.56a16 16 0 0 0 5.44-17.504 16 16 0 0 0-1.28-2.912 16 16 0 0 0-0.608-1.12 16 16 0 0 0-0.192-0.256 16 16 0 0 0-0.192-0.256 16 16 0 0 0-0.8-0.992 16 16 0 0 0-0.192-0.256 16 16 0 0 0-0.864-0.96 16 16 0 0 0-0.064 0l-0.64-0.544a16 16 0 0 0-0.48-0.512l-95.776-94.688a16 16 0 0 0-11.488-4.8z"
fill="#F05542" p-id="19791"></path>
<path
d="M654.88 799.744a16 16 0 0 0-11.84 6.624L608 853.312l-35.136-46.944-0.128 0.064a16 16 0 0 0-13.056-6.368 16 16 0 0 0-15.744 16.192v127.68a16 16 0 1 0 32 0v-80.128l15.936 21.248a16 16 0 0 0 28.864 4.448l19.2-25.6v80.032a16 16 0 1 0 32 0v-126.816a16 16 0 0 0-15.424-17.376 16 16 0 0 0-1.632 0z m-383.136 0.32a16 16 0 0 0-15.744 16.192V943.936a16 16 0 1 0 32 0v-47.808h64v47.808a16 16 0 1 0 32 0v-61.312a16 16 0 0 0 0-5.12v-61.248a16 16 0 0 0-16.256-16.192 16 16 0 0 0-15.744 16.192v47.872H288v-47.872a16 16 0 0 0-16.256-16.192z m192 0a16 16 0 0 0-1.6 0.064H432a16 16 0 1 0 0 32h16v111.808a16 16 0 1 0 32 0v-111.808h16a16 16 0 1 0 0-32h-30.304a16 16 0 0 0-1.92-0.064z m255.936 0a16 16 0 0 0-15.744 16.192v126.496a16 16 0 0 0 0.64 5.824 16 16 0 0 0 0 0.064 16 16 0 0 0 0.096 0.416 16 16 0 0 0 0.448 1.12 16 16 0 0 0 0.864 1.824 16 16 0 0 0 0.64 0.992 16 16 0 0 0 0.192 0.32 16 16 0 0 0 0.736 0.96 16 16 0 0 0 0.832 0.864 16 16 0 0 0 0.352 0.416 16 16 0 0 0 0.832 0.704 16 16 0 0 0 0.448 0.384 16 16 0 0 0 0.352 0.32 16 16 0 0 0 1.12 0.736 16 16 0 0 0 2.464 1.248 16 16 0 0 0 0.864 0.32 16 16 0 0 0 5.376 0.864h63.552a16 16 0 1 0 0-32h-47.808v-111.872a16 16 0 0 0-16.256-16.192z"
fill="#E9EDED" p-id="19792"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg t="1642407370336" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6354"
width="200" height="200">
<path
d="M952.888889 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H156.444444c-62.862222 0-113.777778-50.915556-113.777777-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777777-113.777778h514.844445L952.888889 281.6z"
fill="#85BCFF" p-id="6355"></path>
<path d="M676.664889 167.822222V0l281.6 281.6h-167.822222c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#529EE0" p-id="6356"></path>
<path
d="M685.824 363.804444a53.76 53.76 0 0 1 53.731556 53.731556v307.029333a53.76 53.76 0 0 1-53.731556 53.731556H309.76a53.731556 53.731556 0 0 1-53.731556-53.76V417.564444c0-29.667556 24.035556-53.731556 53.731556-53.731555H685.795556z m-72.903111 149.674667l-138.183111 146.545778-80.583111-62.805333-92.131556 94.208v31.402666c0 11.548444 10.325333 20.906667 23.04 20.906667h345.400889c12.714667 0 23.04-9.386667 23.04-20.906667v-125.610666l-80.583111-83.740445z m-227.896889-85.532444a32.085333 32.085333 0 1 0 0 64.142222 32.085333 32.085333 0 0 0 0-64.142222z"
fill="#FFFFFF" p-id="6357"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg t="1642408007951" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="50023" width="200" height="200">
<path d="M847.872 240.128v688c0 26.56-21.408 48-48 48h-576c-26.56 0-48-21.44-48-48v-832c0-26.592 21.44-48 48-48h432z"
fill="#E9EDED" p-id="50024"></path>
<path d="M160 768.128v160c0 35.456 28.544 64 64 64h576c35.456 0 64-28.544 64-64v-160H160z" fill="#25B39E"
p-id="50025"></path>
<path d="M847.872 240.128h-144c-26.56 0-48-21.44-48-48v-144" fill="#25B39E" p-id="50026"></path>
<path
d="M432.256 320.128c-35.2 0-64 28.8-64 64v32c0 18.016-14.016 32-32 32a16 16 0 0 0-3.2 0.256 16 16 0 0 0-12.384 11.232 16 16 0 0 0-0.352 1.504 16 16 0 0 0 0 0.064 16 16 0 0 0 15.936 18.944c17.984 0 32 13.984 32 32v32c0 35.2 28.8 64 64 64a16 16 0 1 0 0-32c-18.016 0-32-13.984-32-32v-32c0-19.136-8.736-36.256-22.208-48a63.68 63.68 0 0 0 22.208-48v-32c0-18.016 13.984-32 32-32a16 16 0 1 0 0-32z m157.856 0a16 16 0 0 0 1.632 32c18.016 0 32 13.984 32 32v32c0 19.168 8.736 36.224 22.208 48-13.44 11.744-22.208 28.864-22.208 48v32c0 18.016-13.984 32-32 32a16 16 0 1 0 0 32c35.2 0 64-28.8 64-64v-32c0-18.016 14.016-32 32-32a16 16 0 0 0 10.368-3.616 16 16 0 0 0 1.216-1.152 16 16 0 0 0-11.584-27.232c-17.984 0-32-13.984-32-32v-32c0-35.2-28.8-64-64-64a16 16 0 0 0-1.6 0zM512 367.936a32 32 0 0 0-32 32 32 32 0 0 0 32 32 32 32 0 0 0 32-32 32 32 0 0 0-32-32z m0 96a32 32 0 0 0-32 32 32 32 0 0 0 16.256 27.872l-14.4 28.864a16 16 0 1 0 28.64 14.272l24-48.256a32 32 0 0 0 9.44-21.504 16 16 0 0 0 0-0.192 32 32 0 0 0 0.064-1.056 32 32 0 0 0-32-32z"
fill="#25B39E" p-id="50027"></path>
<path
d="M335.872 800a16 16 0 0 0-15.744 16.256V912c0 9.152-6.848 16-16 16a15.616 15.616 0 0 1-16-16 16 16 0 0 0-16.256-16.192 16 16 0 0 0-15.744 16.192c0 26.304 21.696 48 48 48 24.768 0 45.12-19.296 47.488-43.52a16 16 0 0 0 0.512-4.224v-96a16 16 0 0 0-16.256-16.256z m94.4 0.128a16 16 0 0 0-2.016 0.384c-24.64 1.92-44.384 22.528-44.384 47.616 0 25.28 20.096 46.08 44.992 47.68a16 16 0 0 0 3.008 0.32h32c9.152 0 16 6.848 16 16 0 9.152-6.848 16-16 16h-64a16 16 0 1 0 0 32h64a16 16 0 0 0 3.328-0.384 48.096 48.096 0 0 0 44.672-47.616c0-25.152-19.84-45.888-44.544-47.68a16 16 0 0 0-3.456-0.32h-30.88a16 16 0 0 0-1.12 0 15.616 15.616 0 0 1-16-16c0-9.152 6.848-16 16-16h64a16 16 0 1 0 0-32h-62.88a16 16 0 0 0-1.12 0 16 16 0 0 0-1.6 0z"
fill="#E9EDED" p-id="50028"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg t="1642407743753" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="20514" width="200" height="200">
<path d="M847.872 240.128v688c0 26.56-21.408 48-48 48h-576c-26.56 0-48-21.44-48-48v-832c0-26.592 21.44-48 48-48h432z"
fill="#E9EDED" p-id="20515"></path>
<path d="M160 768.128v160c0 35.456 28.544 64 64 64h576c35.456 0 64-28.544 64-64v-160H160z" fill="#F17F53"
p-id="20516"></path>
<path d="M847.872 240.128h-144c-26.56 0-48-21.44-48-48v-144" fill="#F17F53" p-id="20517"></path>
<path
d="M432.256 320.128c-35.2 0-64 28.8-64 64v32c0 18.016-14.016 32-32 32a16 16 0 0 0-15.936 12.992 16 16 0 0 0 0 0.064 16 16 0 0 0 4.736 14.56 16 16 0 0 0 2.496 1.92 16 16 0 0 0 4.448 1.92 16 16 0 0 0 3.136 0.48 16 16 0 0 0 1.12 0.064c17.984 0 32 13.984 32 32v32c0 35.2 28.8 64 64 64a16 16 0 1 0 0-32c-18.016 0-32-13.984-32-32v-32c0-19.136-8.736-36.256-22.208-48a63.68 63.68 0 0 0 22.208-48v-32c0-18.016 13.984-32 32-32a16 16 0 1 0 0-32z m125.856 0a16 16 0 0 0 1.632 32c18.016 0 32 13.984 32 32v32c0 19.168 8.736 36.224 22.208 48-13.44 11.744-22.208 28.864-22.208 48v32c0 18.016-13.984 32-32 32a16 16 0 1 0 0 32c35.2 0 64-28.8 64-64v-32c0-18.016 14.016-32 32-32a16 16 0 0 0 10.368-3.616 16 16 0 0 0 1.216-1.152 16 16 0 0 0-11.584-27.232c-17.984 0-32-13.984-32-32v-32c0-35.2-28.8-64-64-64a16 16 0 0 0-1.6 0zM496 384a16 16 0 0 0-16 16 16 16 0 0 0 16 16 16 16 0 0 0 16-16 16 16 0 0 0-16-16z m-0.256 63.808A16 16 0 0 0 480 464v96a16 16 0 1 0 32 0v-96a16 16 0 0 0-16.256-16.192z"
fill="#F17F53" p-id="20518"></path>
<path
d="M720.576 799.616a16 16 0 0 0-1.632 0.064 16 16 0 0 0-15.008 17.312v126.816a16 16 0 1 0 32 0v-80l66.624 88.96a16 16 0 0 0 0.064 0.096l0.448 0.576a16 16 0 0 0 28.864-9.6v-127.68a16 16 0 0 0-16.256-16.224 16 16 0 0 0-15.744 16.256v79.68l-67.136-89.6a16 16 0 0 0-12.224-6.656zM303.744 800a16 16 0 0 0-15.744 16.256V912c0 9.152-6.848 16-16 16a15.616 15.616 0 0 1-16-16 16 16 0 0 0-16.256-16.192A16 16 0 0 0 224 912c0 26.304 21.696 48 48 48 24.768 0 45.152-19.296 47.488-43.52a16 16 0 0 0 0.512-4.224v-96A16 16 0 0 0 303.744 800z m288.256 0a80.256 80.256 0 0 0-80 80c0 44 36 80 80 80s80-36 80-80-36-80-80-80z m-193.888 0.128a16 16 0 0 0-1.984 0.384c-24.64 1.92-44.384 22.528-44.384 47.616 0 25.28 20.096 46.08 44.992 47.68a16 16 0 0 0 3.008 0.32h32c9.152 0 16 6.848 16 16 0 9.152-6.848 16-16 16h-64a16 16 0 1 0 0 32h64a16 16 0 0 0 3.328-0.384 48.096 48.096 0 0 0 44.672-47.616c0-25.152-19.84-45.888-44.544-47.68a16 16 0 0 0-3.456-0.32h-30.88a16 16 0 0 0-1.12 0 15.616 15.616 0 0 1-16-16c0-9.152 6.88-16 16-16h64a16 16 0 1 0 0-32h-62.88a16 16 0 0 0-1.12 0 16 16 0 0 0-1.6 0zM592 832c26.688 0 48 21.312 48 48s-21.312 48-48 48-48-21.312-48-48 21.312-48 48-48z"
fill="#E9EDED" p-id="20519"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg t="1642407502942" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7293"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#A15FDE" p-id="7294"></path>
<path d="M685.511111 196.266667V0L967.111111 281.6H770.844444a85.333333 85.333333 0 0 1-85.333333-85.333333"
fill="#C386F0" p-id="7295"></path>
<path
d="M669.980444 426.268444v236.999112c0 26.254222-31.857778 47.587556-71.082666 47.587555-39.253333 0-70.741333-21.333333-70.741334-47.587555 0-26.282667 31.516444-47.587556 70.741334-47.587556 14.848 0 28.728889 3.100444 40.163555 8.334222v-165.916444l-205.767111 48.497778v211.057777c0 26.254222-32.142222 47.559111-71.992889 47.559111-39.850667 0-72.305778-21.333333-72.305777-47.559111 0-26.282667 32.426667-47.587556 72.305777-47.587555a96.711111 96.711111 0 0 1 41.102223 8.647111V474.168889c0-14.222222 9.870222-26.88 23.779555-29.980445l205.795556-47.900444a30.862222 30.862222 0 0 1 38.001777 29.980444"
fill="#FFFFFF" p-id="7296"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+2
View File
@@ -0,0 +1,2 @@
<?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="1627364345844" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15116" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_1uhr8ri0pk5.eot?#iefix") format("embedded-opentype"), url("//at.alicdn.com/t/font_1031158_1uhr8ri0pk5.woff2") format("woff2"), url("//at.alicdn.com/t/font_1031158_1uhr8ri0pk5.woff") format("woff"), url("//at.alicdn.com/t/font_1031158_1uhr8ri0pk5.ttf") format("truetype"), url("//at.alicdn.com/t/font_1031158_1uhr8ri0pk5.svg#iconfont") format("svg"); }
</style></defs><path d="M0 128a51.2 51.2 0 0 1 51.2-51.2h350.24a51.2 51.2 0 0 1 47.0592 31.0336L473.6 166.4h499.2a51.2 51.2 0 0 1 51.2 51.2v537.6a51.2 51.2 0 0 1-51.2 51.2H51.2a51.2 51.2 0 0 1-51.2-51.2V128z" fill="#FFA000" p-id="15117"></path><path d="M89.6 249.6m51.2 0l742.4 0q51.2 0 51.2 51.2l0 460.8q0 51.2-51.2 51.2l-742.4 0q-51.2 0-51.2-51.2l0-460.8q0-51.2 51.2-51.2Z" fill="#FFFFFF" p-id="15118"></path><path d="M0 332.8m51.2 0l921.6 0q51.2 0 51.2 51.2l0 512q0 51.2-51.2 51.2l-921.6 0q-51.2 0-51.2-51.2l0-512q0-51.2 51.2-51.2Z" fill="#FFCA28" p-id="15119"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg t="1642408119178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="53519" width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#BABABA" p-id="53520"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#979797" p-id="53521"></path>
<path
d="M733.667556 632.689778a111.104 111.104 0 0 1-110.819556 110.819555h-221.667556a111.132444 111.132444 0 0 1-110.848-110.819555 111.047111 111.047111 0 0 1 99.754667-110.279111A122.197333 122.197333 0 0 1 512 407.694222a122.197333 122.197333 0 0 1 121.912889 114.716445 111.160889 111.160889 0 0 1 99.754667 110.279111"
fill="#FFFFFF" p-id="53522"></path>
</svg>

After

Width:  |  Height:  |  Size: 992 B

+11
View File
@@ -0,0 +1,11 @@
<svg t="1642407248989" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5608"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.577778-51.2 113.777778-113.777778 113.777778H170.666667c-62.577778 0-113.777778-51.2-113.777778-113.777778V113.777778c0-62.577778 51.2-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#D23B41" p-id="5609"></path>
<path d="M685.511111 224.711111V0L967.111111 281.6H742.4c-31.288889 0-56.888889-25.6-56.888889-56.888889"
fill="#9C171C" p-id="5610"></path>
<path
d="M680.277333 662.698667c-11.889778-1.194667-23.751111-3.640889-35.640889-9.728 10.666667-2.133333 20.110222-2.133333 30.776889-2.133334 23.751111 0 28.330667 5.774222 28.330667 9.443556-6.997333 2.417778-15.246222 3.356444-23.466667 2.417778z m-120.945777-15.530667c-25.884444 5.802667-54.556444 14.336-80.440889 23.779556v-2.446223l-2.446223 1.223111c13.084444-26.197333 25.002667-53.333333 35.640889-80.753777l0.938667 1.223111 1.194667-2.133334c13.112889 20.110222 29.866667 40.220444 47.530666 57.884445h-3.640889l1.223112 1.223111zM497.777778 417.450667c1.223111-1.223111 3.669333-1.223111 4.551111-1.223111h3.697778a96.739556 96.739556 0 0 1-1.251556 61.553777c-8.220444-18.915556-11.861333-40.220444-6.997333-60.330666zM352.142222 770.275556l-3.669333 1.223111a96.768 96.768 0 0 1 42.666667-34.417778c-9.443556 15.502222-22.556444 27.392-38.997334 33.194667z m324.494222-155.107556c-25.002667 0-49.664 3.669333-74.666666 8.248889a353.365333 353.365333 0 0 1-73.415111-94.776889c20.110222-66.417778 21.333333-111.217778 5.774222-132.551111a39.253333 39.253333 0 0 0-30.748445-15.502222c-15.246222-1.223111-29.582222 6.087111-36.579555 18.887111-21.333333 35.640889 9.443556 105.415111 23.779555 134.058666-16.782222 50.887111-36.864 99.328-63.089777 146.858667-112.412444 48.440889-114.858667 77.994667-114.858667 88.661333 0 13.084444 7.310222 26.197333 20.110222 32 4.864 3.640889 11.889778 4.835556 17.976889 4.835556 29.582222 0 64-33.194667 100.551111-98.389333 46.307556-18.887111 92.615111-34.133333 141.084445-44.8a153.941333 153.941333 0 0 0 87.722666 35.356444c20.110222 0 59.107556 0 59.107556-40.220444 1.223111-15.530667-6.997333-41.443556-62.748445-42.666667z"
fill="#FFFFFF" p-id="5611"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+12
View File
@@ -0,0 +1,12 @@
<svg t="1642407349568" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6204"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.577778-51.2 113.777778-113.777778 113.777778H170.666667c-62.577778 0-113.777778-51.2-113.777778-113.777778V113.777778c0-62.577778 51.2-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#F16C41" p-id="6205"></path>
<path d="M685.511111 224.711111V0L967.111111 281.6H742.4c-31.288889 0-56.888889-25.6-56.888889-56.888889"
fill="#CD4B29" p-id="6206"></path>
<path
d="M525.880889 648.135111a88.32 88.32 0 0 1-68.750222-32.995555 87.04 87.04 0 0 1-19.626667-55.381334c0-21.048889 7.253333-40.248889 19.626667-55.381333a88.234667 88.234667 0 0 1 68.750222-32.995556 88.490667 88.490667 0 0 1 88.376889 88.376889 88.519111 88.519111 0 0 1-88.376889 88.376889m0-235.690667c-24.945778 0-48.327111 6.087111-68.750222 17.294223a143.075556 143.075556 0 0 0-58.88 56.945777v146.119112a143.132444 143.132444 0 0 0 58.88 56.974222c20.423111 11.178667 43.804444 17.265778 68.750222 17.265778a147.342222 147.342222 0 0 0 147.285333-147.285334 147.342222 147.342222 0 0 0-147.285333-147.342222"
fill="#FFFFFF" p-id="6207"></path>
<path d="M398.222222 824.888889h58.908445V412.444444H398.222222z" fill="#FFFFFF" p-id="6208"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+16
View File
@@ -0,0 +1,16 @@
<svg t="1642407407406" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6649"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#FFC63A" p-id="6650"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#DD9F08" p-id="6651"></path>
<path
d="M436.565333 68.437333h68.437334V0h-68.437334zM505.002667 136.874667h68.437333V68.437333h-68.437333zM436.565333 205.312h68.437334V136.874667h-68.437334zM505.002667 273.749333h68.437333V205.312h-68.437333z"
fill="#FFFFFF" p-id="6652"></path>
<path d="M436.565333 342.158222h68.437334V273.720889h-68.437334zM505.002667 410.624h68.437333V342.186667h-68.437333z"
fill="#FFFFFF" p-id="6653"></path>
<path
d="M436.565333 479.032889h68.437334v-68.437333h-68.437334zM505.002667 547.470222h68.437333v-68.437333h-68.437333zM470.784 762.225778h68.437333v-68.437334h-68.437333v68.437334z m-34.218667-136.874667v136.874667c0 18.915556 15.331556 34.218667 34.218667 34.218666h68.437333c18.915556 0 34.218667-15.303111 34.218667-34.218666v-136.874667h-136.874667z"
fill="#FFFFFF" p-id="6654"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg t="1642407315436" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5906"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#6D9FE5" p-id="5907"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#4B80CB" p-id="5908"></path>
<path
d="M344.177778 485.575111h312.888889V426.666667h-312.888889zM471.153778 770.019556h58.908444v-284.444445h-58.908444z"
fill="#FFFFFF" p-id="5909"></path>
</svg>

After

Width:  |  Height:  |  Size: 785 B

+14
View File
@@ -0,0 +1,14 @@
<svg t="1642407389455" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6501"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#C386F0" p-id="6502"></path>
<path
d="M284.444444 398.222222m42.666667 0l298.666667 0q42.666667 0 42.666666 42.666667l0 234.666667q0 42.666667-42.666666 42.666666l-298.666667 0q-42.666667 0-42.666667-42.666666l0-234.666667q0-42.666667 42.666667-42.666667Z"
fill="#FFFFFF" p-id="6503"></path>
<path
d="M738.417778 457.841778a31.971556 31.971556 0 0 1 48.014222 27.676444v154.538667c0 24.632889-26.652444 40.021333-47.985778 27.704889L684.430222 636.586667V488.96z"
fill="#FFFFFF" p-id="6504"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#A15FDE" p-id="6505"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg t="1642407280584" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5757"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.577778-51.2 113.777778-113.777778 113.777778H170.666667c-62.577778 0-113.777778-51.2-113.777778-113.777778V113.777778c0-62.577778 51.2-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#4F6BF6" p-id="5758"></path>
<path d="M581.262222 755.626667h59.363556L739.555556 439.04h-59.335112z" fill="#FFFFFF" p-id="5759"></path>
<path d="M685.511111 224.711111V0L967.111111 281.6H742.4c-31.288889 0-56.888889-25.6-56.888889-56.888889"
fill="#243EBB" p-id="5760"></path>
<path
d="M640.625778 755.626667h-59.363556l-98.929778-277.020445h59.335112zM442.737778 755.626667h-59.363556L284.444444 439.04h59.335112z"
fill="#FFFFFF" p-id="5761"></path>
<path d="M383.374222 755.626667h59.363556l98.929778-277.020445h-59.335112z" fill="#FFFFFF" p-id="5762"></path>
</svg>

After

Width:  |  Height:  |  Size: 995 B

+1
View File
@@ -0,0 +1 @@
<svg t="1642421944380" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="865" width="200" height="200"><path d="M97.9 376h828.4v269.2H97.9z" fill="#F95F5D" p-id="866"></path><path d="M926.3 376V161.5c0-26.6-23.8-50.3-52.1-50.3H149.9c-28.3 0-52.1 23.7-52.1 50.3V376h828.5z m0 0" fill="#55C7F7" p-id="867"></path><path d="M97.9 645.2v214.5c0 26.6 23.6 50.3 51.7 50.3h725c28.1 0 51.7-23.7 51.7-50.3V645.2H97.9z m0 0" fill="#7ECF3B" p-id="868"></path><path d="M421.8 111.2h184.9V910H421.8z" fill="#FDAF42" p-id="869"></path><path d="M606.7 457.4v112.4H413V457.4h193.7m31.1-45.9H381.9c-4.4 0-11.8 4.4-11.8 11.8v179c0 4.4 4.4 11.8 11.8 11.8h255.9c4.4 0 11.8-4.4 11.8-11.8v-179c-2.9-8.8-7.4-11.8-11.8-11.8z m0 0" fill="#FFFFFF" p-id="870"></path></svg>

After

Width:  |  Height:  |  Size: 787 B

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