init:初始化

master
HuangHuiKang 1 week ago
commit dc0b14f0f0

@ -0,0 +1,12 @@
.git
.gitignore
.husky
.github
node_modules
dist
coverage
.DS_Store
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*

@ -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 # 关闭末尾空格修剪

28
.env

@ -0,0 +1,28 @@
# 【通用】环境变量 - 所有环境共享
# 应用版本号
VITE_VERSION = 3.0.0
# 开发服务器端口
VITE_PORT = 5180
# 应用部署基础路径(如部署在子目录 /admin则设置为 /admin/
VITE_BASE_URL = /
# 权限模式【 frontend 仅前端路由 / backend 仅后端菜单 / mixed 后端菜单+前端路由模块合并 】
VITE_ACCESS_MODE = mixed
# 跨域请求时是否携带 Cookie开启前需确保后端支持
VITE_WITH_CREDENTIALS = false
# 是否在控制台输出路由信息
VITE_OPEN_ROUTE_INFO = false
# 锁屏加密密钥
VITE_LOCK_ENCRYPT_KEY = s3cur3k3y4adpro
# 网络请求超时时间(毫秒)
VITE_API_TIMEOUT = 120000
# 代理前缀
VITE_APP_BASE_API = /api/v1

@ -0,0 +1,21 @@
# 【开发】环境变量 - 覆盖通用配置
# 环境标识
VITE_APP_ENV = development
# 项目名称
VITE_APP_TITLE = FastapiAdmin
# 浏览器侧同源前缀(与 Vite 代理配合时用 /,请求走 localhost:端口 + 代理)
VITE_API_URL = /
# 代理目标:本机后端(若出现 ENOTFOUND说明上面域名在当前网络/DNS 下不可解析,改用本地或可用地址)
VITE_API_BASE_URL = http://127.0.0.1:8001
# VITE_API_BASE_URL = https://service.fastapiadmin.com
# 是否删除控制台输出
VITE_DROP_CONSOLE = false
# WebSocket 端点AI对话功能需要配置
VITE_APP_WS_ENDPOINT = ws://127.0.0.1:8001
# VITE_APP_WS_ENDPOINT = wss://service.fastapiadmin.com

@ -0,0 +1,36 @@
# 复制为 .env 并根据本地情况修改;勿提交真实密钥到公开仓库。
# Vite 会加载:.env、.env.[mode](如 .env.development后者覆盖前者同名变量。
# ---------- 通用(建议复制到 .env----------
VITE_VERSION=3.0.0
VITE_PORT=5180
VITE_BASE_URL=/
# frontend | backend | mixed —— 菜单与路由来源(须与后端约定一致)
VITE_ACCESS_MODE=mixed
VITE_WITH_CREDENTIALS=false
VITE_OPEN_ROUTE_INFO=false
VITE_LOCK_ENCRYPT_KEY=please-change-me
VITE_API_TIMEOUT=60000
VITE_APP_BASE_API=/api/v1
# ---------- 开发(复制到 .env.development----------
VITE_APP_ENV=development
VITE_APP_TITLE=FastapiAdmin
VITE_API_URL=/
# 本地后端;开发时代理会把 /api/v1 转到此处
VITE_API_BASE_URL=http://127.0.0.1:8001
VITE_DROP_CONSOLE=false
VITE_APP_WS_ENDPOINT=ws://127.0.0.1:8001
# ---------- 生产(复制到 .env.production---------
VITE_APP_ENV=production
VITE_APP_TITLE=FastapiAdmin
VITE_APP_BASE_API=/api/v1
VITE_API_BASE_URL=http://fastapiadmin-backend:8001
VITE_DROP_CONSOLE=true
# 留空则使用当前前端域名,例如 https://admin.example.com -> wss://admin.example.com
VITE_APP_WS_ENDPOINT=

@ -0,0 +1,19 @@
# 【生产】环境变量 - 覆盖通用配置
# 环境标识
VITE_APP_ENV = production
# 项目名称
VITE_APP_TITLE = FastapiAdmin
# API 请求基础路径(前端 Nginx 默认反向代理 /api/v1 到后端)
VITE_APP_BASE_API = /api/v1
# Vite 开发代理目标;生产构建不直接使用,保留给 preview/调试场景
VITE_API_BASE_URL = http://fastapiadmin-backend:8001
# 是否删除控制台输出
VITE_DROP_CONSOLE = true
# WebSocket 端点(留空则自动使用当前前端域名,配合 Nginx 代理 /api/v1
VITE_APP_WS_ENDPOINT =

2
.gitattributes vendored

@ -0,0 +1,2 @@
*.html linguist-detectable=false
*.vue linguist-detectable=true

26
.gitignore vendored

@ -0,0 +1,26 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.cursorrules
.history
# Auto-generated files
src/types/import/auto-imports.d.ts
src/types/import/components.d.ts
.auto-import.json
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.local
stats.html
.stylelintcache
.eslintcache
# docs and app directories are at frontend root, managed by frontend/.gitignore

@ -0,0 +1 @@
pnpm dlx commitlint --edit $1

@ -0,0 +1 @@
pnpm run lint:lint-staged

@ -0,0 +1,2 @@
# 抑制废弃子依赖警告fstream、lodash.isequal 等深层依赖已无新版可升级)
deprecated-warning=false

@ -0,0 +1,11 @@
node_modules/
dist/
public/
.vscode/
src/types/import/
.auto-import.json
pnpm-lock.yaml
package-lock.json
.stylelintcache
.eslintcache
stats.html

@ -0,0 +1,41 @@
# 单参数箭头函数始终添加括号
arrowParens: "always"
# JSX 多行元素的闭合标签另起一行
bracketSameLine: false
# 对象字面量括号之间添加空格 { foo: bar }
bracketSpacing: true
# 自动格式化嵌入的代码Markdown/HTML 内的代码块)
embeddedLanguageFormatting: "auto"
# HTML 空白敏感度CSS 模式保留 display 相关空白
htmlWhitespaceSensitivity: "css"
# 不插入 @prettier 的 pragma 注释
insertPragma: false
# JSX 中使用双引号
jsxSingleQuote: false
# 每行最大长度
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
# 换行符自动检测LF / CRLF
endOfLine: "auto"
# HTML 文件使用专用解析器
overrides:
- files: "*.html"
options:
parser: "html"

@ -0,0 +1,11 @@
dist
node_modules
public
.husky
.vscode
.idea
*.sh
*.md
src/assets
stats.html

@ -0,0 +1,77 @@
module.exports = {
extends: [
"stylelint-config-standard",
"stylelint-config-recommended-scss",
"stylelint-config-recess-order",
],
overrides: [
{
files: ["**/*.{vue,html}"],
customSyntax: "postcss-html",
},
{
files: ["**/*.{css,scss}"],
customSyntax: "postcss-scss",
},
],
rules: {
"import-notation": "string",
"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,
"at-rule-empty-line-before": null,
// 允许 placeholder mixin 中必要的浏览器前缀(:-ms-input-placeholder、::-webkit-input-placeholder
"selector-no-vendor-prefix": [
true,
{
ignoreSelectors: ["::-webkit-input-placeholder", ":-ms-input-placeholder"],
},
],
// 允许 Vue SFC :deep() / ::deep / :global() / :slotted() 伪选择器
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["global", "export", "deep", "slotted"],
},
],
"selector-pseudo-element-no-unknown": [
true,
{
ignorePseudoElements: ["deep"],
},
],
// 允许 SCSS + Tailwind CSS v4 专属 at-rules
"at-rule-no-unknown": null,
// 允许注释前无空行section 分隔注释紧凑排版更清晰)
"comment-empty-line-before": null,
"scss/at-rule-no-unknown": [
true,
{
ignoreAtRules: [
"apply",
"use",
"mixin",
"include",
"extend",
"each",
"if",
"else",
"for",
"while",
"reference",
"tailwind",
"theme",
"utility",
"source",
"custom-variant",
"layer",
"config",
"plugin",
],
},
],
},
};

@ -0,0 +1,650 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Released]
### v3.0.1 - 2025-11-15
#### Fixed
- Fixed repeated API calls issue when accessing non-existent interfaces during route registration
- Fixed one-click cleanup script build failure issue
#### Optimized
- Enhanced route configuration validation mechanism, automatically detects and prompts path configuration errors for non-first-level menus
- Optimized top progress bar ghosting issue
- Optimized vite pre-build configuration
- Unified border radius and border consistency
- Redesigned lock screen page, improved visual experience
- Optimized visual experience issue caused by logout menu disappearing prematurely
#### Added
- FaForm and FaSearchBar component labels support custom render functions, enabling complex labels with tooltips
- Menu management form key fields added Tooltip hints, lowering user configuration threshold
- Iconify added offline icon loading mode
- Logout added redirect property for redirecting to corresponding page after re-login
- Logout and re-entry preserves user tabs, identified by userId
- Dual menu added collapse button
- Enhanced menu icons
- Multi-tab pages added icon display
- Added WebSocket connection example
- Lock screen password error triggers input box shake animation
#### ⚠️ Important Notice
> This version requires re-login
---
### v3.0.0 - 2025-11-09 🎉
#### Refactored
- Style system refactored: Sass fully migrated to Tailwind CSS, improving development efficiency and style consistency
- Icon solution upgraded: Iconfont replaced with Iconify, supporting richer icon libraries and on-demand loading
- Route registration refactored: Comprehensive refactoring of route registration system with object-oriented design, improving code maintainability, testability and extensibility
- Refactored ArtTextScroll component, improving performance and maintainability
- Refactored color system, unified UI visual specifications, improving interface consistency
#### Optimized
- Build optimization: Complete package size reduced by 1.3 MB, significantly improving loading performance
- Architecture optimization: Optimized directory structure with clearer responsibility division, reducing user learning curve and onboarding difficulty
- Comment optimization: Unified module comment standards, improved component descriptions, feature explanations and usage examples, reducing user understanding cost and onboarding difficulty
- Performance improvement: Optimized core code logic, improving system runtime efficiency
- Menu optimization: Refined menu styles, optimized interaction experience and visual presentation
- Responsive optimization: Optimized FaForm and FaSearchBar grid layout, adapting to various screen sizes
- Configuration management optimization: Added setting.ts configuration file, supporting one-click copy and reset of system default settings
#### Fixed
- Fixed FaForm and FaSearchBar custom component rendering issues
#### Added
- FaForm and FaSearchBar added render property, supporting custom component rendering
- Festival feature enhancement: Fireworks configuration supports cross-date range settings and custom playback counts
#### Upgraded
- Dependency updates: Upgraded core dependencies to latest stable versions
#### ⚠️ Important Notice
> This upgrade involves fundamental refactoring of the style system (Sass → Tailwind CSS) and icon library (Iconfont → Iconify), which is a breaking change. It is recommended to use v3.0 for new projects. Upgrading existing projects is not recommended.
---
### v2.6.1 - 2025-10-19
#### Fixed
- Fixed repeated API calls issue when accessing invalid addresses for user info and menu endpoints
- Fixed ElButton circle mode styles
- Fixed ElSelect keyboard selection issue
- Fixed static routes with parameters redirecting to login page issue
#### Optimized
- Upgraded some dependencies to be compatible with tailwindcss
- Optimized external link menu click selection state
- Added theme color switching feature to authorization pages
---
### v2.6.0 - 2025-10-16
#### Added
- Extended registration and password reset page top component support
#### Optimized
- Optimized streamlined version menu data structure, improved data consistency
- Optimized local development environment network request proxy configuration
- Optimized ElTree component default styles
- Added VsCode recommended plugin related configuration
- Optimized ElDropdown component click trigger mode interaction styles
- Optimized menu filtering logic
- Optimized page switching animation, improved loading speed
- Optimized dark mode text color
#### Fixed
- Fixed static route custom home page path first visit redirecting to login page issue
- Fixed logout briefly jumping to 500 page issue
- Fixed v2.5.9 version home route redirect configuration failure issue
- Fixed v2.5.9 auto-import mechanism causing build exception
#### ⚠️ Important Notice
> This version requires re-login
---
### v2.5.9 - 2025-10-12
#### Code Optimized
- Optimized views file directory, file names, and code
- Added global configuration for useTable pagination request fields (tableConfig.ts)
- Optimized routing configuration to modular structure
- Menu API now uses apifox mock data (set VITE_ACCESS_MODE to backend mode in .env)
---
### v2.5.8 - 2025-09-29
#### Upgraded
- Upgraded core libraries including vue, vite, element-plus
#### Fixed
- Fixed rich text editor fullscreen top bar z-index issue
- Fixed table column sorting component text overflow issue
- Fixed statistics card condition judgment
- Fixed infinite redirect when root path / and HOME_PAGE_PATH are both /
#### Optimized
- Optimized el-tag styles
- Optimized top progress bar color
- Optimized custom theme configuration
- Optimized ElementPlus custom theme issues
#### ⚠️ Important Notice
> Due to dependency upgrades, Node version needs to be upgraded to v20.19.0 or above
---
### v2.5.7 - 2025-09-14
#### Added
- Added FaForm component
#### Fixed
- Fixed theme switching flicker issue in new Chrome versions
#### Optimized
- Optimized form label height alignment issue
- First screen startup performance optimization
---
### v2.5.6 - 2025-09-06
#### Added
- FaTableHeader added search button to control top search bar visibility
- Added permission demonstration examples
- Added global error handling framework
#### Optimized
- useTable type inference optimization, no need to manually pass types for type hints
- useTable removeColumn supports multiple data deletion
- useTable auto-recognizes response body with custom configuration (src/utils/table/tableConfig.ts)
- useTable empty data browser warning optimization
- API request code optimization, api.d.ts type optimization
- Optimized FaTable top button wrapping adaptive issue
- FaTable pagination component selection style optimization
- FaTable empty state height fills by default
- ArtButtonMore component added icon and color configuration
- FaSearchBar label doesn't occupy space when empty
- Table operation column drag prohibits fixed column dragging
- Role management page API integration and code optimization
- Menu management page optimization
- Optimized settings center scroll following issue
- First-level route external link component validation logic optimization
- Optimized map bottom-right drag issue
- Optimized dark mode page refresh white background issue
- Optimized left menu collapse button spacing issue
- Mobile displays left menu logo
- Network request added showSuccessMessage to configure success message display
- Global components use async loading strategy to improve first screen loading performance
#### Fixed
- Fixed bug where batch deleting entire page data doesn't return to previous page
- Fixed dynamic route parameter issues
- Fixed dynamic route configuration first-level route iframe page fullscreen issue
---
### v2.5.5 - 2025-08-17
#### Added
- Added FaSearchBar component examples
- useTable added excludeParams to exclude certain parameters from requests
#### Refactored
- Refactored FaSearchBar component, supports more components and form validation
#### Optimized
- useTable column configuration: supports dynamic update capability
- Optimized color picker border radius
- Unified el-radio and el-checkbox sizes
- art-stats-card added decimal places and separator configuration
- Route configuration example optimization
- Advanced table added custom data fetching example
- Optimized path alias type issues
- Local development CORS configuration optimization
- useTable property and method naming optimization
- Login page UI upgrade
- 403, 404, 500 page UI upgrade
#### Fixed
- Fixed multiple rich text editor icon inconsistency issue
- Fixed useTable deleting last entire page data not returning to previous page issue
- Fixed echarts chart data initialization and update browser error
- Network request supports logout on HTTP status code 401
- Optimized network request logout multiple prompt issue
#### Removed
- Removed art-chart-empty component
---
### v2.5.4 - 2025-07-27
#### Added
- Network request headers support custom configuration
- Expand row supports formatter rendering
#### Optimized
- Route registration added component validation
#### Fixed
- Fixed user info API timing issue causing route registration menu rendering error
- Fixed dynamic route validation issue causing iframe not to display
- Fixed reset file syntax error
- Fixed FaTable data type error
- Fixed map scroll wheel zoom issue
---
### v2.5.3 - 2025-07-20
#### Added
- Added table left-right layout example
#### Refactored
- FaTable component refactored
#### Upgraded
- Element Plus upgraded to v2.10.2
#### Optimized
- Optimized useTable pagination parameter issue
- Optimized network request example: initialization parameters, pagination carrying parameters
- Optimized search date range parameter handling
- Optimized el-date-picker component border radius
- Optimized el-select component hover style
- Search component and pagination component height reduced
- Optimized login page slider animation interval
- Optimized menu without submenu display issue
#### Fixed
- Fixed FaTable switching page size executing two requests bug
---
### v2.5.2 - 2025-07-13
#### Added
- Added one-click cleanup script for quick development environment preparation
- useTable hooks support custom pagination field name mapping
- Horizontal menu, mixed menu, double column menu support badge display
- Quick entry supports configuration file mode
- Top bar function supports configuration file mode
- Support custom home page path
- Route supports redirect and other attributes configuration
#### Optimized
- Global event bus mittBus type safety optimization
- Optimized mobile settings container width style
- Optimized login page verification slider text centering effect
#### Fixed
- Fixed table no data header not displaying issue
- Fixed v2.5.0 top progress bar not displaying issue
- Fixed left menu mask abnormal display issue
- Fixed hiding all submenus still showing parent menu issue
- Fixed stylelint causing login page slider style abnormality
- Fixed old mobile device loading positioning issue
---
### v2.5.1 - 2025-07-08
#### Optimized
- el-card and el-table background color consistent with system
#### Fixed
- Fixed first login system loading closing early bug
- Fixed v2.5.0 version causing fullscreen page style z-index too low bug
- Fixed v2.5.0 version causing table expand row collapse bug
---
### v2.5.0 - 2025-07-06 🎉
#### Added
- Added useTable hooks table encapsulation, supports data fetching, transformation, response adaptation, intelligent caching (based on LRU algorithm), error handling, column configuration and slots, pagination control, refresh strategies and other core functions, comprehensively improving development efficiency and user experience
#### Refactored
- Refactored FaTable, FaTableHeader, ArtNotification components
#### Upgraded
- Echarts version upgraded to 5.6.0
#### Removed
- Removed CountTo plugin, replaced with FaCountTo component
#### Fixed
- Fixed menu management search directly modifying pinia data issue
- Fixed route guard loading flicker issue
#### ⚠️ Important Notice
> Recommended upgrade for more efficient and intelligent table development experience
---
### v2.4.2.9 - 2025-07-02
#### Refactored
- Menu layout and top navigation code refactored
#### Optimized
- Optimized mobile menu scroll user experience
- Optimized top menu style issues
- Top menu width adaptive, can display more content, mixed menu supports mouse scroll
- asyncRoutes route configuration auth_mark field changed to authMark
- Removed duplicate components.d.ts file, components.d.ts and auto-imports.d.ts ignored from commits
- Optimized i18n language file loading method, changed from async to sync mode
- Optimized el-pagination size inconsistency issue
#### Fixed
- Fixed mobile lock screen page some browsers unable to unlock bug
---
### v2.4.2.8 - 2025-06-26
#### Fixed
- Fixed v2.4.2.7 version accessing / path showing 404 issue
---
### v2.4.2.7 - 2025-06-25
#### Added
- Route supports fullscreen mode configuration
- Route supports auto-jump to first valid route of menu
- Dynamic route added removeAllDynamicRoutes method for completely clearing all dynamic routes
- Permission custom directive optimization, added role permission directive v-roles for controlling element visibility
- Added tab operation examples
#### Optimized
- CORS request carrying cookie configuration from environment variables, disabled by default
- Some optimizations for SEO and accessibility
#### Fixed
- Fixed login page drag component FaDragVerify width and color abnormal bug
- Fixed iframe page mixed mode and double column mode abnormal bug
- Optimized lock screen page being penetrated by el-loading bug
---
### v2.4.2.6 - 2025-06-23
#### Refactored
- Refactored form-related components in components/core/forms folder, improved maintainability and consistency
- Refactored ArtBreadcrumb breadcrumb navigation component, optimized logic structure and styles
- Refactored ArtFireworksEffect fireworks effect component, significantly improved rendering performance and animation smoothness
#### Optimized
- Optimized ArtChatWindow and ArtFastEnter component code, improved readability and performance
#### Added
- README documentation added official website link for easy access to project documentation
---
### v2.4.2.5 - 2025-06-22
#### Refactored
- Refactored chart components, optimized code structure and maintainability
- Fine-tuned chart animations and theme color schemes, improved visual consistency
---
### v2.4.2.4 - 2025-06-18
#### Refactored
- FaMenuRight component refactored
- Components under components/core/cards refactored, code optimized
#### Optimized
- ArtWatermark added type annotations
---
### v2.4.2.3 - 2025-06-18
#### Refactored
- FaResultPage component refactored
#### Optimized
- ArtTextScroll component code optimization
- FaException component added type hints
- FaCutterImg component style optimization, added type definitions
- FaVideoPlayer component added type definitions
---
### v2.4.2.2 - 2025-06-16
#### Refactored
- Back to top component refactored
- Icon selector component refactored
#### Changed
- System Logo component property changes
---
### v2.4.2.1 - 2025-06-16
#### Refactored
- Banner component refactored and optimized
#### Fixed
- Fixed mixed menu first menu nested menu jump bug
---
### v2.4.2 - 2025-06-14
#### Refactored
- Refactored network request module, enhanced error handling, type safety and multi-language support
#### Added
- Route configuration added activePath active menu path property
#### Optimized
- Guide and column settings multi-language improvement
- Removed invalid code from user list and menu management pages
- Updated technical support link
#### Fixed
- Fixed mobile search bar unable to scroll, iPad page scroll abnormal issue
- Fixed el-dialog enabling draggable property causing custom animation failure issue
- Fixed v2.3.0 local storage refactoring causing login, registration page multi-language settings unable to persist issue
- Fixed table fixed column not working bug
- Fixed infinite redirect when root path / and HOME_PAGE_PATH are both /
#### ⚠️ Important Notice
> This version requires re-login
---
### v2.4.1.1 - 2025-06-07
#### Optimized
- Optimized role management page code
- el-dialog visual effect optimization, supports line configuration
- System theme mode changed from Light to follow system mode
#### Fixed
- Fixed menu management collapse bug
- Fixed table empty data height infinitely growing bug
---
### v2.4.1 - 2025-06-07
#### Added
- Prohibit using developer tools to crack lock screen when locked
#### Optimized
- Improved menu operation responsiveness
- Page entrance animation time reduced by 0.04s
- Echarts chart performance optimization, added visible area initialization, memory leak protection, debounce handling
#### Fixed
- Fixed Echarts chart component not displaying in dialog bug
---
### v2.4.0 - 2025-06-06 🎉
#### Refactored
- Global TypeScript type system refactored, improved type accuracy and maintainability
- Refactored utils toolkit, unified utility method structure, enhanced readability and reusability
#### Added
- utils added form validation and Cookie operation related utility functions
- Page components added defineOptions for explicit component naming
- HTTP request added token expiration auto-handling logic, improved security and user experience
#### Optimized
- Deleted unused utility modules and invalid resources, streamlined project size
- Optimized views page structure, removed redundant page files
- Exception page multi-language support, improved internationalization experience
- Image resources uniformly converted to webp format, overall resource size reduced by about 50%
- Build output reduced by about 1MB, improved loading efficiency
#### ⚠️ Important Notice
> This version requires re-login
---
## Version Guidelines
### Version Number Rules
This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html):
- **Major**: Incompatible API changes
- **Minor**: Backwards-compatible functionality additions
- **Patch**: Backwards-compatible bug fixes
### Change Types
- **Added**: New features
- **Changed**: Changes to existing functionality
- **Deprecated**: Soon-to-be removed features
- **Removed**: Removed features
- **Fixed**: Bug fixes
- **Security**: Security-related fixes
- **Optimized**: Performance or experience optimizations
- **Refactored**: Code refactoring
- **Upgraded**: Dependency upgrades
### Important Notice Markers
- 🎉 **Major Update**: Versions with important new features
- ⚠️ **Breaking Changes**: Updates requiring special attention for incompatibility
- 🔒 **Security Update**: Versions fixing security vulnerabilities
---
## Contributing
If you want to contribute to the project, please check [CONTRIBUTING.md](./CONTRIBUTING.md).
## License
[MIT License](./LICENSE)
---
**Note**: This changelog starts recording from version v2.4.0. For earlier version history, please check Git commit records.

@ -0,0 +1,654 @@
# 更新日志
所有重要的项目变更都将记录在此文件中。
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
## [已发布]
### v3.0.1 - 2025-11-15
#### 修复
- 修复路由注册时不存在接口的重复请求问题
- 修复一键精简脚本打包失败的问题
#### 优化
- 完善路由配置验证机制,自动检测并提示非一级菜单的路径配置错误
- 优化顶部进度条残影问题
- 优化 vite 预构建配置
- 优化圆角、边框统一性
- 重新设计锁屏页面,提升视觉体验
- 优化退出登录菜单提前消失造成的视觉体验不好问题
#### 新增
- FaForm 和 FaSearchBar 组件的 label 支持自定义渲染函数,可实现带 tooltip 等复杂标签
- 菜单管理表单关键字段新增 Tooltip 提示,降低用户配置门槛
- iconify 新增离线图标加载模式
- 退出登录新增 redirect 属性,用于重新登录后跳转到对应页面
- 退出登录重新进入系统保留用户标签页,根据 userId 进行身份识别
- 双列菜单新增折叠按钮
- 完善菜单图标
- 多标签页增加图标显示
- 新增 WebSocket 连接示例
- 锁屏密码错误时增加输入框抖动动效
#### ⚠️ 重要提示
> 此版本需要重新登录
---
### v3.0.0 - 2025-11-09 🎉
#### 重构
- 样式系统重构Sass 全面迁移至 Tailwind CSS提升开发效率与样式一致性
- 图标方案升级Iconfont 替换为 Iconify支持更丰富的图标库与按需加载
- 路由注册重构:全面重构路由注册系统,引入面向对象设计,提高代码的可维护性、可测试性和扩展能力
- 重构 ArtTextScroll 组件,提升性能与可维护性
- 重构颜色体系,统一 UI 视觉规范,提升界面一致性
#### 优化
- 构建优化:完整包体积减少 1.3 MB显著提升加载性能
- 架构优化:优化目录结构,职责划分更清晰,降低用户学习成本与上手难度
- 注释优化:统一模块注释规范,完善每一个组件介绍、功能说明与使用示例,降低用户理解成本与上手难度
- 性能提升:优化核心代码逻辑,提升系统运行效率
- 菜单优化:细化菜单样式,优化交互体验与视觉呈现
- 响应式优化:优化 FaForm、FaSearchBar 栅格布局,适配多种屏幕尺寸
- 配置管理优化:新增 setting.ts 配置文件,支持一键复制与重置系统默认设置
#### 修复
- 修复 FaForm、FaSearchBar 自定义组件渲染异常
#### 新增
- FaForm、FaSearchBar 新增 render 属性,支持自定义组件渲染
- 节日功能增强:礼花配置支持跨日期范围设置与自定义播放次数
#### 升级
- 依赖更新:升级核心依赖至最新稳定版本
#### ⚠️ 重要提示
> 本次升级涉及样式系统Sass → Tailwind CSS与图标库Iconfont → Iconify的底层重构属于破坏性更新。建议新项目直接使用 v3.0,旧版本项目不建议升级。
---
### v2.6.1 - 2025-10-19
#### 修复
- 修复获取用户信息、获取菜单接口访问无效地址重复调用问题
- 修复 ElButton circle 模式样式
- 修复 ElSelect 无法通过键盘选择问题
- 修复带参数静态路由跳转登录页面问题
#### 优化
- 升级部分依赖兼容 tailwindcss
- 优化外部链接菜单点击选中状态
- 授权页增加主题色切换功能
---
### v2.6.0 - 2025-10-16
#### 新增
- 扩展注册、密码重置页面顶部组件支持
#### 优化
- 优化精简版本菜单数据结构,提升数据一致性
- 优化本地开发环境网络请求代理配置
- 优化 ElTree 组件默认样式
- 新增 VsCode 推荐插件相关配置
- 优化 ElDropdown 组件点击触发模式下的交互样式
- 优化菜单过滤逻辑
- 优化页面切换动画,提升加载速度
- 优化暗黑模式文字颜色
#### 修复
- 修复静态路由自定义首页路径首次访问跳转登录页问题
- 修复退出登录时短暂跳转至 500 页的问题
- 修复 v2.5.9 版本首页路由跳转配置失效问题
- 修复 v2.5.9 自动导包机制导致的构建异常
#### ⚠️ 重要提示
> 此版本需要重新登录
---
### v2.5.9 - 2025-10-12
#### 代码优化
- views 文件目录、文件名、代码优化
- useTable 分页请求字段增加全局配置 tableConfig.ts
- 优化路由配置为模块化结构
- 获取菜单接口使用 apifox mock 数据(需在 .env 中将 VITE_ACCESS_MODE 设为 backend 模式)
---
### v2.5.8 - 2025-09-29
#### 升级
- vue、vite、element-plus 等核心库升级
#### 修复
- 修复富文本编辑器全屏顶栏层级问题
- 修复表格列排序组件文字溢出问题
- 修复统计卡片条件判断
- 修复根路径 / 与 HOME_PAGE_PATH 同为 / 时出现的无限重定向
#### 优化
- 优化 el-tag 样式
- 优化顶部进度条颜色
- 优化自定义主题配置
- 优化 ElementPlus 自定义主题问题
#### ⚠️ 重要提示
> 由于项目依赖升级node 版本需要升级到 v20.19.0 或以上
---
### v2.5.7 - 2025-09-14
#### 新增
- 新增 FaForm 组件
#### 修复
- 修复新版本谷歌浏览器切换主题闪烁问题
#### 优化
- 优化表单 label 高度没有对齐问题
- 首屏启动性能优化
---
### v2.5.6 - 2025-09-06
#### 新增
- FaTableHeader 新增搜索按钮,用于控制顶部搜索栏的显示与隐藏
- 新增权限演示示例
- 添加全局错误处理基础框架
#### 优化
- useTable 类型推导优化,不需要手动传递类型即可实现类型提示
- useTable removeColumn 支持多数据删除
- useTable 自动识别响应体支持自定义配置 (src/utils/table/tableConfig.ts)
- useTable 空数据浏览器警告优化
- api 接口请求代码优化、api.d.ts 类型优化
- 优化 FaTable 顶部按钮换行无法自适应问题(示例:功能示例 / 左右布局表格)
- FaTable 分页组件选中样式优化
- FaTable 空状态高度默认撑满
- ArtButtonMore 组件新增图标、颜色配置
- FaSearchBar label 为空时不占空间
- 表格操作栏拖拽禁止固定列拖拽
- 角色管理页面接口对接、代码优化
- 菜单管理页面优化
- 优化设置中心滚动页面跟随滚动问题
- 一级路由是外链时component 校验逻辑优化
- 优化地图右下角拖动问题
- 优化暗黑模式刷新页面白色背景问题
- 优化左侧菜单折叠按钮间距问题
- 移动端显示左侧菜单 logo
- 网络请求新增 showSuccessMessage用于配置是否显示成功消息
- 全局组件采用异步加载策略,提升首屏加载性能
#### 修复
- 修复批量删除整页数据没有返回上一页的bug
- 修复动态路由参数导致的问题
- 修复动态路由配置一级路由是 iframe 页面时,全屏问题
---
### v2.5.5 - 2025-08-17
#### 新增
- 新增 FaSearchBar 组件示例
- useTable 新增 excludeParams用于排除某些参数不参与请求
#### 重构
- 重构 FaSearchBar 组件,支持更多组件、表单校验等能力
#### 优化
- useTable 列配置:支持动态更新能力
- 优化颜色选择器圆角
- el-radio、el-checkbox 统一大小
- art-stats-card 新增小数位、分隔符配置
- 路由配置示例优化
- 高级表格新增自定义获取数据示例(等待其他请求完成后执行 useTable 数据获取)
- 优化路径别名类型问题
- 本地开发跨域配置优化
- useTable 属性、方法命名优化
- 登录页UI升级
- 403、404、500 页面UI升级
#### 修复
- 修复多个富文本编辑器图标不统一问题
- 修复 useTable 删除最后一整页数据没有返回上一页的问题
- 修复 echarts 图表数据初始化、更新数据浏览器报错
- 网络请求支持 http 状态码为 401 时退出登录
- 优化网络请求退出登录多次提示问题
#### 移除
- 删除 art-chart-empty 组件
---
### v2.5.4 - 2025-07-27
#### 新增
- 网络请求 headers 支持自定义配置
- 展开行支持 formatter 渲染
#### 优化
- 路由注册新增 component 校验
#### 修复
- 修复获取用户信息接口时序问题导致路由注册菜单渲染错误bug
- 修复动态路由校验问题导致的 iframe 不显示bug
- 修复 reset 文件语法错误
- 修复 FaTable 数据类型错误
- 修复地图滚轮滚动放大问题
---
### v2.5.3 - 2025-07-20
#### 新增
- 新增表格左右布局示例
#### 重构
- FaTable 组件重构
#### 升级
- Element Plus 升级到 v2.10.2
#### 优化
- 优化 useTable 分页参数问题
- 优化网络请求示例:初始化参数、分页携带参数问题
- 优化搜索日期范围参数处理
- 优化 el-date-picker 组件圆角问题
- 优化 el-select 组件 hover 样式
- 搜索组件、分页组件高度降低
- 优化登录页面滑块动画间隔时长
- 优化菜单没有子菜单显示的问题
#### 修复
- 修复 FaTable 切换分页大小时执行两次请求bug
---
### v2.5.2 - 2025-07-13
#### 新增
- 新增一键精简脚本,快速准备开发环境
- useTable hooks 支持分页字段名自定义映射
- 水平菜单、混合菜单、双列菜单支持徽章显示
- 快速入口支持配置文件模式
- 顶栏功能支持配置文件模式
- 支持自定义首页路径
- 路由支持配置 redirect 等属性
#### 优化
- 全局事件总线 mittBus 类型安全优化
- 优化移动端设置中容器宽度样式
- 优化登录页验证滑块文字居中效果
#### 修复
- 修复表格无数据时表头不显示问题
- 修复 v2.5.0 顶部进度条不显示问题
- 修复左侧菜单遮罩异常显示问题
- 修复隐藏所有子菜单时仍显示父级菜单的问题
- 修复 stylelint 导致的登录页滑块样式异常
- 修复老旧移动端设备 loading 定位问题
---
### v2.5.1 - 2025-07-08
#### 优化
- el-card、el-table 背景色跟系统保持一致
#### 修复
- 修复首次登录系统时 loading 提前关闭bug
- 修复 v2.5.0 版本引起的全屏页样式层级过低bug
- 修复 v2.5.0 版本引起的表格展开行折叠bug
---
### v2.5.0 - 2025-07-06 🎉
#### 新增
- 新增 useTable hooks 表格封装,支持数据获取、转换、响应适配、智能缓存(基于 LRU 算法)、错误处理、列配置与插槽、分页控制、刷新策略等核心功能,全面提升开发效率与用户体验
#### 重构
- 重构 FaTable、FaTableHeader、ArtNotification 组件
#### 升级
- Echarts 版本升级到 5.6.0
#### 移除
- 移除 CountTo 插件,替换为 FaCountTo 组件
#### 修复
- 修复菜单管理搜索直接修改 pinia 数据的问题
- 修复路由守卫 loading 闪烁问题
#### ⚠️ 重要提示
> 建议升级,带来更高效、更智能的表格开发体验
---
### v2.4.2.9 - 2025-07-02
#### 重构
- 菜单布局、顶部导航代码重构
#### 优化
- 优化移动端菜单滚动用户体验
- 优化顶部菜单样式问题
- 顶部菜单宽度自适应,可显示更多内容,混合菜单支持鼠标滚动
- asyncRoutes 路由配置 auth_mark 字段改为 authMark
- 去除重复的 components.d.ts 文件components.d.ts、auto-imports.d.ts 忽略提交
- 优化国际化语言文件加载方式,异步改成同步模式
- 优化 el-pagination 大小不一致问题
#### 修复
- 修复移动端锁屏页部分浏览器无法解锁bug
---
### v2.4.2.8 - 2025-06-26
#### 修复
- 修复 v2.4.2.7 版本访问 / 路径时显示 404 的问题
---
### v2.4.2.7 - 2025-06-25
#### 新增
- 路由支持配置全屏模式
- 路由支持自动跳转到菜单的第一个有效路由
- 动态路由新增 removeAllDynamicRoutes 方法,可用于彻底清除所有动态路由
- 权限自定义指令优化、新增角色权限指令 v-roles、可用于控制元素的显示与隐藏
- 新增标签页操作示例
#### 优化
- 跨域请求携带 cookie 配置从环境变量中获取,默认关闭
- 针对SEO、可访问性做一些优化
#### 修复
- 修复登录页面拖拽组件 FaDragVerify 宽度、颜色异常bug
- 修复 iframe 页面混合模式、双列模式异常bug
- 优化锁屏页面被 el-loading 穿透bug
---
### v2.4.2.6 - 2025-06-23
#### 重构
- 重构 components/core/forms 文件夹下的表单相关组件,提升可维护性与一致性
- 重构 ArtBreadcrumb 面包屑导航组件,优化逻辑结构与样式
- 重构 ArtFireworksEffect 烟花效果组件,显著提升渲染性能与动画流畅度
#### 优化
- 优化 ArtChatWindow 与 ArtFastEnter 组件代码,提升可读性与性能
#### 新增
- README 文档新增官方网站链接,便于用户查看项目文档
---
### v2.4.2.5 - 2025-06-22
#### 重构
- 重构图表组件,优化代码结构与可维护性
- 精细调整图表动画与主题配色方案,提升视觉一致性
---
### v2.4.2.4 - 2025-06-18
#### 重构
- FaMenuRight 组件重构
- components/core/cards 下面的组件重构,代码优化
#### 优化
- ArtWatermark 增加类型注释
---
### v2.4.2.3 - 2025-06-18
#### 重构
- FaResultPage 组件重构
#### 优化
- ArtTextScroll 组件代码优化
- FaException 组件增加类型提示
- FaCutterImg 组件样式优化、增加类型定义
- FaVideoPlayer 组件增加类型定义
---
### v2.4.2.2 - 2025-06-16
#### 重构
- 返回顶部组件重构
- 图标选择器组件重构
#### 修改
- 系统Logo组件属性变更
---
### v2.4.2.1 - 2025-06-16
#### 重构
- 横幅组件重构以及优化
#### 修复
- 修复混合菜单下第一个菜单是嵌套菜单跳转bug
---
### v2.4.2 - 2025-06-14
#### 重构
- 重构网络请求模块,增强错误处理、类型安全与多语言支持
#### 新增
- 路由配置新增 activePath 激活菜单路径属性
#### 优化
- 引导、列设置多语言完善
- 去除用户列表、菜单管理页面无效代码
- 更新技术支持链接
#### 修复
- 修复移动端搜索栏无法滚动、iPad端页面滚动异常问题
- 修复 el-dialog 启用 draggable 属性后,自定义动画失效的问题
- 修复 2.3.0 版本本地存储重构后,导致登录、注册等页面多语言设置无法持久化的问题
- 修复表格固定列不起作用bug
- 修复根路径 / 与 HOME_PAGE_PATH 同为 / 时出现的无限重定向
#### ⚠️ 重要提示
> 此版本需要重新登录
---
### v2.4.1.1 - 2025-06-07
#### 优化
- 优化角色管理页面代码
- el-dialog视觉效果优化支持配置线条
- 系统主题模式从Light改成跟随系统模式
#### 修复
- 修复菜单管理折叠 bug
- 修复表格数据为空高度无限变大bug
---
### v2.4.1 - 2025-06-07
#### 新增
- 锁屏状态下禁止使用开发者工具破解锁屏
#### 优化
- 提升菜单操作跟手感
- 页面入场动画时间减少0.04s
- Echarts 图表性能优化,新增可视区域初始化、内存泄漏防护、防抖处理
#### 修复
- 修复 Echarts 图表组件在弹窗中不显示的 bug
---
### v2.4.0 - 2025-06-06 🎉
#### 重构
- 全局 TypeScript 类型体系重构,提升类型准确性与可维护性
- 重构 utils 工具包,统一工具方法结构,增强可读性与复用性
#### 新增
- utils 新增表单验证与 Cookie 操作相关工具函数
- 页面组件增加 defineOptions明确组件命名
- HTTP 请求增加 token 过期自动处理逻辑,提升安全性与用户体验
#### 优化
- 删除未使用的工具模块与无效资源,精简项目体积
- 优化 views 页面结构,移除冗余页面文件
- 异常页面多语言支持, 提升国际化体验
- 图片资源统一转换为 webp 格式,整体资源体积减少约 50%
- 打包产物减少约 1MB提高加载效率
#### ⚠️ 重要提示
> 此版本需要重新登录
---
## 版本说明
### 版本号规则
本项目遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/) 规范:
- **主版本号Major**:当你做了不兼容的 API 修改
- **次版本号Minor**:当你做了向下兼容的功能性新增
- **修订号Patch**:当你做了向下兼容的问题修正
### 变更类型
- **新增Added**:新功能
- **修改Changed**:对现有功能的变更
- **弃用Deprecated**:即将移除的功能
- **移除Removed**:已移除的功能
- **修复Fixed**Bug 修复
- **安全Security**:安全相关的修复
- **优化Optimized**:性能优化或体验优化
- **重构Refactored**:代码重构
- **升级Upgraded**:依赖升级
### 重要提示标记
- 🎉 **重大更新**:包含重要新特性的版本
- ⚠️ **破坏性变更**:需要特别注意的不兼容更新
- 🔒 **安全更新**:修复安全漏洞的版本
---
## 贡献指南
如果你想为项目做出贡献,请查看 [CONTRIBUTING.md](./CONTRIBUTING.md)。
## 许可证
[MIT License](./LICENSE)
---
**注意**:本更新日志从 v2.4.0 版本开始记录。更早的版本历史请查看 Git 提交记录。
---
[English Version](./CHANGELOG.en.md)

@ -0,0 +1,31 @@
FROM node:20.19-alpine AS build
WORKDIR /app
RUN corepack enable
COPY package.json pnpm-lock.yaml .npmrc ./
RUN pnpm install --frozen-lockfile
COPY . .
ARG VITE_BASE_URL=/
ARG VITE_APP_BASE_API=/api/v1
ARG VITE_APP_WS_ENDPOINT=
ENV VITE_BASE_URL=${VITE_BASE_URL} \
VITE_APP_BASE_API=${VITE_APP_BASE_API} \
VITE_APP_WS_ENDPOINT=${VITE_APP_WS_ENDPOINT}
RUN pnpm run build
FROM nginx:1.25-alpine AS runtime
ENV BACKEND_UPSTREAM=http://fastapiadmin-backend:8001
COPY nginx/default.conf.template /etc/nginx/templates/default.conf.template
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -q --spider http://127.0.0.1/ || exit 1

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 SuperManTT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,278 @@
# FastAPI Admin · 前端工程web
基于 **Vue 3 + Vite + TypeScript + Element Plus** 的后台管理前端,与 FastAPI Admin 后端配套使用。状态管理为 **Pinia**,样式以 **Tailwind CSS 4****SCSS** 为主,接口请求使用 **Axios**
> **与仓库根文档的关系**项目总览、一键前后端启动、演示账号、Docker 部署等请以 [根目录 README.md](../../README.md) 为准;**本文档**侧重 `frontend/web/` 目录结构、环境变量与前端开发约定。
## 快速开始
### 环境准备
| 工具 | 版本要求 |
| ------- | ------------------------------------------------------ |
| Node.js | ≥ 20.19(见 `package.json``engines` |
| pnpm | ≥ 8.8,推荐 **pnpm 9**(与 `packageManager` 字段一致) |
未安装 pnpm 时可执行:`corepack enable && corepack prepare pnpm@9.15.3 --activate`(版本可按项目 `packageManager` 调整)。
### 安装依赖并启动
```bash
cd frontend/web
pnpm install
pnpm dev
```
默认开发端口由 **`.env`** 中的 **`VITE_PORT`** 决定(当前模板为 **5173**)。
### 与后端联调
1. 先启动 **FastAPI Admin 后端**,监听地址与 **`.env.development`** 里 **`VITE_API_BASE_URL`** 一致(模板默认为 **`http://127.0.0.1:8001`**)。
2. 前端开发时,浏览器请求发往当前页面同源路径,由 **Vite `server.proxy`****`VITE_APP_BASE_API`**(如 `/api/v1`)转发到上述后端。
3. 若页面提示「连接被拒绝」,检查后端是否启动、端口是否一致,或把 **`VITE_API_BASE_URL`** 改成你的实际后端地址。
## 架构概览
```
main.ts 启动
└─ initPlugins(app) ← 插件注册Pinia → Router → 指令 → i18n → Element Plus
└─ mount("#app")
└─ App.vue
├─ onBeforeMount: 主题初始化
└─ onMounted: bootstrap() → 存储检查/版本升级/站点配置
└─ 路由守卫 beforeEach
├─ 存储失效检测
├─ 登录态校验
├─ 动态路由注册(菜单 → addRoute
└─ 标签/标题同步
```
路由采用 **Hash 模式**静态路由Layout/登录/404首屏注册业务路由由守卫根据菜单权限延迟 `addRoute`。HTTP 拦截器支持 **Token 静默续期**401 时自动 refresh失败后跳转登录
## 技术栈
| 类别 | 选型 |
| ------ | ------------------------------------------------------ |
| 框架 | Vue 3Composition API / `<script setup>` |
| 构建 | Vite 7 |
| 语言 | TypeScript |
| UI | Element Plus |
| 路由 | Vue Router 4Hash静态路由 + 守卫内动态 `addRoute` |
| 状态 | Pinia + pinia-plugin-persistedstate |
| 样式 | Tailwind CSS 4、SCSS |
| HTTP | Axios |
| 国际化 | vue-i18n |
## 常用脚本
| 命令 | 说明 |
| --------------------------------------------- | ------------------------------------------------------------- |
| `pnpm dev` | 本地开发(读取 `.env` + `.env.development` |
| `pnpm dev:force` | 强制预打包依赖后启动(缓存异常时) |
| `pnpm build` | `vue-tsc` 类型检查 + 生产构建,产物在 **`dist/`** |
| `pnpm build:dev` / `build:test` / `build:pro` | 按 mode 构建(需对应 env 文件) |
| `pnpm preview` | 本地预览构建结果 |
| `pnpm type-check` | 仅 TypeScript 检查 |
| `pnpm lint` | ESLint + Prettier + Stylelint |
| `pnpm clean:dev` | 执行 `scripts/clean-dev.ts`(清理演示等,使用前阅读脚本说明) |
| `pnpm clean:cache` | 清理 Vite 等缓存 |
## 目录结构src
```
src/
├── api/ # 按业务模块划分的接口封装
├── assets/ # 图片、字体、全局样式等
├── components/ # 通用与业务组件
├── config/ # 应用配置fastEnter、headerBar 等)
├── enums/ # 枚举
├── hooks/ # 组合式函数
├── layouts/ # 布局壳art-* 顶栏、侧栏、Tab、设置抽屉等
├── locales/ # i18n如 langs/zh.json
├── plugins/ # Vue 插件注册入口plugins/index.ts → initPlugins
├── router/ # staticRoutes、动态路由、守卫、MenuProcessor
├── store/ # Pinia 模块
├── types/ # TypeScript 类型
├── utils/ # 工具(含 `@utils`
├── views/ # 页面module_* / dashboard 等)
├── App.vue
└── main.ts # 入口
```
## 路径别名
| 别名 | 指向 |
| ------------------ | ------------ |
| `@` | `src/` |
| `@views` | `src/views` |
| `@stores` | `src/store` |
| `@utils` | `src/utils` |
| `@styles` | `src/styles` |
| `@imgs` / `@icons` | 图片与 SVG |
**`vite.config.ts`**、`tsconfig.json` 中 `paths` 保持一致。
## 环境变量
只有以 **`VITE_`** 开头的变量会注入前端代码:
| 变量 | 作用 |
| ---------------------- | ------------------------------------------------ |
| `VITE_PORT` | 开发服务器端口 |
| `VITE_BASE_URL` | 部署基础路径(子目录部署时形如 `/admin/` |
| `VITE_APP_BASE_API` | 接口路径前缀,与 Vite 代理匹配 |
| `VITE_API_URL` | 浏览器侧发出的 API 根前缀(开发时常为 `/` |
| `VITE_API_BASE_URL` | **代理目标**:后端 HTTP 根地址 |
| `VITE_ACCESS_MODE` | `frontend` / `backend` / `mixed`,菜单与路由来源 |
| `VITE_APP_WS_ENDPOINT` | WebSocket如 AI 对话) |
| `VITE_APP_TITLE` | 页面标题(可被后端参数配置覆盖) |
完整列表以仓库内 **`.env`**、**`.env.development`** 为准;模板说明见 **`.env.example`**。修改任一 env 后需 **重启** `pnpm dev`
## 路由与菜单
| 文件 | 职责 |
| ----------------------------- | ------------------------------------------------- |
| `src/router/staticRoutes.ts` | 静态路由、`dashboardLayoutChildren`、壳层菜单合并 |
| `src/router/dynamicRoutes.ts` | 菜单驱动的动态路由 |
| `src/router/beforeEach.ts` | 权限与动态挂载 |
| `src/router/MenuProcessor.ts` | 后端菜单 → 前端路由记录 |
新增业务页:一般需要 **视图 +(可选)静态或动态路由 + 后端菜单/i18n**,三者路径与 **name** 保持一致。
## 常见问题
| 现象 | 建议 |
| ------------------------- | --------------------------------------------------------------------------------------- |
| `ECONNREFUSED` / 网络错误 | 后端未启动或 **`VITE_API_BASE_URL`** 端口错误 |
| 接口 401 / 频繁跳转登录 | Token 失效或未登录;清除站点本地存储后重新登录 |
| 修改 `.env` 不生效 | 必须 **重启** `pnpm dev` |
| 依赖异常、热更新怪异 | 尝试 **`pnpm clean:cache`** 后再 **`pnpm dev`**;仍不行可 **`pnpm dev:force`** |
| 类型报错 | 运行 **`pnpm type-check`**;自动生成类型见 `src/types/import/`(勿手改自动生成的 d.ts |
## 构建与部署
- 输出目录:**`dist/`**
- 部署在子路径时配置 **`VITE_BASE_URL`**,并配置网关/Nginx 将前端资源与 `/api` 等转发到后端
- 生产构建可能移除部分 `console`(见 **`vite.config.ts`** 中 `terserOptions`
## 代码规范与 Git
- **格式化与校验**`pnpm lint`
- **提交**husky、lint-staged、commitlint可使用 **`pnpm commit`**Commitizen / cz-git
/\*\*
- 浅蓝色系主题 — #1975FC
-
- 来源:花瓣网 B端淡蓝色配色参考
- 主色 #1975FC — 明快透亮、B端专业感
-
- 【启用方式】在本文件中取消注释对应主题模式的变量块,
- 并在 src/styles/index.scss 中 @use "./core/theme-blue";
- 同时在 src/config/setting.ts 中将 themeColor 改为 "#1975FC"。
-
- 【色阶 palette】
- #f8fcff 最浅底色(页面背景)
- #f0f5ff 浅蓝悬停态
- #e5f2ff 选中态 / tag 背景
- #c5ddff 浅蓝边框 / 分割线点缀
- #82b8ff 中浅蓝(图标辅助色)
- #1975FC ★ 主色
- #115ac8 深蓝hover 按下态)
-
- 【功能色】
- success: #16a34a
- warning: #d97706
- danger: #dc2626
- error: #fa896b
- info: #8b92a0
-
- ================================================================
\*/
// ================================================================
// 亮色主题 — 浅蓝色系
// ================================================================
// :root {
// /_ ---- 侧边栏 ---- _/
// --sidebar-bg-color: #ffffff;
// --sidebar-border-color: #edf0f2;
// --sidebar-text-color: #485260;
// --sidebar-text-hover-color: #1975fc;
// --sidebar-text-active-color: #1975fc;
// --sidebar-bg-hover-color: #f0f5ff;
// --sidebar-bg-active-color: #e5f2ff;
// --sidebar-logo-color: #121926;
// --sidebar-section-title-color: #8b92a0;
//
// /_ ---- 页面 ---- _/
// --main-bg-color: #f4f6f9;
// --header-bg-color: #ffffff;
// --header-border-color: #edf0f2;
//
// /_ ---- 卡片 ---- _/
// --card-bg-color: #ffffff;
// --card-border-color: #edf0f2;
//
// /_ ---- 表格 ---- _/
// --table-header-bg: #f8fafc;
// --table-header-color: #8b92a0;
// --table-row-hover-bg: #f8fafc;
// --table-border-color: #edf0f2;
//
// /_ ---- 文字 ---- _/
// --text-primary: #121926;
// --text-regular: #334155;
// --text-secondary: #8b92a0;
//
// /_ ---- 标签 ---- _/
// --tag-bg-color: #e5f2ff;
// --tag-text-color: #1975fc;
//
// /_ ---- 按钮 —— 由 Element Plus 通过 primary 色阶自动生成 ---- _/
// /_ ---- 进度条 ---- _/
// --nprogress-color: #1975fc;
//
// /_ ---- 边框模式 ---- _/
// --fa-card-border: #edf0f2;
// --default-box-color: #ffffff;
// --default-border: #edf0f2;
// --custom-radius: 0.75rem;
// }
// ================================================================
// 暗色主题 — 浅蓝色系(可选)
// ================================================================
// html.dark {
// --sidebar-bg-color: #0c1031;
// --sidebar-border-color: #1a1e3d;
// --sidebar-text-color: #a8adcc;
// --sidebar-text-hover-color: #5c9fff;
// --sidebar-text-active-color: #1975fc;
// --sidebar-bg-hover-color: rgba(25, 117, 252, 0.15);
// --sidebar-bg-active-color: rgba(25, 117, 252, 0.25);
// --sidebar-logo-color: #ffffff;
// --sidebar-section-title-color: #6b7094;
//
// --main-bg-color: #0f1420;
// --header-bg-color: #151a2d;
// --header-border-color: #1a1e3d;
//
// --card-bg-color: #151a2d;
// --card-border-color: #1a1e3d;
//
// --table-header-bg: #1a1e3d;
// --table-header-color: #6b7094;
// --table-row-hover-bg: #1a1e3d;
// --table-border-color: #1a1e3d;
//
// --text-primary: #e5e7eb;
// --text-regular: #a8adcc;
// --text-secondary: #6b7094;
//
// --tag-bg-color: rgba(25, 117, 252, 0.2);
// --tag-text-color: #5c9fff;
// }

@ -0,0 +1,8 @@
export const BANNER = [
`
`,
].join("\n");

@ -0,0 +1,47 @@
import type { PluginOption, ResolvedConfig } from "vite";
import pc from "picocolors";
import { BANNER } from "./banner";
const { green, blue, bold, dim, cyan, yellow, magenta } = pc;
const W = 96;
const ANSI_RE = new RegExp(String.fromCharCode(27) + "\\[\\d+(;\\d+)*m", "g");
const strip = (s: string) => s.replace(ANSI_RE, "");
const BANNER_LINES = BANNER.split("\n");
const row = (content: string) => {
const pad = Math.max(0, W - strip(content).length - 1);
return ` ${dim("│")} ${content}${" ".repeat(pad)}${dim("│")}`;
};
function printBanner(env: Record<string, string>, mode: string) {
const line = dim("─".repeat(W));
console.log(
[
"",
` ${dim("┌")}${line}${dim("┐")}`,
row(""),
row(`${bold(cyan("fastapiadmin"))} ${bold(magenta(`v${env.VITE_VERSION || "3.0.0"}`))}`),
row(""),
...BANNER_LINES.map((l) => row(green(l))),
row(""),
row(`${dim("mode")} ${yellow(env.VITE_APP_ENV || mode)}`),
row(`${dim("api")} ${blue(env.VITE_API_BASE_URL || "")}`),
row(`${dim("port")} ${cyan(env.VITE_PORT || "")}`),
row(""),
` ${dim("└")}${line}${dim("┘")}`,
"",
].join("\n")
);
}
export default function vitePluginStart(): PluginOption {
return {
name: "vite:start-banner",
enforce: "pre",
configResolved(resolvedConfig: ResolvedConfig) {
const env = resolvedConfig.env as Record<string, string>;
printBanner(env, resolvedConfig.mode);
},
};
}

@ -0,0 +1,93 @@
module.exports = {
// 继承的规则
extends: ["@commitlint/config-conventional"],
// 自定义规则
rules: {
// @see https://commitlint.js.org/#/reference-rules
// 提交类型枚举git提交type必须是以下类型
"type-enum": [
2,
"always",
[
"feat", // 新增功能
"fix", // 修复缺陷
"docs", // 文档变更
"style", // 代码格式(不影响功能,例如空格、分号等格式修正)
"refactor", // 代码重构(不包括 bug 修复、功能新增)
"perf", // 性能优化
"test", // 添加疏漏测试或已有测试改动
"build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
"ci", // 修改 CI 配置、脚本
"revert", // 回滚 commit
"chore", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
"wip", // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
],
],
"subject-case": [0], // subject大小写不做校验
},
prompt: {
messages: {
type: "选择你要提交的类型 :",
scope: "选择一个提交范围(可选):",
customScope: "请输入自定义的提交范围 :",
subject: "填写简短精炼的变更描述 :\n",
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixesSelect: "选择关联issue前缀可选:",
customFooterPrefix: "输入自定义issue前缀 :",
footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
generatingByAI: "正在通过 AI 生成你的提交简短描述...",
generatedSelectByAI: "选择一个 AI 生成的简短描述:",
confirmCommit: "是否提交或修改commit ?",
},
// prettier-ignore
types: [
{ value: "feat", name: "特性: ✨ 新增功能", emoji: ":sparkles:" },
{ value: "fix", name: "修复: 🐛 修复缺陷", emoji: ":bug:" },
{ value: "docs", name: "文档: 📝 文档变更(更新README文件或者注释)", emoji: ":memo:" },
{ value: "style", name: "格式: 🌈 代码格式(空格、格式化、缺失的分号等)", emoji: ":lipstick:" },
{ value: "refactor", name: "重构: 🔄 代码重构(不修复错误也不添加特性的代码更改)", emoji: ":recycle:" },
{ value: "perf", name: "性能: 🚀 性能优化", emoji: ":zap:" },
{ value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
{ value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
{ value: "ci", name: "集成: ⚙️ 修改 CI 配置、脚本", emoji: ":ferris_wheel:"},
{ value: "revert", name: "回退: ↩️ 回滚 commit",emoji: ":rewind:"},
{ value: "chore", name: "其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
{ value: "wip", name: "开发中: 🚧 开发阶段临时提交", emoji: ":construction:"},
],
useEmoji: true,
emojiAlign: "center",
useAI: false,
aiNumber: 1,
themeColorCode: "",
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixes: [{ value: "closed", name: "closed: ISSUES has been processed" }],
customIssuePrefixAlign: "top",
emptyIssuePrefixAlias: "skip",
customIssuePrefixAlias: "custom",
allowCustomIssuePrefix: true,
allowEmptyIssuePrefix: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: "",
},
};

@ -0,0 +1,87 @@
// ESLint 配置文件
import fs from "fs";
import path, { dirname } from "path";
import { fileURLToPath } from "url";
// 从 ESLint 插件中导入推荐配置
import pluginJs from "@eslint/js";
import pluginVue from "eslint-plugin-vue";
import globals from "globals";
import tseslint from "typescript-eslint";
// 使用 import.meta.url 获取当前模块的路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// 读取 .auto-import.json 文件的内容,并将其解析为 JSON 对象
const autoImportConfig = JSON.parse(
fs.readFileSync(path.resolve(__dirname, ".auto-import.json"), "utf-8")
);
export default [
// 忽略文件flat config 中 ignores 需在最前面)
{
ignores: [
"**/node_modules/**",
"**/dist/**",
"public/**",
".vscode/**",
"src/assets/**",
"src/utils/console.ts",
".auto-import.json",
],
},
// 全局语言环境
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
// 基础推荐配置
pluginJs.configs.recommended,
...tseslint.configs.recommended,
...pluginVue.configs["flat/essential"],
// 项目自定义规则
{
files: ["**/*.{js,mjs,cjs,ts,tsx,vue}"],
languageOptions: {
globals: {
...autoImportConfig.globals,
// TypeScript 全局命名空间global.d.ts 中声明的类型)
Api: "readonly",
OptionType: "readonly",
ApiResponse: "readonly",
UploadFilePath: "readonly",
// unplugin-vue-components 自动注册的组件引用
FaSearchBar: "readonly",
FaSearchBarWithAudit: "readonly",
FaForm: "readonly",
},
},
rules: {
"no-var": "error",
"no-multiple-empty-lines": ["warn", { max: 1 }],
"no-unexpected-multiline": "error",
"@typescript-eslint/no-explicit-any": "off",
"vue/multi-word-component-names": "off",
// Prettier 负责格式化ESLint 只关注代码质量
},
},
// Vue 文件:使用 TypeScript parser
{
files: ["**/*.vue"],
languageOptions: {
parserOptions: { parser: tseslint.parser },
},
},
// .d.ts 声明文件:放宽规则
{
files: ["**/*.d.ts"],
rules: {
"@typescript-eslint/no-empty-object-type": "off",
},
},
];

@ -0,0 +1,56 @@
<!doctype html>
<html lang="zh-CN">
<head>
<title>%VITE_APP_TITLE%</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板" />
<meta
name="keywords"
content="vue,element-plus,typescript,vue-element-admin,vue3-element-admin"
/>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<style>
/* 防止 Vue 加载前出现白屏:背景色与主题初始 class 同步 */
html {
background-color: #fafbfc;
}
html.dark {
background-color: #070707;
}
</style>
<script>
/**
* 在 Vue 挂载前同步主题 class。
*
* Vue 初始化后 useTheme 会接管主题管理,
* 但在此之前若用户之前选择了暗色主题html 缺少 "dark" class 会导致闪烁。
* 此处直接从 localStorage 读取主题偏好并提前设置 class。
*
* key "sys-theme" 须与 StorageConfig.THEME_KEY 保持一致。
*/
(function () {
try {
if (typeof Storage === "undefined" || !window.localStorage) {
return;
}
const themeType = localStorage.getItem("sys-theme");
if (themeType === "dark") {
document.documentElement.classList.add("dark");
}
} catch (e) {
console.warn("Failed to apply initial theme:", e);
}
})();
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

@ -0,0 +1,41 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
client_max_body_size 100m;
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript application/xml image/svg+xml;
location / {
try_files $uri $uri/ /index.html;
}
location /api/v1 {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_pass ${BACKEND_UPSTREAM};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /static {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass ${BACKEND_UPSTREAM};
}
}

12368
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,216 @@
{
"name": "fastapiadmin",
"description": "Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板",
"version": "3.0.0",
"private": true,
"type": "module",
"scripts": {
"i": "pnpm install",
"dev": "vite",
"dev:force": "vite --force",
"prod": "vite --mode prod",
"build": "vue-tsc --noEmit && vite build",
"build:pro": "pnpm vite build --mode pro",
"build:gitee": "pnpm vite build --mode gitee",
"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",
"icon": "esno ./scripts/icon.ts",
"preview": "vite preview",
"type-check": "vue-tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"lint": "pnpm run lint:eslint && pnpm run lint:prettier && pnpm run lint:stylelint",
"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:stylelint": "stylelint \"**/*.{css,scss,vue}\" --cache --fix",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.cjs",
"commit": "git-cz",
"clean:dev": "tsx scripts/clean-dev.ts"
},
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
},
"lint-staged": {
"*.{js,ts,mjs,mts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{cjs,json,jsonc}": [
"prettier --write"
],
"*.vue": [
"eslint --fix",
"stylelint --fix --allow-empty-input",
"prettier --write"
],
"*.{html,htm}": [
"prettier --write"
],
"*.{scss,css,less}": [
"stylelint --fix --allow-empty-input",
"prettier --write"
],
"*.{md,mdx}": [
"prettier --write"
],
"*.{yaml,yml}": [
"prettier --write"
]
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@iconify-json/line-md": "^1.2.16",
"@iconify-json/ri": "^1.2.10",
"@iconify-json/svg-spinners": "^1.2.4",
"@iconify/vue": "^5.0.1",
"@tailwindcss/vite": "^4.3.0",
"@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",
"@wangeditor-next/editor": "^5.7.0",
"@wangeditor-next/editor-for-vue": "^5.1.14",
"animate.css": "^4.1.1",
"axios": "^1.16.1",
"clipboard": "^2.0.11",
"codemirror": "^5.65.21",
"codemirror-editor-vue3": "^2.8.0",
"crypto-js": "^4.2.0",
"dagre": "^0.8.5",
"dayjs": "^1.11.20",
"dompurify": "^3.4.3",
"echarts": "^6.0.0",
"element-plus": "~2.13.7",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"highlight.js": "^11.11.1",
"js-beautify": "^1.15.4",
"markdown-it": "^14.1.0",
"markdown-it-highlightjs": "^4.3.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"ohash": "^2.0.11",
"path-browserify": "^1.0.1",
"path-to-regexp": "^8.4.2",
"picocolors": "^1.1.1",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode.vue": "^3.9.0",
"qs": "^6.15.0",
"tailwindcss": "^4.3.0",
"vue": "^3.5.34",
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^11.1.10",
"vue-json-pretty": "^2.5.0",
"vue-router": "^5.0.7",
"vue-web-terminal": "^3.4.1",
"vue3-cron-plus": "^0.1.9",
"vuedraggable": "^2.24.3",
"xgplayer": "^3.0.20",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^21.0.1",
"@commitlint/config-conventional": "^21.0.1",
"@eslint/js": "^10.0.0",
"@iconify/utils": "^2.3.0",
"@types/codemirror": "^5.60.17",
"@types/dagre": "^0.7.54",
"@types/file-saver": "^2.0.7",
"@types/markdown-it": "^14.1.2",
"@types/node": "^24.0.5",
"@types/nprogress": "^0.2.3",
"@types/path-browserify": "^1.0.3",
"@types/qs": "^6.14.0",
"@typescript-eslint/eslint-plugin": "^8.59.3",
"@typescript-eslint/parser": "^8.59.3",
"@vitejs/plugin-vue": "^6.0.6",
"@vue/compiler-sfc": "^3.5.22",
"@vue/test-utils": "^2.4.10",
"autoprefixer": "^10.4.21",
"commitizen": "^4.3.1",
"cz-git": "^1.13.1",
"eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.3",
"eslint-plugin-vue": "^10.4.0",
"fs-extra": "^11.2.0",
"globals": "^15.15.0",
"husky": "^9.1.7",
"jsdom": "^29.1.1",
"lint-staged": "^15.5.2",
"postcss": "^8.5.6",
"postcss-html": "^1.8.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.6.2",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.89.2",
"stylelint": "^17.0.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^7.7.0",
"stylelint-config-recommended": "^18.0.0",
"stylelint-config-recommended-scss": "^17.0.1",
"stylelint-config-recommended-vue": "^1.6.1",
"stylelint-config-standard": "^40.0.0",
"stylelint-order": "^8.0.0",
"stylelint-prettier": "^5.0.3",
"terser": "^5.43.1",
"tsx": "^4.20.3",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.3",
"unplugin-auto-import": "^20.2.0",
"unplugin-element-plus": "^0.10.0",
"unplugin-vue-components": "^29.1.0",
"vite": "^7.1.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^8.1.2",
"vitest": "^4.1.7",
"vue-demi": "^0.14.9",
"vue-eslint-parser": "^10.4.0",
"vue-img-cutter": "^3.1.1",
"vue-tsc": "^3.2.9"
},
"pnpm": {
"overrides": {
"glob": "13.0.6",
"rimraf": "6.1.3",
"uuid": "14.0.0",
"fstream": "1.0.12",
"lodash.isequal": "4.5.0"
}
},
"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",
"browserslist": [
"Chrome >= 84",
"Firefox >= 83",
"Safari >= 14.1",
"Edge >= 84",
"not dead"
]
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

@ -0,0 +1 @@
<svg viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="a" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="44" y="42" width="312" height="217"><path d="M355.3 42H44v216.9h311.3V42Z" fill="#fff"/></mask><g mask="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M288.2 248.4h25.1v-30h-25.1v30Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M304.498 238.199c-1.5-3.9-5.9-15.4-4-21.6-2.9.8-3.3.1-5-.1-1.7-.1 0 10.7 2.2 16.4 1.7 4.5 2.1 11.1 2.1 13.6h5.4c.2-1.9.3-5.5-.7-8.3Z" fill="#fff"/><path d="M311.5 214.7v-1.6c0-.7-.6-1.3-1.3-1.3h-22.8c-.7 0-1.3.6-1.3 1.3v1.6" fill="#fff"/><path d="M311.5 214.7v-1.6c0-.7-.6-1.3-1.3-1.3h-22.8c-.7 0-1.3.6-1.3 1.3v1.6M290.2 214.7h21.4c1 0 1.8.8 1.8 1.8v29" stroke="#071F4D" stroke-width="1.096"/><path d="M284.3 245.6v-29c0-1 .8-1.8 1.8-1.8h1.6" fill="#fff"/><path d="M284.3 245.6v-29c0-1 .8-1.8 1.8-1.8h1.6" stroke="#071F4D" stroke-width="1.096"/><path d="M295.402 216.5c-.9 4.2-.4 9.7 2.8 17.5 2.4 5.9 1.9 10.2 1.8 12.3M300.502 216.5c-.9 4.2-.4 9.7 2.8 17.5 2.4 5.9 1.9 10.2 1.8 12.3" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m331 258.4-.3-5.2H88.5l-1.2 5.2H331Z" fill="#C7DEFF"/><path d="M252.9 248.7H331M216.6 258.4H331M47.1 139.3l-2.6 1.5 42.7 117.6h129.2v-6.6" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m247.2 248.6-40.4-111.3H50.5l40.3 111.3h156.4Z" fill="#fff"/><path d="m247.2 248.6-40.4-111.3H50.5l40.3 111.3h156.4Z" stroke="#071F4D"/><path d="m203.2 153.2 32.2 88.7H97.8l-32.3-88.7" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M72.2 146.9c-.77 0-1.4.63-1.4 1.4 0 .77.63 1.4 1.4 1.4.77 0 1.4-.63 1.4-1.4 0-.77-.63-1.4-1.4-1.4ZM79.3 146.9c-.77 0-1.4.63-1.4 1.4 0 .77.63 1.4 1.4 1.4.77 0 1.4-.63 1.4-1.4 0-.77-.63-1.4-1.4-1.4Z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M263.5 171.2h80.3v-63.7h-80.3v63.7Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M290 143.9h-45.6l12.5 51.3H290v-51.3Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M286 117.4h-29.3v77.8h92.9v-67.6l-55.9.6-7.7-10.8Z" fill="#00E4E5"/><path d="m332.6 127.6-38.9.6-7.7-10.8h-11.7M308.9 195.2h45.9M250.3 195.2h28.5M287.3 195.2h12.3" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M130.5 211.4H186v-44h-55.5v44Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M148.7 192.5h-31.6l8.7 35.5h22.9v-35.5Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M145.9 174.2h-20.2V228h64.1v-46.7l-38.6.4-5.3-7.5Z" fill="#006EFF"/><path d="m179 181.3-27.8.4-5.3-7.5h-7.7M176.2 201.7h19.2M163.2 210.7H195M172.1 228h-54.2M184.8 228h8.1M174.9 228h5.4" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m293.2 155.7-6.4 6.3 15.3 15.3 22.7-22.6-6.4-6.4-16.3 16.3-8.9-8.9Z" fill="#fff"/><path d="M57.2 258.4h283.6M345.9 258.4h8.1M55.4 258.4h220.5M160.1 118.8l-1.2 2.7M156.7 127c-.3.8-.7 1.8-1.1 2.8M222 68.5c-1 .2-1.9.5-2.9.8M214.1 70.7c-5.8 1.9-11.3 4.4-16.5 7.4M195.4 79.5c-.9.5-1.7 1.1-2.5 1.6M314.2 98.5c-.6-.8-1.3-1.5-2-2.3M308.9 92.8c-4-4-8.3-7.6-13-10.8M293.9 80.7c-.8-.5-1.7-1.1-2.5-1.6" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M251.296 71.203c-3.6-1.5-18.5-2.9-21.8-1.9-1 5.8 4.9 13.5 4.9 13.5s6-9.9 16.9-11.6Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M251.3 42.704c-6.5 6.7-7.8 13-8.8 19.3 24.4-1.1 36.3 13 42.8 20 3.2-9.1 7.8-23 7.2-29-7.1-6.4-20-11.7-41.2-10.3Z" fill="#C7DEFF"/><path d="M230 69.3c36.2-3.8 52 21.1 52 21.1s11.4-28.2 10.5-37.4c-7.3-6.5-23.3-12-45.6-10.1-9 6.3-15.6 18.7-16.9 26.4Z" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M161.604 70.7c-6 8.4-9.9 21.9-8.8 33.8 8.4 5.3 32.3 10.5 43.6 11.5 6.1-7.9 15.9-26 15.9-26s-32-4.8-50.7-19.3Z" fill="#C7DEFF"/><path d="M193.103 119.5c4.8-2.7 19.2-29.5 19.2-29.5s-35.8-5.4-53.7-21.8c-9.3 6.1-16.4 24.3-15 40.1 10.6 6.7 45.8 13.3 49.5 11.2Z" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M189.5 111.6c-3 5.2-5.7 7.2-9.8 6.6 12.2 2.6 13.5 1.2 15.6-1.1 2.2-2.4 4.2-6.6 4.2-6.6s-3.1 2.5-10 1.1Z" fill="#071F4D"/><path d="M331 251.8v6.6M77 165.4l-2.7-6.7h7.8M222.8 228.9l2.8 6.6h-7.9" stroke="#071F4D"/></g></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 KiB

@ -0,0 +1,70 @@
<svg width="695" height="695" viewBox="195 164.5 695 695" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="white"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="#ECEFFF"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint0_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint1_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint2_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint3_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="white"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="#ECEFFF"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint4_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint5_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint6_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint7_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="white"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="#ECEFFF"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint8_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint9_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint10_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint11_radial_402_5926)"/>
<defs>
<radialGradient id="paint0_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-137.956 -387.324 387.315 -228.814 483.689 731.186)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint1_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-140.464 -366.075 365.281 -152.396 447.03 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint2_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-111.514 -277.545 374.955 -279.04 405.025 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint3_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-190.674 -263.482 350.863 -361.721 469.942 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint4_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-186.573 -387.324 523.807 -228.814 804.163 731.186)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint5_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-189.964 -366.075 494.008 -152.396 754.585 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint6_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-150.813 -277.545 507.091 -279.04 697.777 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint7_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-257.868 -263.482 474.509 -361.721 785.571 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint8_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-143.901 -163.816 404.006 -96.7753 707.774 729.368)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint9_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-146.517 -154.829 381.023 -64.455 669.536 712.651)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint10_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-116.32 -117.386 391.113 -118.018 625.721 732.407)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint11_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-198.891 -111.438 365.983 -152.988 693.435 718.729)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

@ -0,0 +1,712 @@
// scripts/clean-dev.ts
import fs from "fs/promises";
import path from "path";
// 现代化颜色主题
const theme = {
// 基础颜色
reset: "\x1b[0m",
bold: "\x1b[1m",
dim: "\x1b[2m",
// 前景色
primary: "\x1b[38;5;75m", // 亮蓝色
success: "\x1b[38;5;82m", // 亮绿色
warning: "\x1b[38;5;220m", // 亮黄色
error: "\x1b[38;5;196m", // 亮红色
info: "\x1b[38;5;159m", // 青色
purple: "\x1b[38;5;141m", // 紫色
orange: "\x1b[38;5;208m", // 橙色
gray: "\x1b[38;5;245m", // 灰色
white: "\x1b[38;5;255m", // 白色
// 背景色
bgDark: "\x1b[48;5;235m", // 深灰背景
bgBlue: "\x1b[48;5;24m", // 蓝色背景
bgGreen: "\x1b[48;5;22m", // 绿色背景
bgRed: "\x1b[48;5;52m", // 红色背景
};
// 现代化图标集
const icons = {
rocket: "🚀",
fire: "🔥",
star: "⭐",
gem: "💎",
crown: "👑",
magic: "✨",
warning: "⚠️",
success: "✅",
error: "❌",
info: "",
folder: "📁",
file: "📄",
image: "🖼️",
code: "💻",
data: "📊",
globe: "🌐",
map: "🗺️",
chat: "💬",
bolt: "⚡",
shield: "🛡️",
key: "🔑",
link: "🔗",
clean: "🧹",
trash: "🗑️",
check: "✓",
cross: "✗",
arrow: "→",
loading: "⏳",
};
// 格式化工具
const fmt = {
title: (text: string) => `${theme.bold}${theme.primary}${text}${theme.reset}`,
subtitle: (text: string) => `${theme.purple}${text}${theme.reset}`,
success: (text: string) => `${theme.success}${text}${theme.reset}`,
error: (text: string) => `${theme.error}${text}${theme.reset}`,
warning: (text: string) => `${theme.warning}${text}${theme.reset}`,
info: (text: string) => `${theme.info}${text}${theme.reset}`,
highlight: (text: string) => `${theme.bold}${theme.white}${text}${theme.reset}`,
dim: (text: string) => `${theme.dim}${theme.gray}${text}${theme.reset}`,
orange: (text: string) => `${theme.orange}${text}${theme.reset}`,
// 带背景的文本
badge: (text: string, bg: string = theme.bgBlue) =>
`${bg}${theme.white}${theme.bold} ${text} ${theme.reset}`,
// 渐变效果模拟
gradient: (text: string) => {
const colors = ["\x1b[38;5;75m", "\x1b[38;5;81m", "\x1b[38;5;87m", "\x1b[38;5;159m"];
const chars = text.split("");
return chars.map((char, i) => `${colors[i % colors.length]}${char}`).join("") + theme.reset;
},
};
// 创建现代化标题横幅
function createModernBanner() {
console.log();
console.log(
fmt.gradient(" ╔══════════════════════════════════════════════════════════════════╗")
);
console.log(
fmt.gradient(" ║ ║")
);
console.log(
`${icons.rocket} ${fmt.title("ART DESIGN PRO")} ${fmt.subtitle("· 代码精简程序")} ${icons.magic}`
);
console.log(
`${fmt.dim("为项目移除演示数据,快速切换至开发模式")}`
);
console.log(
fmt.gradient(" ║ ║")
);
console.log(
fmt.gradient(" ╚══════════════════════════════════════════════════════════════════╝")
);
console.log();
}
// 创建分割线
function createDivider(char = "─", color = theme.primary) {
console.log(`${color}${" " + char.repeat(66)}${theme.reset}`);
}
// 创建卡片样式容器
function createCard(title: string, content: string[]) {
console.log(` ${fmt.badge("", theme.bgBlue)} ${fmt.title(title)}`);
console.log();
content.forEach((line) => {
console.log(` ${line}`);
});
console.log();
}
// 进度条动画
function createProgressBar(current: number, total: number, text: string, width = 40) {
const percentage = Math.round((current / total) * 100);
const filled = Math.round((current / total) * width);
const empty = width - filled;
const filledBar = "█".repeat(filled);
const emptyBar = "░".repeat(empty);
process.stdout.write(
`\r ${fmt.info("进度")} [${theme.success}${filledBar}${theme.gray}${emptyBar}${theme.reset}] ${fmt.highlight(percentage + "%")})}`
);
if (current === total) {
console.log();
}
}
// 统计信息
const stats = {
deletedFiles: 0,
deletedPaths: 0,
failedPaths: 0,
startTime: Date.now(),
totalFiles: 0,
};
// 清理目标
const targets = [
"README.md",
"README.zh-CN.md",
"CHANGELOG.md",
"CHANGELOG.zh-CN.md",
"src/views/safeguard",
"src/views/dashboard/analysis",
"src/views/dashboard/ecommerce",
"src/mock/json",
"src/mock/temp/articleList.ts",
"src/mock/temp/commentDetail.ts",
"src/mock/temp/commentList.ts",
"src/assets/images/cover",
"src/assets/images/safeguard",
"src/assets/images/3d",
"src/components/charts/art-map-chart",
"src/components/business/comment-widget",
];
// 递归统计文件数量
async function countFiles(targetPath: string): Promise<number> {
const fullPath = path.resolve(process.cwd(), targetPath);
try {
const stat = await fs.stat(fullPath);
if (stat.isFile()) {
return 1;
} else if (stat.isDirectory()) {
const entries = await fs.readdir(fullPath);
let count = 0;
for (const entry of entries) {
const entryPath = path.join(targetPath, entry);
count += await countFiles(entryPath);
}
return count;
}
} catch {
return 0;
}
return 0;
}
// 统计所有目标的文件数量
async function countAllFiles(): Promise<number> {
let totalCount = 0;
for (const target of targets) {
const count = await countFiles(target);
totalCount += count;
}
return totalCount;
}
// 删除文件和目录
async function remove(targetPath: string, index: number) {
const fullPath = path.resolve(process.cwd(), targetPath);
createProgressBar(index + 1, targets.length, targetPath);
try {
const fileCount = await countFiles(targetPath);
await fs.rm(fullPath, { recursive: true, force: true });
stats.deletedFiles += fileCount;
stats.deletedPaths++;
await new Promise((resolve) => setTimeout(resolve, 50));
} catch (err) {
stats.failedPaths++;
console.log();
console.log(` ${icons.error} ${fmt.error("删除失败")}: ${fmt.highlight(targetPath)}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
}
}
// 重置前端内置动态路由为空(`MenuProcessor.ts` 内 `builtinFrontendRoutes`;异常页在 staticRoutes
async function cleanRouteModules() {
const routesPath = path.resolve(process.cwd(), "src/router");
try {
try {
await fs.rm(path.join(routesPath, "dashboard.ts"), { force: true });
} catch {
// 忽略
}
try {
await fs.rm(path.join(routesPath, "builtinFrontendRoutes.ts"), { force: true });
} catch {
// 忽略(旧独立文件可能已不存在)
}
const menuProcessorPath = path.join(routesPath, "MenuProcessor.ts");
const mp = await fs.readFile(menuProcessorPath, "utf-8");
const reset = mp.replace(
/export const builtinFrontendRoutes: AppRouteRecord\[\] = \[[\s\S]*?\];/,
"export const builtinFrontendRoutes: AppRouteRecord[] = [];"
);
if (reset === mp) {
console.log(
` ${icons.warning} ${fmt.dim("MenuProcessor.ts 中未找到 builtinFrontendRoutes跳过重置")}`
);
} else {
await fs.writeFile(menuProcessorPath, reset, "utf-8");
}
console.log(` ${icons.success} ${fmt.success("清理路由演示模块完成")}`);
} catch (err) {
console.log(` ${icons.error} ${fmt.error("清理路由演示模块失败")}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
}
}
// 路由占位常量已写在 src/router/staticRoutes.ts 顶部,清理脚本不再单独重写
async function cleanRouterConstants() {
console.log(
` ${icons.info} ${fmt.dim("路由常量ROUTE_*)位于 staticRoutes.ts 顶部,请按需手动修改")}`
);
}
// 清理变更日志
async function cleanChangeLog() {
const changeLogPath = path.resolve(process.cwd(), "src/mock/upgrade/changeLog.ts");
try {
const cleanedChangeLog = `import { ref } from 'vue'
interface UpgradeLog {
version: string // 版本号
title: string // 更新标题
date: string // 更新日期
detail?: string[] // 更新内容
requireReLogin?: boolean // 是否需要重新登录
remark?: string // 备注
}
export const upgradeLogList = ref<UpgradeLog[]>([])
`;
await fs.writeFile(changeLogPath, cleanedChangeLog, "utf-8");
console.log(` ${icons.success} ${fmt.success("清空变更日志数据完成")}`);
} catch (err) {
console.log(` ${icons.error} ${fmt.error("清理变更日志失败")}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
}
}
// 清理语言文件
async function cleanLanguageFiles() {
const languageFiles = [
{ path: "src/locales/langs/zh.json", name: "中文语言文件" },
{ path: "src/locales/langs/en.json", name: "英文语言文件" },
];
for (const { path: langPath, name } of languageFiles) {
try {
const fullPath = path.resolve(process.cwd(), langPath);
const content = await fs.readFile(fullPath, "utf-8");
const langData = JSON.parse(content);
const menusToRemove = [
"widgets",
"template",
"article",
"examples",
"safeguard",
"plan",
"help",
];
if (langData.menus) {
menusToRemove.forEach((menuKey) => {
if (langData.menus[menuKey]) {
delete langData.menus[menuKey];
}
});
if (langData.menus.dashboard) {
if (langData.menus.dashboard.analysis) {
delete langData.menus.dashboard.analysis;
}
if (langData.menus.dashboard.ecommerce) {
delete langData.menus.dashboard.ecommerce;
}
}
if (langData.menus.system) {
const systemKeysToRemove = [
"nested",
"menu1",
"menu2",
"menu21",
"menu3",
"menu31",
"menu32",
"menu321",
];
systemKeysToRemove.forEach((key) => {
if (langData.menus.system[key]) {
delete langData.menus.system[key];
}
});
}
}
await fs.writeFile(fullPath, JSON.stringify(langData, null, 2), "utf-8");
console.log(` ${icons.success} ${fmt.success(`清理${name}完成`)}`);
} catch (err) {
console.log(` ${icons.error} ${fmt.error(`清理${name}失败`)}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
}
}
}
// 清理快速入口组件
async function cleanFastEnterComponent() {
const fastEnterPath = path.resolve(process.cwd(), "src/config/fastEnter.ts");
try {
const cleanedFastEnter = `/**
*
*
*/
import { WEB_LINKS } from "@utils/constants";
import type { FastEnterConfig } from '@/types/config'
const fastEnterConfig: FastEnterConfig = {
// 显示条件(屏幕宽度)
minWidth: 1200,
// 应用列表
applications: [
{
name: '工作台',
description: '系统概览与数据统计',
icon: 'ri:pie-chart-line',
iconColor: '#377dff',
enabled: true,
order: 1,
routeName: 'Console'
},
{
name: '官方文档',
description: '使用指南与开发文档',
icon: 'ri:bill-line',
iconColor: '#ffb100',
enabled: true,
order: 2,
link: WEB_LINKS.DOCS
},
{
name: '技术支持',
description: '技术支持与问题反馈',
icon: 'ri:user-location-line',
iconColor: '#ff6b6b',
enabled: true,
order: 3,
link: WEB_LINKS.COMMUNITY
},
{
name: '哔哩哔哩',
description: '技术分享与交流',
icon: 'ri:bilibili-line',
iconColor: '#FB7299',
enabled: true,
order: 4,
link: WEB_LINKS.BILIBILI
}
],
// 快速链接
quickLinks: [
{
name: '登录',
enabled: true,
order: 1,
routeName: 'Login'
},
{
name: '注册',
enabled: true,
order: 2,
routeName: 'Login'
},
{
name: '忘记密码',
enabled: true,
order: 3,
routeName: 'Login'
},
{
name: '个人中心',
enabled: true,
order: 4,
routeName: 'Profile'
}
]
}
export default Object.freeze(fastEnterConfig)
`;
await fs.writeFile(fastEnterPath, cleanedFastEnter, "utf-8");
console.log(` ${icons.success} ${fmt.success("清理快速入口配置完成")}`);
} catch (err) {
console.log(` ${icons.error} ${fmt.error("清理快速入口配置失败")}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
}
}
// 更新菜单接口
async function updateMenuApi() {
const apiPath = path.resolve(process.cwd(), "src/api/system-manage.ts");
try {
const content = await fs.readFile(apiPath, "utf-8");
const updatedContent = content.replace(
"url: '/api/v3/system/menus'",
"url: '/api/v3/system/menus/simple'"
);
await fs.writeFile(apiPath, updatedContent, "utf-8");
console.log(` ${icons.success} ${fmt.success("更新菜单接口完成")}`);
} catch (err) {
console.log(` ${icons.error} ${fmt.error("更新菜单接口失败")}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
}
}
// 用户确认函数
async function getUserConfirmation(): Promise<boolean> {
const { createInterface } = await import("readline");
return new Promise((resolve) => {
const rl = createInterface({
input: process.stdin,
output: process.stdout,
});
console.log(
` ${fmt.highlight("请输入")} ${fmt.success("yes")} ${fmt.highlight("确认执行清理操作,或按 Enter 取消")}`
);
console.log();
process.stdout.write(` ${icons.arrow} `);
rl.question("", (answer: string) => {
rl.close();
resolve(answer.toLowerCase().trim() === "yes");
});
});
}
// 显示清理警告
async function showCleanupWarning() {
createCard("安全警告", [
`${fmt.warning("此操作将永久删除以下演示内容,且无法恢复!")}`,
`${fmt.dim("请仔细阅读清理列表,确认后再继续操作")}`,
]);
const cleanupItems = [
{
icon: icons.image,
name: "图片资源",
desc: "演示用的封面图片、3D图片、运维图片等",
color: theme.orange,
},
{
icon: icons.file,
name: "演示页面",
desc: "widgets、article、examples、safeguard 等页面",
color: theme.purple,
},
{
icon: icons.code,
name: "路由模块文件",
desc: "删除演示路由模块只保留核心模块dashboard、system、result、exception",
color: theme.primary,
},
{
icon: icons.link,
name: "路由常量",
desc: "占位路径见 staticRoutes.ts 顶部(脚本不自动覆盖)",
color: theme.info,
},
{
icon: icons.data,
name: "Mock数据",
desc: "演示用的JSON数据、文章列表、评论数据等",
color: theme.success,
},
{
icon: icons.globe,
name: "多语言文件",
desc: "清理中英文语言包中的演示菜单项",
color: theme.warning,
},
{ icon: icons.map, name: "地图组件", desc: "移除art-map-chart地图组件", color: theme.error },
{ icon: icons.chat, name: "评论组件", desc: "移除comment-widget评论组件", color: theme.orange },
{
icon: icons.bolt,
name: "快速入口",
desc: "移除分析页、礼花效果、聊天、更新日志、定价、留言管理等无效项目",
color: theme.purple,
},
];
console.log(` ${fmt.badge("", theme.bgRed)} ${fmt.title("将要清理的内容")}`);
console.log();
cleanupItems.forEach((item, index) => {
console.log(` ${item.color}${theme.reset} ${fmt.highlight(`${index + 1}. ${item.name}`)}`);
console.log(` ${fmt.dim(item.desc)}`);
});
console.log();
console.log(` ${fmt.badge("", theme.bgGreen)} ${fmt.title("保留的功能模块")}`);
console.log();
const preservedModules = [
{ name: "Dashboard", desc: "工作台页面" },
{ name: "System", desc: "系统管理模块" },
{ name: "Result", desc: "结果页面" },
{ name: "Exception", desc: "异常页面" },
{ name: "Auth", desc: "登录注册功能" },
{ name: "Core Components", desc: "核心组件库" },
];
preservedModules.forEach((module) => {
console.log(` ${icons.check} ${fmt.success(module.name)} ${fmt.dim(`- ${module.desc}`)}`);
});
console.log();
createDivider();
console.log();
}
// 显示统计信息
async function showStats() {
const duration = Date.now() - stats.startTime;
const seconds = (duration / 1000).toFixed(2);
console.log();
createCard("清理统计", [
`${fmt.success("成功删除")}: ${fmt.highlight(stats.deletedFiles.toString())} 个文件`,
`${fmt.info("涉及路径")}: ${fmt.highlight(stats.deletedPaths.toString())} 个目录/文件`,
...(stats.failedPaths > 0
? [
`${icons.error} ${fmt.error("删除失败")}: ${fmt.highlight(stats.failedPaths.toString())} 个路径`,
]
: []),
`${fmt.info("耗时")}: ${fmt.highlight(seconds)}`,
]);
}
// 创建成功横幅
function createSuccessBanner() {
console.log();
console.log(
fmt.gradient(" ╔══════════════════════════════════════════════════════════════════╗")
);
console.log(
fmt.gradient(" ║ ║")
);
console.log(
`${icons.star} ${fmt.success("清理完成!项目已准备就绪")} ${icons.rocket}`
);
console.log(
`${fmt.dim("现在可以开始您的开发之旅了!")}`
);
console.log(
fmt.gradient(" ║ ║")
);
console.log(
fmt.gradient(" ╚══════════════════════════════════════════════════════════════════╝")
);
console.log();
}
// 主函数
async function main() {
// 清屏并显示横幅
console.clear();
createModernBanner();
// 显示清理警告
await showCleanupWarning();
// 统计文件数量
console.log(` ${fmt.info("正在统计文件数量...")}`);
stats.totalFiles = await countAllFiles();
console.log(` ${fmt.info("即将清理")}: ${fmt.highlight(stats.totalFiles.toString())} 个文件`);
console.log(` ${fmt.dim(`涉及 ${targets.length} 个目录/文件路径`)}`);
console.log();
// 用户确认
const confirmed = await getUserConfirmation();
if (!confirmed) {
console.log(` ${fmt.warning("操作已取消,清理中止")}`);
console.log();
return;
}
console.log();
console.log(` ${icons.check} ${fmt.success("确认成功,开始清理...")}`);
console.log();
// 开始清理过程
console.log(` ${fmt.badge("步骤 1/6", theme.bgBlue)} ${fmt.title("删除演示文件")}`);
console.log();
for (let i = 0; i < targets.length; i++) {
await remove(targets[i], i);
}
console.log();
console.log(` ${fmt.badge("步骤 2/6", theme.bgBlue)} ${fmt.title("清理路由模块")}`);
console.log();
await cleanRouteModules();
console.log();
console.log(` ${fmt.badge("步骤 3/6", theme.bgBlue)} ${fmt.title("重写路由常量")}`);
console.log();
await cleanRouterConstants();
console.log();
console.log(` ${fmt.badge("步骤 4/6", theme.bgBlue)} ${fmt.title("清空变更日志")}`);
console.log();
await cleanChangeLog();
console.log();
console.log(` ${fmt.badge("步骤 5/6", theme.bgBlue)} ${fmt.title("清理语言文件")}`);
console.log();
await cleanLanguageFiles();
console.log();
console.log(` ${fmt.badge("步骤 6/7", theme.bgBlue)} ${fmt.title("清理快速入口")}`);
console.log();
await cleanFastEnterComponent();
console.log();
console.log(` ${fmt.badge("步骤 7/7", theme.bgBlue)} ${fmt.title("更新菜单接口")}`);
console.log();
await updateMenuApi();
// 显示统计信息
await showStats();
// 显示成功横幅
createSuccessBanner();
}
main().catch((err) => {
console.log();
console.log(` ${icons.error} ${fmt.error("清理脚本执行出错")}`);
console.log(` ${fmt.dim("错误详情: " + err)}`);
console.log();
process.exit(1);
});

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -e
JOB_NAME=${JOB_NAME:-csg-web}
SERVICE_NAME=${SERVICE_NAME:-csg_web}
COMPOSE_FILE=${COMPOSE_FILE:-../docker-compose.yml}
echo "[deploy] image: ${JOB_NAME}"
echo "[deploy] service: ${SERVICE_NAME}"
echo "[deploy] compose: ${COMPOSE_FILE}"
docker stop "${SERVICE_NAME}" || true
docker rm "${SERVICE_NAME}" || true
docker rmi "${JOB_NAME}" || true
docker build -t "${JOB_NAME}" .
if docker compose version >/dev/null 2>&1; then
IMAGE_NAME="${JOB_NAME}" docker compose -f "${COMPOSE_FILE}" up -d "${SERVICE_NAME}"
else
IMAGE_NAME="${JOB_NAME}" docker-compose -f "${COMPOSE_FILE}" up -d "${SERVICE_NAME}"
fi
echo "[deploy] ${SERVICE_NAME} deploy done"

@ -0,0 +1,110 @@
<template>
<ElConfigProvider
:size="size"
:locale="locale"
:z-index="3000"
:card="{
shadow: 'never',
}"
>
<ElWatermark
:font="{ color: fontColor }"
:content="showWatermark ? watermarkContent : ''"
:z-index="9999"
class="wh-full"
>
<RouterView></RouterView>
<!-- AI 助手 -->
<AiAssistant v-if="enableAiAssistant" />
</ElWatermark>
</ElConfigProvider>
</template>
<script setup lang="ts">
import { computed, onBeforeMount, onMounted, onUnmounted } from "vue";
import { useWindowSize } from "@vueuse/core";
import { useAppStore, useUserStore } from "./store";
import { useSettingsStore } from "./store/modules/setting.store";
import { defaultSettings } from "./config/setting";
import { ComponentSize } from "./enums/settings/layout.enum";
import { MOBILE_BREAKPOINT } from "./utils/constants/definitions";
import AiAssistant from "./components/others/fa-ai-assistant/index.vue";
import { hexToRgba, toggleTransition } from "./utils/ui";
import { initializeTheme } from "./hooks/core/useTheme";
import { useAppBootstrap } from "@/hooks/core/useAppBootstrap";
import { ThemeMode } from "./enums";
import en from "element-plus/es/locale/lang/en";
import zhCn from "element-plus/es/locale/lang/zh-cn";
import { router } from "@/router";
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const userStore = useUserStore();
const { width } = useWindowSize();
// H5
const size = computed(() => {
if (width.value < MOBILE_BREAKPOINT) return "small" as ComponentSize;
return appStore.size as ComponentSize;
});
const showWatermark = computed(() => settingsStore.showWatermark);
const watermarkContent = defaultSettings.watermarkContent;
//
const locale = computed(() => {
return appStore.language === "en" ? en : zhCn;
});
// AI
const enableAiAssistant = computed(() => {
const isEnabled = settingsStore.userEnableAi;
const isLoggedIn = userStore.basicInfo && Object.keys(userStore.basicInfo).length > 0;
return isEnabled && isLoggedIn;
});
// 使
const fontColor = computed(() => {
const hex = settingsStore.themeColor || defaultSettings.themeColor;
const alpha = settingsStore.theme === ThemeMode.DARK ? 0.22 : 0.16;
try {
return hexToRgba(hex, alpha).rgba;
} catch {
return hexToRgba(defaultSettings.themeColor, alpha).rgba;
}
});
/**
* 应用根组件生命周期
*
* onBeforeMount
* 1. toggleTransition(true) 临时禁用页面过渡避免主题切换时的闪烁
* 2. initializeTheme() 加载主题配色(CSS 变量)暗色模式 classauto 监听
*
* onMounted
* 1. bootstrap() 存储检查 过渡恢复 版本升级 站点配置
* 2. 监听 "app:storage-invalidated" 事件 存储异常时由 storage 模块派发
*/
onBeforeMount(() => {
toggleTransition(true);
initializeTheme();
});
// storage detect
const handleStorageInvalidated = () => {
router.push({ name: "Login" });
};
const { bootstrap } = useAppBootstrap();
onMounted(() => {
bootstrap();
//
window.addEventListener("app:storage-invalidated", handleStorageInvalidated);
});
onUnmounted(() => {
window.removeEventListener("app:storage-invalidated", handleStorageInvalidated);
});
</script>

@ -0,0 +1,34 @@
/**
*
*
*
* const enumThemeMode/DeviceEnum/LayoutMode/ResultEnum
* TypeScript isolatedModules 访
* MenuTypeEnum enum enum
*/
import { describe, it, expect } from "vitest";
// ══════════════════ 菜单类型枚举 ══════════════════
describe("MenuTypeEnum — 菜单类型", () => {
it("should define 4 menu types with correct values", async () => {
const { MenuTypeEnum } = await import("@/enums/system/menu.enum");
expect(MenuTypeEnum.CATALOG).toBe(1);
expect(MenuTypeEnum.MENU).toBe(2);
expect(MenuTypeEnum.BUTTON).toBe(3);
expect(MenuTypeEnum.EXTLINK).toBe(4);
});
});
// ══════════════════ 代码生成枚举 ══════════════════
describe("代码生成枚举 — 完整性", () => {
it("FormRuleType should be importable", async () => {
const mod = await import("@/enums/codegen/form.enum");
expect(mod).toBeDefined();
});
it("QueryRuleType should be importable", async () => {
const mod = await import("@/enums/codegen/query.enum");
expect(mod).toBeDefined();
});
});

@ -0,0 +1,198 @@
import { request } from "@utils";
const API_PATH = "/ai/chat";
/** 会话分页列表查询(列表接口) */
export interface ChatSessionListQuery extends PageQuery {
title?: string;
created_at?: string[];
updated_at?: string[];
}
export const AiChatAPI = {
getSessionList(query: ChatSessionListQuery) {
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",
});
},
// ============ AI 模型配置 ============ //
getModelConfig() {
return request<ApiResponse<AiModelConfigList>>({
url: `${API_PATH}/model`,
method: "get",
});
},
createModelConfig(body: AiModelConfigInput) {
return request<ApiResponse<AiModelConfigItem>>({
url: `${API_PATH}/model`,
method: "post",
data: body,
});
},
updateModelConfig(id: string, body: AiModelConfigInput) {
return request<ApiResponse<AiModelConfigItem>>({
url: `${API_PATH}/model/${id}`,
method: "put",
data: body,
});
},
deleteModelConfig(id: string) {
return request<ApiResponse<null>>({
url: `${API_PATH}/model/${id}`,
method: "delete",
});
},
activateModelConfig(id: string) {
return request<ApiResponse<null>>({
url: `${API_PATH}/model/${id || "__default__"}/activate`,
method: "post",
});
},
};
export interface AiModelConfigInput {
name: string;
base_url: string;
api_key: string;
model_id: string;
temperature: number;
}
export interface AiModelConfigItem extends AiModelConfigInput {
id: string;
created_time: string | null;
}
export interface AiModelConfigList {
items: AiModelConfigItem[];
active_id: string | null;
}
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;
action: 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,117 @@
import { request } from "@utils";
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, UserByQueryParams, TenantByQueryParams {
name?: string;
status?: number;
}
export interface DemoTable extends BaseType {
name?: string;
status?: number;
description?: string;
int_val?: number;
bigint_val?: number;
float_val?: number;
bool_val?: boolean;
date_val?: string;
time_val?: string;
datetime_val?: string;
text_val?: string;
json_val?: Record<string, any>;
}
export interface DemoForm extends BaseFormType {
name?: string;
status?: number;
description?: string;
int_val?: number;
bigint_val?: number;
float_val?: number;
bool_val?: boolean;
date_val?: string;
time_val?: Date | string;
datetime_val?: Date | string;
text_val?: string;
json_val?: Record<string, any>;
}

@ -0,0 +1,204 @@
import { request } from "@utils";
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;
status?: number;
}
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;
status?: number;
description?: string;
}
/** 查询参数:生成表 */
export interface GenTablePageQuery extends PageQuery, UserByQueryParams {
table_name?: string;
table_comment?: string;
status?: number;
}
/** 查询参数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[];
changed: GenSyncColumnChange[];
unchanged: number;
sub_table_name?: string;
sub?: GenSyncPreviewSchema;
}

@ -0,0 +1,95 @@
import { request } from "@utils";
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,54 @@
import { request } from "@utils";
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, UserByQueryParams, TenantByQueryParams {
ipaddr?: string;
name?: string;
login_location?: string;
}
export interface OnlineUserTable {
session_id: string;
user_id: number;
tenant_id?: number;
is_superuser?: boolean;
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";
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";
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_ip: string;
computer_name: string;
os_arch: string;
os_name: 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 {
dir_name: string;
sys_type_name: string;
type_name: 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,238 @@
import { request } from "@utils";
const API_PATH = "/platform/email";
const EmailAPI = {
// ─── SMTP 配置 ────────────────────────────────────────────
listConfig(query?: EmailConfigPageQuery) {
return request<ApiResponse<PageResult<EmailConfigTable>>>({
url: `${API_PATH}/config/list`,
method: "get",
params: query,
});
},
detailConfig(id: number) {
return request<ApiResponse<EmailConfigTable>>({
url: `${API_PATH}/config/detail/${id}`,
method: "get",
});
},
createConfig(body: EmailConfigCreateForm) {
return request<ApiResponse>({
url: `${API_PATH}/config/create`,
method: "post",
data: body,
});
},
updateConfig(id: number, body: EmailConfigUpdateForm) {
return request<ApiResponse>({
url: `${API_PATH}/config/update/${id}`,
method: "put",
data: body,
});
},
deleteConfig(ids: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/config/delete`,
method: "delete",
data: ids,
});
},
testConfig(body: EmailTestForm) {
return request<ApiResponse>({
url: `${API_PATH}/config/test`,
method: "post",
data: body,
});
},
// ─── 邮件模板 ────────────────────────────────────────────
listTemplate(query?: EmailTemplatePageQuery) {
return request<ApiResponse<PageResult<EmailTemplateTable>>>({
url: `${API_PATH}/template/list`,
method: "get",
params: query,
});
},
detailTemplate(id: number) {
return request<ApiResponse<EmailTemplateTable>>({
url: `${API_PATH}/template/detail/${id}`,
method: "get",
});
},
createTemplate(body: EmailTemplateCreateForm) {
return request<ApiResponse>({
url: `${API_PATH}/template/create`,
method: "post",
data: body,
});
},
updateTemplate(id: number, body: EmailTemplateUpdateForm) {
return request<ApiResponse>({
url: `${API_PATH}/template/update/${id}`,
method: "put",
data: body,
});
},
deleteTemplate(ids: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/template/delete`,
method: "delete",
data: ids,
});
},
// ─── 手动发送 ────────────────────────────────────────────
sendEmail(body: EmailSendForm) {
return request<ApiResponse>({
url: `${API_PATH}/send`,
method: "post",
data: body,
});
},
// ─── 发送日志 ────────────────────────────────────────────
listLog(query?: EmailLogPageQuery) {
return request<ApiResponse<PageResult<EmailLogTable>>>({
url: `${API_PATH}/log/list`,
method: "get",
params: query,
});
},
};
export default EmailAPI;
// ─── EmailConfig 类型 ──────────────────────────────────────
export interface EmailConfigPageQuery extends PageQuery, UserByQueryParams {
name?: string;
is_default?: boolean;
}
export interface EmailConfigTable extends BaseType {
name: string;
smtp_host: string;
smtp_port: number;
smtp_user: string;
from_name: string;
use_tls: boolean;
is_default: boolean;
timeout: number;
status?: number;
description?: string;
}
export interface EmailConfigCreateForm {
name: string;
smtp_host: string;
smtp_port: number;
smtp_user: string;
smtp_password: string;
from_name?: string;
use_tls?: boolean;
is_default?: boolean;
timeout?: number;
status?: number;
description?: string;
}
export interface EmailConfigUpdateForm {
name?: string;
smtp_host?: string;
smtp_port?: number;
smtp_user?: string;
smtp_password?: string;
from_name?: string;
use_tls?: boolean;
is_default?: boolean;
timeout?: number;
status?: number;
description?: string;
}
export interface EmailTestForm {
config_id: number;
to_email: string;
}
// ─── EmailTemplate 类型 ──────────────────────────────────────
export interface EmailTemplatePageQuery extends PageQuery, UserByQueryParams {
name?: string;
template_code?: string;
}
export interface EmailTemplateTable extends BaseType {
name: string;
template_code: string;
subject: string;
body_html: string;
body_text?: string;
variables?: string;
status?: number;
description?: string;
}
export interface EmailTemplateCreateForm {
name: string;
template_code: string;
subject: string;
body_html: string;
body_text?: string;
variables?: string;
description?: string;
}
export interface EmailTemplateUpdateForm {
name?: string;
template_code?: string;
subject?: string;
body_html?: string;
body_text?: string;
variables?: string;
description?: string;
}
// ─── EmailLog 类型 ──────────────────────────────────────────
export interface EmailLogPageQuery extends PageQuery, UserByQueryParams {
to_email?: string;
biz_type?: string;
template_code?: string;
status?: number;
}
export interface EmailLogTable {
id: number;
config_id?: number;
template_code?: string;
to_email: string;
to_name?: string;
subject: string;
biz_type: string;
status: number;
error_msg?: string;
retry_count: number;
tenant_id?: number;
created_time?: string;
sent_time?: string;
}
export interface EmailSendForm {
to_email: string;
to_name?: string;
template_code: string;
variables?: Record<string, unknown>;
config_id?: number;
biz_type?: string;
}

@ -0,0 +1,92 @@
import { request } from "@utils";
const PLATFORM_API = "/platform/invoice";
const TENANT_API = "/platform/tenant/invoice";
const InvoiceAPI = {
// ─── 平台端 ───
listInvoices(query?: InvoicePageQuery) {
return request<ApiResponse<{ list: InvoiceTable[]; total: number }>>({
url: `${PLATFORM_API}/list`,
method: "get",
params: query,
});
},
issueInvoice(invoiceId: number, body: { pdf_url?: string; api_response?: string }) {
return request<ApiResponse>({
url: `${PLATFORM_API}/issue/${invoiceId}`,
method: "put",
data: body,
});
},
voidInvoice(invoiceId: number, reason?: string) {
return request<ApiResponse>({
url: `${PLATFORM_API}/void/${invoiceId}`,
method: "put",
data: { description: reason },
});
},
// ─── 租户端 ───
tenantListInvoices(query?: InvoicePageQuery) {
return request<ApiResponse<{ list: InvoiceTable[]; total: number }>>({
url: `${TENANT_API}/list`,
method: "get",
params: query,
});
},
applyInvoice(body: InvoiceApplyForm) {
return request<ApiResponse>({
url: `${TENANT_API}/apply`,
method: "post",
data: body,
});
},
downloadInvoice(invoiceId: number) {
return request<Blob>({
url: `${TENANT_API}/${invoiceId}/download`,
method: "get",
responseType: "blob",
});
},
};
export default InvoiceAPI;
export interface InvoicePageQuery extends PageQuery, TenantByQueryParams {
invoice_type?: string;
}
export interface InvoiceTable {
id: number;
tenant_id: number;
order_id: number;
invoice_no: string;
invoice_type: string;
title: string;
tax_no?: string;
amount: number;
tax_amount: number;
status: number;
bank_info?: string;
address_info?: string;
pdf_url?: string;
oss_license_pdf_url?: string;
api_response?: string;
description?: string;
created_time?: string;
}
export interface InvoiceApplyForm {
order_id: number;
invoice_type: string;
title: string;
tax_no?: string;
address_info?: string;
bank_info?: string;
description?: string;
}

@ -0,0 +1,131 @@
import { request } from "@utils";
const API_PATH = "/platform/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}/status/batch`,
method: "patch",
data: body,
});
},
};
export default MenuAPI;
export interface MenuPageQuery extends BaseQueryParams {
name?: string;
menu_client?: "pc" | "app";
status?: number;
type?: number;
permission?: string;
route_path?: string;
component_path?: string;
description?: string;
scope?: "platform" | "tenant";
}
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";
link?: string;
is_iframe?: boolean;
is_hide_tab?: boolean;
active_path?: string;
show_badge?: boolean;
show_text_badge?: string;
scope?: "platform" | "tenant";
status?: number;
description?: string;
}
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";
link?: string;
is_iframe?: boolean;
is_hide_tab?: boolean;
active_path?: string;
show_badge?: boolean;
show_text_badge?: string;
scope?: "platform" | "tenant";
status?: number;
description?: string;
}
export interface KeyValue {
key: string;
value: string;
}

@ -0,0 +1,188 @@
import { request } from "@utils";
// ─── 平台订单 ──────────────────────────────────────────
const ORDER_API = "/platform/order";
const PAYMENT_API = "/platform/payment";
const REFUND_API = "/platform/refund";
const TENANT_ORDER_API = "/tenant/order";
const OrderAPI = {
// ─── 订单管理 ───
listOrders(query?: OrderPageQuery) {
return request<ApiResponse<{ items: OrderTable[]; total: number }>>({
url: `${ORDER_API}/list`,
method: "get",
params: query,
});
},
detailOrder(orderId: number) {
return request<ApiResponse<OrderTable>>({
url: `${ORDER_API}/detail/${orderId}`,
method: "get",
});
},
createOrder(body: OrderCreateForm) {
return request<ApiResponse<{ id: number; order_no: string }>>({
url: `${ORDER_API}/create`,
method: "post",
data: body,
});
},
cancelOrder(orderId: number) {
return request<ApiResponse<{ message: string }>>({
url: `${ORDER_API}/cancel/${orderId}`,
method: "post",
});
},
// ─── 支付操作 ───
payOrder(orderId: number, body?: { pay_method?: string }) {
return request<
ApiResponse<{ order_no: string; amount: number; qr_code_url?: string; pay_url?: string }>
>({
url: `${PAYMENT_API}/pay/${orderId}`,
method: "post",
data: body,
});
},
queryPaymentStatus(orderId: number) {
return request<ApiResponse<{ paid: boolean; order_no?: string; amount?: number }>>({
url: `${PAYMENT_API}/status/${orderId}`,
method: "get",
});
},
mockPaymentCallback(orderId: number) {
return request<ApiResponse>({
url: `${PAYMENT_API}/mock/callback`,
method: "post",
data: { order_id: orderId },
});
},
// ─── 支付记录 ───
listPaymentRecords(query?: PageQuery) {
return request<ApiResponse<{ items: PaymentRecordTable[]; total: number }>>({
url: `${PAYMENT_API}/record/list`,
method: "get",
params: query,
});
},
// ─── 退款管理 ───
listRefunds(query?: RefundPageQuery) {
return request<ApiResponse<{ items: RefundTable[]; total: number }>>({
url: `${REFUND_API}/list`,
method: "get",
params: query,
});
},
approveRefund(refundId: number) {
return request<ApiResponse<{ message: string }>>({
url: `${REFUND_API}/approve/${refundId}`,
method: "put",
});
},
rejectRefund(refundId: number, body: { reject_reason?: string }) {
return request<ApiResponse<{ message: string }>>({
url: `${REFUND_API}/reject/${refundId}`,
method: "put",
data: body,
});
},
// ─── 租户端 ───
tenantCreateOrder(body: {
tenant_id: number;
package_id?: number;
plugin_id?: number;
order_type: string;
pay_method?: string;
}) {
return request<ApiResponse<{ id: number; order_no: string }>>({
url: `${TENANT_ORDER_API}/create`,
method: "post",
data: body,
});
},
tenantApplyRefund(orderId: number, body: { reason: string }) {
return request<ApiResponse<{ id: number }>>({
url: `${TENANT_ORDER_API}/refund/apply/${orderId}`,
method: "post",
data: body,
});
},
};
export default OrderAPI;
// ─── Order 类型 ──────────────────────────────────────────
export interface OrderPageQuery extends PageQuery, TenantByQueryParams {
order_type?: string;
status?: number;
}
export interface OrderTable {
id: number;
order_no: string;
tenant_id: number;
package_id?: number;
plugin_id?: number;
order_type: string;
amount: number;
period_count: number;
status: number;
pay_method?: string;
pay_time?: string;
expire_time: string;
created_time?: string;
}
export interface OrderCreateForm {
tenant_id: number;
package_id?: number;
plugin_id?: number;
order_type: "new" | "renew" | "upgrade" | "downgrade" | "plugin";
pay_method?: string;
}
// ─── Payment 类型 ────────────────────────────────────────
export interface PaymentRecordTable {
id: number;
order_id: number;
transaction_id?: string;
pay_method: string;
amount: number;
status: number;
pay_time?: string;
created_time?: string;
}
// ─── Refund 类型 ─────────────────────────────────────────
export interface RefundPageQuery extends PageQuery, UserByQueryParams, TenantByQueryParams {
status?: number;
}
export interface RefundTable {
id: number;
order_id: number;
refund_no: string;
amount: number;
reason: string;
status: number;
refund_transaction_id?: string;
reviewer_id?: number;
review_time?: string;
reject_reason?: string;
created_time?: string;
}

@ -0,0 +1,154 @@
import { request } from "@utils";
const API_PATH = "/platform/package";
const PackageAPI = {
listPackage(query?: PackagePageQuery) {
return request<ApiResponse<PageResult<PackageTable>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailPackage(id: number) {
return request<ApiResponse<PackageTable>>({
url: `${API_PATH}/detail/${id}`,
method: "get",
});
},
createPackage(body: PackageCreateForm) {
return request<ApiResponse<PackageTable>>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
updatePackage(id: number, body: PackageUpdateForm) {
return request<ApiResponse<PackageTable>>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
deletePackage(body: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: body,
});
},
batchPackageStatus(body: { ids: number[]; status: number }) {
return request<ApiResponse>({
url: `${API_PATH}/status/batch`,
method: "patch",
data: body,
});
},
getPackageMenus(packageId: number) {
return request<ApiResponse<number[]>>({
url: `${API_PATH}/menus/${packageId}`,
method: "get",
});
},
setPackageMenus(packageId: number, menuIds: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/menus/${packageId}/set`,
method: "post",
data: { menu_ids: menuIds },
});
},
getPackagePlugins(packageId: number) {
return request<ApiResponse<number[]>>({
url: `${API_PATH}/plugins/${packageId}`,
method: "get",
});
},
setPackagePlugins(packageId: number, pluginIds: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/plugins/${packageId}/set`,
method: "post",
data: { plugin_ids: pluginIds },
});
},
};
export default PackageAPI;
export interface PackagePageQuery extends PageQuery, UserByQueryParams, TenantByQueryParams {
name?: string;
code?: string;
status?: number;
}
export interface PackageTable extends BaseType {
name: string;
code: string;
sort: number;
price: number;
period: string;
trial_days: number;
max_users: number;
max_roles: number;
max_depts: number;
max_storage_mb: number;
rate_limit: number;
status?: number;
description?: string;
}
export interface PackageForm extends BaseFormType {
name?: string;
code?: string;
sort?: number;
price?: number;
period?: string;
trial_days?: number;
max_users?: number;
max_roles?: number;
max_depts?: number;
max_storage_mb?: number;
rate_limit?: number;
status?: number;
description?: string;
}
export interface PackageCreateForm {
name: string;
code: string;
status?: number;
sort?: number;
description?: string;
price?: number;
period?: string;
trial_days?: number;
max_users?: number;
max_roles?: number;
max_depts?: number;
max_storage_mb?: number;
rate_limit?: number;
}
export interface PackageUpdateForm {
name?: string;
code?: string;
status?: number;
sort?: number;
description?: string;
price?: number;
period?: string;
trial_days?: number;
max_users?: number;
max_roles?: number;
max_depts?: number;
max_storage_mb?: number;
rate_limit?: number;
}

@ -0,0 +1,142 @@
import { request } from "@utils";
const API_PATH = "/platform/plugin";
const PluginAPI = {
// 超管:插件列表
list(page: PageQuery, search?: PluginQueryParam) {
return request<ApiResponse<PageResult<PluginTable>>>({
url: `${API_PATH}/list`,
method: "get",
params: { ...page, ...search },
});
},
// 超管:插件详情
detail(id: number) {
return request<ApiResponse<PluginTable>>({
url: `${API_PATH}/detail/${id}`,
method: "get",
});
},
// 超管:创建插件
create(body: PluginForm) {
return request<ApiResponse<PluginTable>>({
url: `${API_PATH}/create`,
method: "post",
data: body,
});
},
// 超管:更新插件
update(id: number, body: PluginForm) {
return request<ApiResponse<PluginTable>>({
url: `${API_PATH}/update/${id}`,
method: "put",
data: body,
});
},
// 超管:删除插件
delete(ids: number[]) {
return request<ApiResponse>({
url: `${API_PATH}/delete`,
method: "delete",
data: ids,
});
},
// 租户:插件市场
marketplace(page: PageQuery, category?: string) {
return request<ApiResponse<PageResult<PluginTable>>>({
url: `${API_PATH}/marketplace`,
method: "get",
params: { ...page, category },
});
},
// 租户:安装插件
install(pluginId: number) {
return request<ApiResponse>({
url: `${API_PATH}/install`,
method: "post",
data: { plugin_id: pluginId },
});
},
// 租户:卸载插件
uninstall(pluginId: number) {
return request<ApiResponse>({
url: `${API_PATH}/uninstall`,
method: "post",
data: { plugin_id: pluginId },
});
},
// 租户:启用/禁用插件
toggle(pluginId: number) {
return request<ApiResponse>({
url: `${API_PATH}/toggle`,
method: "post",
data: { plugin_id: pluginId },
});
},
// 租户:我的插件
myPlugins() {
return request<ApiResponse<PluginTable[]>>({
url: `${API_PATH}/my`,
method: "get",
});
},
// 超管:热重载插件路由
reload() {
return request<ApiResponse<string>>({
url: `${API_PATH}/reload`,
method: "post",
});
},
};
export default PluginAPI;
export interface PluginQueryParam extends PageQuery, UserByQueryParams, TenantByQueryParams {
name?: string;
category?: string;
status?: number;
}
export interface PluginTable extends BaseType {
name: string;
code: string;
category?: string;
icon?: string;
version?: string;
price?: number;
installed?: boolean;
author?: string;
menu_path?: string;
permission_prefix?: string;
dependencies?: string;
sort?: number;
status?: number;
description?: string;
}
export interface PluginForm extends BaseFormType {
name?: string;
code?: string;
category?: string;
icon?: string;
version?: string;
price?: number;
sort?: number;
author?: string;
menu_path?: string;
permission_prefix?: string;
dependencies?: string;
status?: number;
description?: string;
}

@ -0,0 +1,152 @@
import { request } from "@utils";
const API_PATH = "/platform/tenant";
const SelfServiceAPI = {
/** 可选购的套餐列表 */
getAvailablePackages() {
return request<ApiResponse<{ packages: AvailablePackage[] }>>({
url: `${API_PATH}/package/available`,
method: "get",
});
},
/** 套餐变更预览 */
previewPackageChange(packageId: number) {
return request<ApiResponse<PackageChangePreview>>({
url: `${API_PATH}/package/preview`,
method: "get",
params: { target_package_id: packageId },
});
},
/** 创建自助订单 */
createOrder(body: SelfServiceOrderForm) {
return request<ApiResponse<{ order_id: number; amount: number }>>({
url: `${API_PATH}/order/create`,
method: "post",
data: body,
});
},
/** 我的订单列表 */
listMyOrders(query?: PageQuery) {
return request<ApiResponse<PageResult<SelfServiceOrderItem>>>({
url: `${API_PATH}/order/list`,
method: "get",
params: query,
});
},
/** 订单详情 */
detailMyOrder(orderId: number) {
return request<ApiResponse<SelfServiceOrderItem>>({
url: `${API_PATH}/order/detail/${orderId}`,
method: "get",
});
},
/** 租户工作台概览 */
getWorkspace() {
return request<ApiResponse<WorkspaceData>>({
url: `${API_PATH}/workspace`,
method: "get",
});
},
/** 购买付费插件 */
purchasePlugin(body: { plugin_id: number; pay_method?: string }) {
return request<ApiResponse<{ id: number; order_no: string }>>({
url: `${API_PATH}/plugin/purchase`,
method: "post",
data: body,
});
},
};
export default SelfServiceAPI;
export interface AvailablePackage {
id: number;
name: string;
price: number;
period: string;
trial_days: number;
max_users: number;
max_roles: number;
max_depts: number;
max_storage_mb: number;
description: string | null;
is_current: boolean;
available_actions: string[];
}
export interface PackageChangePreview {
current_package: string;
target_package: string;
action: string;
amount: number;
period: string;
gained_menus: { id: number; name: string; path: string }[];
lost_menus: { id: number; name: string; path: string }[];
affected_roles: string[];
affected_users: number;
}
export interface SelfServiceOrderForm {
package_id: number;
order_type: "buy" | "renew" | "upgrade" | "downgrade";
pay_method?: string;
}
export interface SelfServiceOrderItem {
id: number;
order_no: string;
package_name: string;
order_type: string;
amount: number;
status: number;
pay_method?: string;
pay_time?: string;
created_at: string;
}
export interface WorkspaceData {
tenant: {
id: number;
name: string;
code: string;
status: number;
status_label: string;
start_time: string | null;
end_time: string | null;
days_remaining: number;
};
package: {
id: number;
name: string;
price: number;
period: string;
} | null;
quota: {
max_users: number;
max_roles: number;
max_depts: number;
current_users: number;
current_roles: number;
current_depts: number;
usage_percent: {
users: number;
roles: number;
depts: number;
};
};
recent_orders: {
id: number;
order_no: string;
amount: number;
order_type: string;
status: number;
created_at: string | null;
}[];
}

@ -0,0 +1,275 @@
import { request, NO_AUTH_FLAG } from "@utils";
const API_PATH = "/platform/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,
});
},
/** 批量修改租户状态 */
batchTenantStatus(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/status/batch`,
method: "patch",
data: body,
});
},
/** 切换单个租户启用/禁用状态 */
toggleTenantStatus(id: number) {
return request<ApiResponse>({
url: `${API_PATH}/status/${id}`,
method: "put",
});
},
/** 租户续期 */
renewTenant(id: number, body: { end_time: string }) {
return request<ApiResponse<TenantTable>>({
url: `${API_PATH}/renew/${id}`,
method: "put",
data: body,
});
},
/** 套餐变更影响预览 */
getPackageChangePreview(tenantId: number, newPackageId: number) {
return request<ApiResponse<PackageChangePreview>>({
url: `${API_PATH}/${tenantId}/package-change-preview`,
method: "get",
params: { new_package_id: newPackageId },
});
},
getTenantUsers(tenantId: number) {
return request<ApiResponse<TenantUser[]>>({
url: `${API_PATH}/${tenantId}/users`,
method: "get",
});
},
addTenantUser(tenantId: number, body: TenantUserAddForm) {
return request<ApiResponse>({
url: `${API_PATH}/${tenantId}/users`,
method: "post",
data: body,
});
},
removeTenantUser(tenantId: number, userId: number) {
return request<ApiResponse>({
url: `${API_PATH}/${tenantId}/users/${userId}`,
method: "delete",
});
},
/** 公开接口:无需登录即可获取租户配置(用于登录页等场景) */
getTenantConfigInfo(tenantId: number) {
return request<ApiResponse<TenantConfigItem[]>>({
url: `${API_PATH}/${tenantId}/config/info`,
method: "get",
headers: {
Authorization: NO_AUTH_FLAG,
},
});
},
/** 获取租户个性化配置 */
getTenantConfig(tenantId: number) {
return request<ApiResponse<TenantConfigItem[]>>({
url: `${API_PATH}/${tenantId}/config`,
method: "get",
});
},
/** 批量更新租户个性化配置 */
updateTenantConfig(tenantId: number, body: TenantConfigItem[]) {
return request<ApiResponse<TenantConfigItem[]>>({
url: `${API_PATH}/${tenantId}/config`,
method: "put",
data: body,
});
},
};
export default TenantAPI;
export interface TenantPageQuery extends PageQuery, UserByQueryParams, TenantByQueryParams {
name?: string;
code?: string;
status?: number;
}
export interface TenantTable extends BaseType {
name: string;
code: string;
package_id?: number;
start_time?: string;
end_time?: string;
contact_name?: string;
contact_phone?: string;
contact_email?: string;
address?: string;
domain?: string;
logo_url?: string;
sort?: number;
version?: string;
favicon?: string;
login_bg?: string;
copyright?: string;
keep_record?: string;
help_doc?: string;
privacy?: string;
clause?: string;
git_code?: string;
status?: number;
description?: string;
}
export interface TenantForm extends BaseFormType {
name?: string;
code?: string;
package_id?: number;
start_time?: string;
end_time?: string;
contact_name?: string;
contact_phone?: string;
contact_email?: string;
address?: string;
domain?: string;
logo_url?: string;
sort?: number;
version?: string;
favicon?: string;
login_bg?: string;
copyright?: string;
keep_record?: string;
help_doc?: string;
privacy?: string;
clause?: string;
git_code?: string;
status?: number;
description?: string;
}
export interface TenantCreateForm extends BaseFormType {
name: string;
code: string;
package_id?: number;
start_time?: string;
end_time?: string;
contact_name?: string;
contact_phone?: string;
contact_email?: string;
address?: string;
domain?: string;
logo_url?: string;
sort?: number;
version?: string;
favicon?: string;
login_bg?: string;
copyright?: string;
keep_record?: string;
help_doc?: string;
privacy?: string;
clause?: string;
git_code?: string;
status?: number;
description?: string;
}
export interface TenantUpdateForm extends BaseFormType {
name?: string;
code?: string;
package_id?: number;
start_time?: string;
end_time?: string;
contact_name?: string;
contact_phone?: string;
contact_email?: string;
address?: string;
domain?: string;
logo_url?: string;
sort?: number;
version?: string;
favicon?: string;
login_bg?: string;
copyright?: string;
keep_record?: string;
help_doc?: string;
privacy?: string;
clause?: string;
git_code?: string;
status?: number;
description?: string;
}
/** 套餐变更影响预览 */
export interface PackageChangePreview {
new_package_id: number;
new_package_name: string;
affected_roles: Record<string, unknown>[];
removed_menus: Record<string, unknown>[];
added_menus: Record<string, unknown>[];
quota_changes: Record<string, unknown>;
total_affected_users: number;
}
export interface TenantUser {
id: number;
user_id: number;
tenant_id: number;
role: string;
is_default: number;
create_time?: string;
username: string;
name: string;
}
export interface TenantUserAddForm {
user_id: number;
role: string;
is_default: number;
}
/** 租户配置项 */
export interface TenantConfigItem {
config_key: string;
config_value: string | null;
}

@ -0,0 +1,148 @@
import { request } from "@utils";
const API_PATH = "/system/auth";
/** 方案提供方 */
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<JWTOut>>({
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,
});
},
/** 获取当前用户的可选租户列表 */
getTenants() {
return request<ApiResponse<TenantOption[]>>({
url: `${API_PATH}/tenants`,
method: "get",
});
},
/** 选择租户,返回含 tenant_id 的新 JWT */
selectTenant(tenantId: number) {
return request<ApiResponse<SelectTenantResult>>({
url: `${API_PATH}/select-tenant`,
method: "post",
data: { tenant_id: tenantId },
});
},
/** 租户自助注册PRD §4.5 */
tenantRegister(body: TenantRegisterForm) {
return request<ApiResponse<TenantRegisterResult>>({
url: `${API_PATH}/tenant/register`,
method: "post",
data: body,
});
},
};
export default AuthAPI;
export interface TenantRegisterForm {
username: string;
password: string;
email: string;
tenant_name?: string;
}
export interface TenantRegisterResult {
user_id: number;
username: string;
tenant_id: number;
tenant_name: string;
tenant_code: string;
package: string | null;
trial_end: string;
message: string;
}
// ─── Auth 类型定义 ───
/** 登录表单 */
export interface LoginFormData {
username: string;
password: string;
captcha?: string;
captcha_key?: string;
remember?: boolean;
login_type?: string;
}
/** JWT 响应 (JWTOutSchema) */
export interface JWTOut {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
}
/** 登录成功返回 */
export interface LoginResult extends JWTOut {
tenants?: TenantOption[];
}
/** 刷新 Token 请求体 */
export interface RefreshToekenBody {
refresh_token: string;
}
/** 退出登录请求体 */
export interface LogoutBody {
token: string;
}
/** 租户选项 */
export interface TenantOption {
id: number;
name: string;
code: string;
}
/** 选择租户返回 (SelectTenantOutSchema) */
export interface SelectTenantResult {
access_token: string;
token_type: string;
expires_in: number;
}
/** 验证码信息 */
export interface CaptchaInfo {
enable: boolean;
key: string;
img_base: string;
}

@ -0,0 +1,85 @@
import { request } from "@utils";
const API_PATH = "/system/dept";
const DeptAPI = {
listDept(query?: DeptPageQuery) {
return request<ApiResponse<DeptTable[]>>({
url: `${API_PATH}/tree`,
method: "get",
params: query,
});
},
detailDept(id: number) {
return request<ApiResponse<DeptTable>>({
url: `${API_PATH}/detail/${id}`,
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}/status/batch`,
method: "patch",
data: body,
});
},
};
export default DeptAPI;
export interface DeptPageQuery extends UserByQueryParams, TenantByQueryParams {
name?: string;
status?: number;
}
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[];
status?: number;
description?: string;
}
export interface DeptForm extends BaseFormType {
name?: string;
code: string;
leader?: string;
phone?: string;
email?: string;
parent_id?: number;
order?: number;
status?: number;
description?: string;
}

@ -0,0 +1,186 @@
import { request } from "@utils";
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/status/batch`,
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/status/batch`,
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, UserByQueryParams {
dict_name?: string;
dict_type?: string;
status?: number;
}
export interface DictDataPageQuery extends PageQuery, UserByQueryParams {
dict_label?: string;
dict_type?: string;
dict_type_id?: number;
status?: number;
}
export interface DictTable extends BaseType {
dict_name?: string;
dict_type?: string;
status?: number;
description?: string;
}
export interface DictForm extends BaseFormType {
dict_name?: string;
dict_type?: string;
status?: number;
description?: 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;
status?: number;
description?: string;
}
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;
status?: number;
description?: string;
}

@ -0,0 +1,105 @@
import { request } from "@utils";
// ==================== 操作日志 ====================
const OP_API = "/system/log/operation";
const OperationLogAPI = {
list(query?: OperationLogPageQuery) {
return request<ApiResponse<PageResult<OperationLogTable>>>({
url: `${OP_API}/list`,
method: "get",
params: query,
});
},
detail(id: number) {
return request<ApiResponse<OperationLogTable>>({
url: `${OP_API}/detail/${id}`,
method: "get",
});
},
delete(body: number[]) {
return request<ApiResponse>({
url: `${OP_API}/delete`,
method: "delete",
data: body,
});
},
export(query?: OperationLogPageQuery) {
return request<Blob>({
url: `${OP_API}/export`,
method: "post",
data: query,
responseType: "blob",
});
},
};
export default OperationLogAPI;
export interface OperationLogPageQuery extends PageQuery, UserByQueryParams, TenantByQueryParams {
request_path?: string;
creator_name?: string;
status?: number;
}
export interface OperationLogTable {
id: number;
tenant_id: number;
request_path?: string;
request_method?: string;
request_payload?: Record<string, unknown> | string;
response_code?: number;
response_json?: Record<string, unknown> | string;
process_time?: string;
created_time?: string;
}
// ==================== 登录日志 ====================
const LOGIN_API = "/system/log/login";
export const LoginLogAPI = {
list(query?: LoginLogPageQuery) {
return request<ApiResponse<PageResult<LoginLogTable>>>({
url: `${LOGIN_API}/list`,
method: "get",
params: query,
});
},
detail(id: number) {
return request<ApiResponse<LoginLogTable>>({
url: `${LOGIN_API}/detail/${id}`,
method: "get",
});
},
delete(body: number[]) {
return request<ApiResponse>({
url: `${LOGIN_API}/delete`,
method: "delete",
data: body,
});
},
};
export interface LoginLogPageQuery extends PageQuery, UserByQueryParams, TenantByQueryParams {
username?: string;
status?: number;
}
export interface LoginLogTable {
id: number;
username: string;
status: number;
login_ip?: string;
login_location?: string;
request_os?: string;
request_browser?: string;
msg?: string;
created_time?: string;
}

@ -0,0 +1,134 @@
import { request } from "@utils";
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}/status/batch`,
method: "patch",
data: body,
});
},
exportNotice(body: NoticePageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "post",
data: body,
responseType: "blob",
});
},
readNotice(id: number) {
return request<ApiResponse>({
url: `${API_PATH}/read/${id}`,
method: "post",
});
},
readAllNotice() {
return request<ApiResponse>({
url: `${API_PATH}/read-all`,
method: "post",
});
},
getUnreadCount() {
return request<ApiResponse<number>>({
url: `${API_PATH}/unread-count`,
method: "get",
});
},
getNotificationPanel() {
return request<ApiResponse<NotificationPanel>>({
url: `${API_PATH}/panel`,
method: "get",
});
},
};
export default NoticeAPI;
export interface NoticePageQuery extends PageQuery, UserByQueryParams {
notice_title?: string;
notice_type?: string;
status?: number;
}
export interface NoticeTable extends BaseType {
notice_title?: string;
notice_type?: string;
notice_content?: string;
status?: number;
description?: string;
}
export interface NoticeForm extends BaseFormType {
notice_title?: string;
notice_type?: string;
notice_content?: string;
status?: number;
description?: string;
}
export interface NotificationPanelMessage {
id?: number;
title: string;
content?: string;
time: string;
type?: string;
}
export interface NotificationPanel {
notices: NoticeTable[];
messages: NotificationPanelMessage[];
pendings: NotificationPanelMessage[];
}

@ -0,0 +1,108 @@
import { request, NO_AUTH_FLAG } from "@utils";
const API_PATH = "/system/param";
const ParamsAPI = {
uploadFile(body: any) {
return request<ApiResponse<UploadFilePath>>({
url: `/common/file/upload?upload_type=param`,
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,
});
},
batchParams(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/status/batch`,
method: "patch",
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, UserByQueryParams {
config_name?: string;
config_key?: string;
config_type?: boolean;
status?: number;
}
export interface ConfigTable extends BaseType {
config_name?: string;
config_key?: string;
config_value?: string;
config_type?: boolean;
status?: number;
description?: string;
}
export interface ConfigForm extends BaseFormType {
config_name?: string;
config_key?: string;
config_value?: string;
config_type?: boolean;
status?: number;
description?: string;
}

@ -0,0 +1,84 @@
import { request } from "@utils";
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}/status/batch`,
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, UserByQueryParams {
name?: string;
status?: number;
}
export interface PositionTable extends BaseType {
name?: string;
code?: string;
order?: number;
status?: number;
description?: string;
}
export interface PositionForm extends BaseFormType {
name?: string;
code?: string;
order?: number;
status?: number;
description?: string;
}

@ -0,0 +1,120 @@
import { request } from "@utils";
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}/status/batch`,
method: "patch",
data: body,
});
},
setPermission(body: permissionDataType) {
return request<ApiResponse>({
url: `${API_PATH}/permission`,
method: "put",
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, UserByQueryParams {
name?: string;
}
export interface RoleTable extends BaseType {
name: string;
order?: number;
code: string;
data_scope?: number;
menus?: permissionMenuType[];
depts?: permissionDeptType[];
status?: number;
description?: string;
}
export interface RoleForm extends BaseFormType {
name?: string;
order?: number;
code: string;
data_scope?: number;
status?: number;
description?: 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: number;
description?: string;
children?: permissionMenuType[];
}

@ -0,0 +1,90 @@
import { request } from "@utils";
const API_PATH = "/system/ticket";
const TicketAPI = {
listTicket(query?: TicketPageQuery) {
return request<ApiResponse<PageResult<TicketTable>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailTicket(id: number) {
return request<ApiResponse<TicketTable>>({ url: `${API_PATH}/detail/${id}`, method: "get" });
},
createTicket(body: TicketCreateForm) {
return request<ApiResponse>({ url: `${API_PATH}/create`, method: "post", data: body });
},
updateTicket(id: number, body: TicketUpdateForm) {
return request<ApiResponse>({ url: `${API_PATH}/update/${id}`, method: "put", data: body });
},
deleteTicket(body: number[]) {
return request<ApiResponse>({ url: `${API_PATH}/delete`, method: "delete", data: body });
},
exportTicket(query?: TicketPageQuery) {
return request<ApiResponse<Blob>>({
url: `${API_PATH}/export`,
method: "get",
params: query,
responseType: "blob",
});
},
batchTicket(body: { ids: number[]; status: number }) {
return request<ApiResponse>({ url: `${API_PATH}/batch`, method: "put", data: body });
},
};
export default TicketAPI;
export interface TicketPageQuery extends PageQuery, UserByQueryParams {
title?: string;
ticket_type?: string;
assigned_id?: number;
status?: number;
}
export interface TicketTable extends BaseType {
title: string;
ticket_content?: string;
summary?: string;
ticket_type: string;
images?: string;
reply?: string;
assigned_id?: number;
assigned_by?: CommonType;
status?: number;
description?: string;
}
export interface TicketCreateForm {
title: string;
ticket_content?: string;
summary?: string;
ticket_type: string;
images?: string;
description?: string;
}
export interface TicketUpdateForm {
title?: string;
ticket_content?: string;
summary?: string;
ticket_type?: string;
status?: number;
reply?: string;
assigned_id?: number;
description?: string;
}
export interface TicketForm extends BaseFormType {
title: string;
ticket_content?: string;
summary?: string;
ticket_type: string;
images?: string;
reply?: string;
assigned_id?: number;
status?: number;
description?: string;
}

@ -0,0 +1,283 @@
import { request } from "@utils";
import { MenuTable, MenuForm } from "@/api/module_platform/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: `/common/file/upload?upload_type=avatar`,
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(id: number, body: ResetPasswordForm) {
return request<ApiResponse>({
url: `${API_PATH}/password/reset/${id}`,
method: "put",
data: body,
});
},
registerUser(body: RegisterForm) {
return request<ApiResponse<UserInfo>>({
url: `${API_PATH}/register`,
method: "post",
data: body,
});
},
forgetPassword(body: ForgetPasswordForm) {
return request<ApiResponse>({
url: `${API_PATH}/password/forget`,
method: "post",
data: body,
});
},
listUser(query: UserPageQuery) {
return request<ApiResponse<PageResult<UserInfo>>>({
url: `${API_PATH}/list`,
method: "get",
params: query,
});
},
detailUser(id: number) {
return request<ApiResponse<UserInfo>>({
url: `${API_PATH}/detail/${id}`,
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}/status/batch`,
method: "patch",
data: body,
});
},
exportUser(query: UserPageQuery) {
return request<Blob>({
url: `${API_PATH}/export`,
method: "get",
params: query,
responseType: "blob",
});
},
downloadTemplateUser() {
return request<ApiResponse>({
url: `${API_PATH}/import/template`,
method: "get",
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;
email?: string;
}
export interface UserPageQuery extends PageQuery, UserByQueryParams, TenantByQueryParams {
username?: string;
name?: string;
mobile?: string;
email?: string;
dept_id?: number;
}
export interface searchSelectDataType {
name?: string;
status?: number;
}
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;
tenant_id?: number;
tenant_name?: string;
gitee_login?: string;
github_login?: string;
wx_login?: string;
qq_login?: string;
status?: number;
description?: string;
}
export interface deptTreeType {
id?: number;
name?: string;
parent_id?: number;
children?: deptTreeType[];
}
export interface roleSelectorType {
id?: number;
name?: string;
code?: string;
status?: number;
description?: string;
menus?: MenuForm[];
}
export interface positionSelectorType {
id?: number;
name?: string;
status?: number;
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;
status?: number;
description?: string;
tenant_by?: { id?: number; name?: string };
gitee_login?: string;
github_login?: string;
wx_login?: string;
qq_login?: string;
}
export interface PasswordFormState {
old_password: string;
new_password: string;
confirm_password: string;
}
export interface ResetPasswordForm {
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;
avatar?: string;
tenant_id?: number;
status?: number;
description?: string;
}
export interface CurrentUserFormState {
name?: string;
gender?: number;
mobile?: string;
email?: string;
avatar?: string;
}

@ -0,0 +1,154 @@
import { request } from "@utils";
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: number;
}
export interface JobLogPageQuery extends PageQuery, UserByQueryParams {
job_id?: string;
job_name?: string;
trigger_type?: string;
status?: number;
}
export interface JobLogTable extends BaseType {
job_id: string;
job_name?: string;
trigger_type?: string;
next_run_time?: string;
job_state?: string;
result?: string;
error?: string;
status?: number;
description?: string;
}

@ -0,0 +1,143 @@
import { request } from "@utils";
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",
});
},
batchNode(body: BatchType) {
return request<ApiResponse>({
url: `${API_PATH}/status/batch`,
method: "patch",
data: body,
});
},
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, UserByQueryParams {
name?: string;
code?: string;
status?: number;
}
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: number;
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;
status?: number;
description?: string;
}
export interface NodeForm extends BaseFormType {
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;
status?: number;
description?: string;
}
export interface NodeType {
id: number;
name: string;
code: string;
func?: string;
args?: string;
kwargs?: string;
}

@ -0,0 +1,110 @@
import { request } from "@utils";
/** 对应后端 `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, UserByQueryParams {
name?: string;
code?: string;
status?: number;
}
export interface WorkflowTable extends BaseType {
name?: string;
code?: string;
nodes?: any[];
edges?: any[];
status?: number;
description?: string;
}
export interface WorkflowForm extends BaseFormType {
name?: string;
code?: string;
nodes?: any[];
edges?: any[];
status?: number;
description?: string;
}
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: number;
start_time?: string;
end_time?: string;
variables?: Record<string, any>;
node_results?: Record<string, any>;
error?: string;
}

@ -0,0 +1,109 @@
import { request } from "@utils";
/** 对应后端 `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",
});
},
getWorkflowNodeTypeSelect() {
return request<ApiResponse<WorkflowNodeTypeOption[]>>({
url: `${API_PATH}/select`,
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, UserByQueryParams {
name?: string;
code?: string;
category?: string;
is_active?: boolean;
status?: 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;
status?: number;
description?: string;
}
export interface WorkflowNodeTypeForm extends BaseFormType {
name: string;
code: string;
category?: string;
func: string;
args?: string;
kwargs?: string;
sort_order?: number;
is_active?: boolean;
status?: number;
description?: string;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

@ -0,0 +1 @@
<svg viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="a" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="44" y="42" width="312" height="217"><path d="M355.3 42H44v216.9h311.3V42Z" fill="#fff"/></mask><g mask="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M288.2 248.4h25.1v-30h-25.1v30Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M304.498 238.199c-1.5-3.9-5.9-15.4-4-21.6-2.9.8-3.3.1-5-.1-1.7-.1 0 10.7 2.2 16.4 1.7 4.5 2.1 11.1 2.1 13.6h5.4c.2-1.9.3-5.5-.7-8.3Z" fill="#fff"/><path d="M311.5 214.7v-1.6c0-.7-.6-1.3-1.3-1.3h-22.8c-.7 0-1.3.6-1.3 1.3v1.6" fill="#fff"/><path d="M311.5 214.7v-1.6c0-.7-.6-1.3-1.3-1.3h-22.8c-.7 0-1.3.6-1.3 1.3v1.6M290.2 214.7h21.4c1 0 1.8.8 1.8 1.8v29" stroke="#071F4D" stroke-width="1.096"/><path d="M284.3 245.6v-29c0-1 .8-1.8 1.8-1.8h1.6" fill="#fff"/><path d="M284.3 245.6v-29c0-1 .8-1.8 1.8-1.8h1.6" stroke="#071F4D" stroke-width="1.096"/><path d="M295.402 216.5c-.9 4.2-.4 9.7 2.8 17.5 2.4 5.9 1.9 10.2 1.8 12.3M300.502 216.5c-.9 4.2-.4 9.7 2.8 17.5 2.4 5.9 1.9 10.2 1.8 12.3" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m331 258.4-.3-5.2H88.5l-1.2 5.2H331Z" fill="#C7DEFF"/><path d="M252.9 248.7H331M216.6 258.4H331M47.1 139.3l-2.6 1.5 42.7 117.6h129.2v-6.6" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m247.2 248.6-40.4-111.3H50.5l40.3 111.3h156.4Z" fill="#fff"/><path d="m247.2 248.6-40.4-111.3H50.5l40.3 111.3h156.4Z" stroke="#071F4D"/><path d="m203.2 153.2 32.2 88.7H97.8l-32.3-88.7" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M72.2 146.9c-.77 0-1.4.63-1.4 1.4 0 .77.63 1.4 1.4 1.4.77 0 1.4-.63 1.4-1.4 0-.77-.63-1.4-1.4-1.4ZM79.3 146.9c-.77 0-1.4.63-1.4 1.4 0 .77.63 1.4 1.4 1.4.77 0 1.4-.63 1.4-1.4 0-.77-.63-1.4-1.4-1.4Z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M263.5 171.2h80.3v-63.7h-80.3v63.7Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M290 143.9h-45.6l12.5 51.3H290v-51.3Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M286 117.4h-29.3v77.8h92.9v-67.6l-55.9.6-7.7-10.8Z" fill="#00E4E5"/><path d="m332.6 127.6-38.9.6-7.7-10.8h-11.7M308.9 195.2h45.9M250.3 195.2h28.5M287.3 195.2h12.3" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M130.5 211.4H186v-44h-55.5v44Z" fill="#C7DEFF"/><path fill-rule="evenodd" clip-rule="evenodd" d="M148.7 192.5h-31.6l8.7 35.5h22.9v-35.5Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M145.9 174.2h-20.2V228h64.1v-46.7l-38.6.4-5.3-7.5Z" fill="#006EFF"/><path d="m179 181.3-27.8.4-5.3-7.5h-7.7M176.2 201.7h19.2M163.2 210.7H195M172.1 228h-54.2M184.8 228h8.1M174.9 228h5.4" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="m293.2 155.7-6.4 6.3 15.3 15.3 22.7-22.6-6.4-6.4-16.3 16.3-8.9-8.9Z" fill="#fff"/><path d="M57.2 258.4h283.6M345.9 258.4h8.1M55.4 258.4h220.5M160.1 118.8l-1.2 2.7M156.7 127c-.3.8-.7 1.8-1.1 2.8M222 68.5c-1 .2-1.9.5-2.9.8M214.1 70.7c-5.8 1.9-11.3 4.4-16.5 7.4M195.4 79.5c-.9.5-1.7 1.1-2.5 1.6M314.2 98.5c-.6-.8-1.3-1.5-2-2.3M308.9 92.8c-4-4-8.3-7.6-13-10.8M293.9 80.7c-.8-.5-1.7-1.1-2.5-1.6" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M251.296 71.203c-3.6-1.5-18.5-2.9-21.8-1.9-1 5.8 4.9 13.5 4.9 13.5s6-9.9 16.9-11.6Z" fill="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M251.3 42.704c-6.5 6.7-7.8 13-8.8 19.3 24.4-1.1 36.3 13 42.8 20 3.2-9.1 7.8-23 7.2-29-7.1-6.4-20-11.7-41.2-10.3Z" fill="#C7DEFF"/><path d="M230 69.3c36.2-3.8 52 21.1 52 21.1s11.4-28.2 10.5-37.4c-7.3-6.5-23.3-12-45.6-10.1-9 6.3-15.6 18.7-16.9 26.4Z" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M161.604 70.7c-6 8.4-9.9 21.9-8.8 33.8 8.4 5.3 32.3 10.5 43.6 11.5 6.1-7.9 15.9-26 15.9-26s-32-4.8-50.7-19.3Z" fill="#C7DEFF"/><path d="M193.103 119.5c4.8-2.7 19.2-29.5 19.2-29.5s-35.8-5.4-53.7-21.8c-9.3 6.1-16.4 24.3-15 40.1 10.6 6.7 45.8 13.3 49.5 11.2Z" stroke="#071F4D"/><path fill-rule="evenodd" clip-rule="evenodd" d="M189.5 111.6c-3 5.2-5.7 7.2-9.8 6.6 12.2 2.6 13.5 1.2 15.6-1.1 2.2-2.4 4.2-6.6 4.2-6.6s-3.1 2.5-10 1.1Z" fill="#071F4D"/><path d="M331 251.8v6.6M77 165.4l-2.7-6.7h7.8M222.8 228.9l2.8 6.6h-7.9" stroke="#071F4D"/></g></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

@ -0,0 +1,70 @@
<svg width="695" height="695" viewBox="195 164.5 695 695" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="white"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="#ECEFFF"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint0_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint1_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint2_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint3_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="white"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="#ECEFFF"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint4_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint5_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint6_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint7_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="white"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="#ECEFFF"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint8_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint9_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint10_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint11_radial_402_5926)"/>
<defs>
<radialGradient id="paint0_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-137.956 -387.324 387.315 -228.814 483.689 731.186)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint1_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-140.464 -366.075 365.281 -152.396 447.03 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint2_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-111.514 -277.545 374.955 -279.04 405.025 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint3_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-190.674 -263.482 350.863 -361.721 469.942 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint4_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-186.573 -387.324 523.807 -228.814 804.163 731.186)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint5_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-189.964 -366.075 494.008 -152.396 754.585 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint6_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-150.813 -277.545 507.091 -279.04 697.777 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint7_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-257.868 -263.482 474.509 -361.721 785.571 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint8_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-143.901 -163.816 404.006 -96.7753 707.774 729.368)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint9_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-146.517 -154.829 381.023 -64.455 669.536 712.651)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint10_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-116.32 -117.386 391.113 -118.018 625.721 732.407)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint11_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-198.891 -111.438 365.983 -152.988 693.435 718.729)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,70 @@
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="white"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="#ECEFFF"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint0_radial_402_5772)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint1_radial_402_5772)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint2_radial_402_5772)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint3_radial_402_5772)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="white"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="#ECEFFF"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint4_radial_402_5772)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint5_radial_402_5772)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint6_radial_402_5772)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint7_radial_402_5772)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="white"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="#ECEFFF"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint8_radial_402_5772)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint9_radial_402_5772)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint10_radial_402_5772)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint11_radial_402_5772)"/>
<defs>
<radialGradient id="paint0_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-112.689 -593.187 613.696 -244.529 483.689 731.187)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint1_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-140.464 -366.075 365.281 -152.396 447.03 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint2_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-111.514 -277.545 374.955 -279.04 405.025 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint3_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-190.674 -263.482 350.863 -361.721 469.942 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint4_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-227.163 -513.187 693.033 -276.545 804.163 731.187)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint5_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-189.964 -366.075 494.008 -152.396 754.585 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint6_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-150.813 -277.545 507.091 -279.04 697.777 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint7_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-257.868 -263.482 474.509 -361.721 785.571 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint8_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-143.901 -163.816 404.006 -96.7753 707.774 729.368)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint9_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-146.517 -154.829 381.023 -64.455 669.536 712.651)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint10_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-116.32 -117.386 391.113 -118.018 625.721 732.407)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint11_radial_402_5772" cx="0" cy="0" r="1" gradientTransform="matrix(-198.891 -111.438 365.983 -152.988 693.435 718.729)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,70 @@
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="white"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="#ECEFFF"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint0_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint1_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint2_radial_402_5926)"/>
<path d="M498.848 411.545C497.534 414.363 494.707 416.164 491.598 416.164H321.164C315.641 416.164 311.164 420.641 311.164 426.164V502C311.164 507.523 315.641 512 321.164 512H439.448C445.3 512 449.172 518.077 446.699 523.381L399.992 623.545C398.678 626.363 395.85 628.164 392.741 628.164H321.164C315.641 628.164 311.164 632.641 311.164 638.164V716C311.164 720.418 307.582 724 303.164 724H203C198.582 724 195 720.418 195 716V310C195 304.477 199.477 300 205 300H538.305C544.157 300 548.028 306.077 545.555 311.381L498.848 411.545Z" fill="url(#paint3_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="white"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="#ECEFFF"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint4_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint5_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint6_radial_402_5926)"/>
<path d="M688.908 560.322C686.291 554.709 690.77 548.423 696.893 549.353C757.064 558.491 810.471 588.221 849.703 631.13C850.244 631.721 850.689 632.392 851.027 633.118L888.099 712.619C890.572 717.923 886.701 724 880.849 724H770.33C767.221 724 764.393 722.199 763.079 719.381L688.908 560.322ZM518.744 719.381C517.43 722.199 514.602 724 511.493 724H421.614C416.148 724 412.297 718.637 414.255 713.534C442.643 639.551 503.816 581.812 579.989 558.107C586.39 556.114 591.818 562.67 588.985 568.746L518.744 719.381ZM704.825 319.586C704.844 319.626 704.877 319.659 704.917 319.678L704.956 319.696C705.03 319.731 705.062 319.819 705.027 319.893C705.009 319.933 705.009 319.979 705.027 320.019L807.477 539.725C810.905 547.077 802.133 554.798 794.992 550.951C758.455 531.27 717.22 519.187 673.402 516.817C670.405 516.655 667.72 514.883 666.451 512.163L648.161 472.939C645.29 466.781 636.532 466.781 633.66 472.939L613.528 516.112C612.404 518.523 610.151 520.21 607.53 520.664C559.993 528.902 516.417 548.698 479.804 577.052C472.49 582.716 458.831 572.994 462.741 564.609L576.796 320.02C576.814 319.98 576.814 319.934 576.795 319.894C576.761 319.819 576.793 319.73 576.867 319.695L576.906 319.677C576.947 319.658 576.98 319.625 576.999 319.584L583.978 304.619C585.292 301.801 588.119 300 591.228 300H690.596C693.705 300 696.532 301.801 697.846 304.619L704.825 319.586Z" fill="url(#paint7_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="white"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="#ECEFFF"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint8_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint9_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint10_radial_402_5926)"/>
<path d="M656.303 547C697.059 547 735.69 556.094 770.28 572.362C775.259 574.704 773.176 582.062 767.674 582.062C666.005 582.062 577.554 638.65 532.097 722.047C530.671 724.664 527.945 726.328 524.965 726.328H414.524C409.01 726.328 405.156 720.88 407.2 715.76C446.677 616.873 543.33 547 656.303 547Z" fill="url(#paint11_radial_402_5926)"/>
<defs>
<radialGradient id="paint0_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-137.956 -387.324 387.315 -228.814 483.689 731.186)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint1_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-140.464 -366.075 365.281 -152.396 447.03 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint2_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-111.514 -277.545 374.955 -279.04 405.025 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint3_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-190.674 -263.482 350.863 -361.721 469.942 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint4_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-186.573 -387.324 523.807 -228.814 804.163 731.186)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint5_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-189.964 -366.075 494.008 -152.396 754.585 691.661)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint6_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-150.813 -277.545 507.091 -279.04 697.777 738.373)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint7_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-257.868 -263.482 474.509 -361.721 785.571 706.034)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint8_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-143.901 -163.816 404.006 -96.7753 707.774 729.368)" gradientUnits="userSpaceOnUse">
<stop offset="0.634139" stop-color="#1629FF"/>
<stop offset="1" stop-color="#A274FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint9_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-146.517 -154.829 381.023 -64.455 669.536 712.651)" gradientUnits="userSpaceOnUse">
<stop offset="0.493539" stop-color="#8258FF"/>
<stop offset="1" stop-color="#8258FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint10_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-116.32 -117.386 391.113 -118.018 625.721 732.407)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="1" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint11_radial_402_5926" cx="0" cy="0" r="1" gradientTransform="matrix(-198.891 -111.438 365.983 -152.988 693.435 718.729)" gradientUnits="userSpaceOnUse">
<stop stop-color="#59B2FF"/>
<stop offset="0.690597" stop-color="#5EB4FF" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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

Loading…
Cancel
Save