Compare commits

42 Commits

Author SHA1 Message Date
Coder慌
4836a770c4 !139 feat(es):增加ES实例中对HTTP/HTTPS协议的支持,默认使用HTTP协议,使用https时默认证书免校验
Merge pull request !139 from davidathena/dev
2025-10-28 11:25:47 +00:00
fudawei
e6c89fad1b feat(es):增加ES实例中对HTTPS协议的支持,默认证书免校验 2025-10-23 15:29:27 +08:00
meilin.huang
dba19b1e66 fix: editor提示被遮挡问题修复等 2025-10-18 11:21:33 +08:00
davidathena
4e30bdb7cc !138 fix: 后端数据连接断开后报空指针异常后程序中断问题
* fix(connection):fix the bug for nil error in connection when connection reset
* fix(sqleditor): fix the spell error in sql editor
2025-10-18 03:15:25 +00:00
meilin.huang
4ac57cd140 refactor: 标签不可移动,资源选择优化等 2025-10-07 15:41:19 +08:00
meilin.huang
c4d52ce47a feat: 资源操作新增右键菜单操作等 2025-09-17 21:23:12 +08:00
meilin.huang
54d0688571 fix: 名称调整等 2025-09-14 20:53:47 +08:00
meilin.huang
66d5fd6ca4 feat: 容器操作优化等 2025-09-06 21:32:48 +08:00
时光似水戏流年
25195b6360 !137 现在执行sql只执行当前光标所在的sql(分号分割的),如果要执行全部sql需要先全选,再执行
* 现在执行sql只执行当前sql(分号分割的),如果要执行全部sql需要先全选,再执行
2025-09-02 11:12:04 +00:00
meilin.huang
e02ecf053f feat: 资源操作统一管理&容器操作 2025-08-31 21:46:10 +08:00
meilin.huang
c86f2ad412 refactor: 样式优化 2025-08-19 19:44:14 +08:00
meilin.huang
82fd97e06a fix: file文件缺失 2025-08-08 12:55:10 +08:00
meilin.huang
614a144f60 refactor: 样式优化 2025-08-04 21:02:27 +08:00
meilin.huang
7d344c71e1 refactor: 消息模块调整 & 样式优化 2025-08-02 22:08:56 +08:00
meilin.huang
6ad6c69660 refactor: 消息模块重构,infra包路径简写等 2025-07-27 21:02:48 +08:00
meilin.huang
e96379b6c0 fix: vite配置调整 2025-07-07 12:05:55 +08:00
meilin.huang
f7480f3bac refactor: cast包替换 2025-06-27 12:17:45 +08:00
meilin.huang
54d3a5b368 fix: sql执行记录根据关键词搜索问题修复等 2025-06-22 10:52:06 +08:00
meilin.huang
7eb4d064ea feat: 机器脚本新增分配、组件属性类型不匹配警告调整 2025-06-16 20:13:03 +08:00
meilin.huang
cc66fcddf5 refactor: 动态路由调整&分隔面板使用element自带组件 2025-06-09 21:18:55 +08:00
meilin.huang
aac4c2b42b fix: 机器计划任务、数据库迁移任务初始化问题修复 2025-06-01 20:39:54 +08:00
meilin.huang
7a17042276 refactor: pool get options支持不创建连接 2025-05-29 20:24:48 +08:00
Coder慌
42fbfd3c47 !136 fix: 连接池修复
Merge pull request !136 from zongyangleo/dev_0529
2025-05-29 04:39:31 +00:00
zongyangleo
e273ade0b0 fix: 连接池修复 2025-05-29 11:38:29 +08:00
meilin.huang
bcaa4563ac fix: ssh tunnel检测导致死锁问题调整 2025-05-27 22:56:54 +08:00
meilin.huang
e0c01d4561 fix: 移除隧道连接时检测是否正在使用 2025-05-26 22:33:51 +08:00
meilin.huang
d6280ea280 refactor: 使用泛型重构参数绑定等 2025-05-24 16:22:54 +08:00
meilin.huang
666b191b6c fix: some issue 2025-05-23 17:26:12 +08:00
meilin.huang
778cb7f4de reafctor: pool 2025-05-22 23:29:50 +08:00
zongyangleo
142bbd265d !134 feat: 新增支持es和连接池
* feat: 各连接,支持连接池
* feat:支持es
2025-05-21 04:42:30 +00:00
meilin.huang
f676ec9e7b feat: flow design & page query refactor 2025-05-20 21:04:47 +08:00
meilin.huang
44d379a016 otp: 样式优化 2025-04-26 17:37:09 +08:00
meilin.huang
2170509d92 refactor: code optimization 2025-04-23 20:36:32 +08:00
meilin.huang
798ab7d18b style: fix 2025-04-20 21:01:01 +08:00
meilin.huang
abd2b4bac0 refactor: 引入tailwind css & 后端部分非公共包位置调整 2025-04-18 22:07:37 +08:00
meilin.huang
585cbbed23 fix: i18n特殊字符调整 2025-04-16 12:09:55 +08:00
meilin.huang
1b40d345eb feat: message notify 2025-04-15 21:42:31 +08:00
zongyangleo
3c0292b56e !132 fix:
* fix: 表结构同步修复
* fix: 达梦低权限兼容,pgsql bool值转换
2025-03-17 11:12:52 +00:00
meilin.huang
bc21ba7c1e fix: some issue 2025-03-11 12:42:20 +08:00
meilin.huang
c7c3fd7f7e refactor: form rules refactor 2025-03-05 12:47:52 +08:00
meilin.huang
547e31eae6 refactor: pacakge version upgrade 2025-02-27 19:40:31 +08:00
meilin.huang
6072bcb111 fix: fixed some issues 2025-02-20 17:07:13 +08:00
822 changed files with 29717 additions and 17842 deletions

View File

@@ -15,11 +15,14 @@
<img src="https://img.shields.io/github/stars/dromara/mayfly-go.svg?style=social" alt="github star"/>
<img src="https://img.shields.io/github/forks/dromara/mayfly-go.svg?style=social" alt="github fork"/>
</a>
<a href="https://github.com/dromara/mayfly-go" target="_blank">
<img src="https://gitcode.com/dromara/mayfly-go/star/badge.svg" alt="github star"/>
</a>
<a href="https://hub.docker.com/r/mayflygo/mayfly-go/tags" target="_blank">
<img src="https://img.shields.io/docker/pulls/mayflygo/mayfly-go.svg?label=docker%20pulls&color=fac858" alt="docker pulls"/>
</a>
<a href="https://github.com/golang/go" target="_blank">
<img src="https://img.shields.io/badge/Golang-1.22%2B-yellow.svg" alt="golang"/>
<img src="https://img.shields.io/badge/Golang-1.24%2B-yellow.svg" alt="golang"/>
</a>
<a href="https://cn.vuejs.org" target="_blank">
<img src="https://img.shields.io/badge/Vue-3.x-green.svg" alt="vue">
@@ -28,7 +31,7 @@
## 前言
web 版 **linux(终端[终端回放、命令过滤] 文件 脚本 进程 计划任务)。数据库mysql postgres oracle sqlserver 达梦 高斯 sqlite数据操作、数据同步数据迁移。redis(单机 哨兵 集群)。mongo 等集工单流程审批于一体的统一管理操作平台。**
Web 版 **统一管理操作平台**,集成了对 Linux 系统的全面操作支持(包括终端管理[终端回放、命令过滤]文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server达梦高斯、SQLite 等)的数据操作、数据同步数据迁移功能。此外,还支持 Redis单机哨兵集群模式)、 MongoDB 、Es 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。
## 开发语言与主要框架
@@ -48,42 +51,36 @@ http://go.mayfly.run
#### 首页
![首页](https://foruda.gitee.com/images/1714378104294194769/149fd257_1240250.png "屏幕截图")
![首页](https://foruda.gitee.com/images/1757163736351080323/afb6b330_1240250.png "屏幕截图")
#### 机器操作
#### 资源管理
##### 状态查看
![资源树](https://foruda.gitee.com/images/1757163958991119284/83eb2171_1240250.png "屏幕截图")
![机器状态查看](https://foruda.gitee.com/images/1714378556642584686/93c46ec0_1240250.png "屏幕截图")
#### 资源操作
##### ssh 终端
![终端操作](https://foruda.gitee.com/images/1757164093410206293/1c7dda30_1240250.png)
![终端操作](https://foruda.gitee.com/images/1714378353790214943/2864ba66_1240250.png "屏幕截图")
##### 文件操作
![文件操作](https://foruda.gitee.com/images/1714378417206086701/74a188d8_1240250.png "屏幕截图")
![文件操作](https://foruda.gitee.com/images/1757164149388450531/0542398c_1240250.png)
![文件查看](https://foruda.gitee.com/images/1714378482611638688/7753faf6_1240250.png "屏幕截图")
#### 数据库操作
##### sql 编辑器
![sql编辑器](https://foruda.gitee.com/images/1757164386318836686/c3b17a52_1240250.png)
![sql编辑器](https://foruda.gitee.com/images/1714378747473077515/3c9387c0_1240250.png "屏幕截图")
##### 在线增删改查数据
![选表查数据](https://foruda.gitee.com/images/1757164281011401749/5109485f_1240250.png)
![选表查数据](https://foruda.gitee.com/images/1714378625059063750/3951e5a8_1240250.png "屏幕截图")
#### Redis 操作
![redis操作](https://foruda.gitee.com/images/1757164442298752845/4af1b296_1240250.png)
![redis操作](https://foruda.gitee.com/images/1714378855845451114/4c3f0097_1240250.png "屏幕截图")
#### Mongo 操作
![mongo操作](https://foruda.gitee.com/images/1714378916425714642/77fc0ed9_1240250.png "屏幕截图")
![es操作](https://foruda.gitee.com/images/1757164553845346963/b5b70381_1240250.png)
![容器操作](https://foruda.gitee.com/images/1757164625186816754/2b195e25_1240250.png)
#### 工单流程审批
![流程审批](https://foruda.gitee.com/images/1714379057627690037/ad136862_1240250.png "屏幕截图")
@@ -106,4 +103,11 @@ http://go.mayfly.run
## 💌 支持作者
如果觉得项目不错,或者已经在使用了,希望你可以去 <a target="_blank" href="https://github.com/dromara/mayfly-go">Github</a> <a target="_blank" href="https://gitee.com/dromara/mayfly-go">Gitee</a> 帮我点个 ⭐ Star这将是对我极大的鼓励与支持。
如果觉得项目不错,或者已经在使用了,希望你可以去 <a target="_blank" href="https://github.com/dromara/mayfly-go">Github</a><a target="_blank" href="https://gitee.com/dromara/mayfly-go">Gitee</a><a target="_blank" href="https://gitcode.com/dromara/mayfly-go">Gitcode</a> 帮我点个 ⭐ Star这将是对我极大的鼓励与支持。
> 喝杯咖啡 ☕️ 或者来杯奶茶 🧋,让作者更有精神,写出更棒的代码!
<img class="no-margin" src="https://foruda.gitee.com/images/1744113367791412282/36a3c23b_1240250.png" alt="微信打赏" width="200" height="200">
> **特别感谢:**
> 赞助金额达 199 元以上加微信wx-error可受邀进入付费交流群享受更快、更优先的技术支持与交流服务

View File

@@ -28,7 +28,7 @@
## Preface
Browser-based management platform. **linux(Terminal [terminal playback, command filtering], file, script, process, cronjob), database (mysql, postgres, oracle, sqlserver, Dameng, gauss, sqlite) data operation, data synchronization, data migration, redis(standlone, sentinel, cluster), mongo and other unified management and operation platforms that integrate work order process approval.**
Web-based **Unified Management and Operation Platform**, integrating comprehensive operation support for Linux systems (including terminal management [terminal playback, command filtering], file management, script execution, process monitoring, and cronjob settings). It also provides data operation, data synchronization, and data migration for multiple databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports Redis operations (standalone, sentinel, and cluster modes) and MongoDB、Es management, combined with work order process approval functionality to offer enterprises an all-in-one solution for operations and management.
## Development languages and major frameworks
@@ -46,40 +46,35 @@ account/passwordtest/test123.
![首页](https://foruda.gitee.com/images/1714378104294194769/149fd257_1240250.png "屏幕截图")
#### Machine Operation
#### Resource Manage
##### Status
![资源树](https://foruda.gitee.com/images/1757163958991119284/83eb2171_1240250.png "屏幕截图")
![机器状态查看](https://foruda.gitee.com/images/1714378556642584686/93c46ec0_1240250.png "屏幕截图")
#### Resource Operation
##### SSH Terminal
![终端操作](https://foruda.gitee.com/images/1757164093410206293/1c7dda30_1240250.png)
![终端操作](https://foruda.gitee.com/images/1714378353790214943/2864ba66_1240250.png "屏幕截图")
##### File Operation
![文件操作](https://foruda.gitee.com/images/1714378417206086701/74a188d8_1240250.png "屏幕截图")
![文件操作](https://foruda.gitee.com/images/1757164149388450531/0542398c_1240250.png)
![文件查看](https://foruda.gitee.com/images/1714378482611638688/7753faf6_1240250.png "屏幕截图")
#### Database Operation
##### SQL Editor
![sql编辑器](https://foruda.gitee.com/images/1757164386318836686/c3b17a52_1240250.png)
![sql编辑器](https://foruda.gitee.com/images/1714378747473077515/3c9387c0_1240250.png "屏幕截图")
##### Add, delete, update and check data online
![选表查数据](https://foruda.gitee.com/images/1757164281011401749/5109485f_1240250.png)
![选表查数据](https://foruda.gitee.com/images/1714378625059063750/3951e5a8_1240250.png "屏幕截图")
#### Redis Operation
![redis操作](https://foruda.gitee.com/images/1757164442298752845/4af1b296_1240250.png)
![redis操作](https://foruda.gitee.com/images/1714378855845451114/4c3f0097_1240250.png "屏幕截图")
#### Mongo Operation
![mongo操作](https://foruda.gitee.com/images/1714378916425714642/77fc0ed9_1240250.png "屏幕截图")
![es操作](https://foruda.gitee.com/images/1757164553845346963/b5b70381_1240250.png)
![容器操作](https://foruda.gitee.com/images/1757164625186816754/2b195e25_1240250.png)
#### Work order process approval
![流程审批](https://foruda.gitee.com/images/1714379057627690037/ad136862_1240250.png "屏幕截图")

View File

@@ -77,6 +77,7 @@ function build() {
if [ "${copyDocScript}" == "1" ] ; then
echo_green "Copy resources such as scripts [config.yml.example、readme.txt、startup.sh、shutdown.sh]"
cp ${server_folder}/config.yml.example ${toFolder}
mv ${toFolder}/config.yml.example ${toFolder}/config.yml
cp ${server_folder}/readme.txt ${toFolder}
cp ${server_folder}/readme_en.txt ${toFolder}
cp ${server_folder}/resources/script/startup.sh ${toFolder}

View File

@@ -14,18 +14,18 @@ services:
restart: always
server:
image: ccr.ccs.tencentyun.com/mayfly/mayfly-go:v1.8.5
image: ccr.ccs.tencentyun.com/mayfly/mayfly-go:latest
build:
context: .
dockerfile: Dockerfile
container_name: mayfly-go-server
ports:
- "8888:8888"
- "18888:18888"
environment:
TZ: Asia/Shanghai
WAIT_HOSTS: mysql:3306
volumes:
- ./server/config.yml.example:/mayfly/config.yml
- ./server/config.yml:/mayfly/config.yml
depends_on:
- mysql
restart: always

View File

@@ -7,4 +7,8 @@ VITE_OPEN = false
# public path 配置线上环境路径(打包)
VITE_PUBLIC_PATH = ''
VITE_EDITOR=idea
VITE_EDITOR=idea
# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE = hash

View File

@@ -4,8 +4,4 @@ ENV = 'development'
VITE_OPEN = true
# 本地环境接口地址
VITE_API_URL = '/api'
# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE = hash
VITE_API_URL = '/api'

View File

@@ -3,7 +3,3 @@ ENV = 'production'
# 线上环境接口地址
VITE_API_URL = '/api'
# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE = hash

View File

@@ -11,7 +11,7 @@ module.exports = {
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
extends: ['plugin:vue/essential', 'eslint:recommended'],
plugins: ['vue', '@typescript-eslint'],
overrides: [
{
@@ -35,9 +35,8 @@ module.exports = {
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-unused-vars': [2],
'@typescript-eslint/no-unused-vars': 'off',
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
@@ -53,6 +52,7 @@ module.exports = {
'vue/no-arrow-functions-in-watch': 'off',
'vue/no-template-key': 'off',
'vue/no-v-html': 'off',
'vue/no-unused-vars': 'off',
'vue/comment-directive': 'off',
'vue/no-parsing-error': 'off',
'vue/no-deprecated-v-on-native-modifier': 'off',
@@ -67,7 +67,7 @@ module.exports = {
'generator-star-spacing': 'off',
'no-unreachable': 'off',
'no-multiple-template-root': 'off',
'no-unused-vars': 'error',
'no-unused-vars': 'off',
'no-v-model-argument': 'off',
'no-case-declarations': 'off',
// 'no-console': 'error',

View File

@@ -10,61 +10,61 @@
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^12.5.0",
"asciinema-player": "^3.9.0",
"@element-plus/icons-vue": "^2.3.2",
"@logicflow/core": "^2.1.3",
"@logicflow/extension": "^2.1.5",
"@vueuse/core": "^13.9.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-search": "^0.15.0",
"@xterm/addon-web-links": "^0.11.0",
"@xterm/xterm": "^5.5.0",
"asciinema-player": "^3.11.1",
"axios": "^1.6.2",
"clipboard": "^2.0.11",
"cropperjs": "^1.6.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.6.0",
"element-plus": "^2.9.4",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
"monaco-editor": "^0.52.2",
"monaco-sql-languages": "^0.12.2",
"monaco-themes": "^0.4.4",
"dayjs": "^1.11.18",
"echarts": "^6.0.0",
"element-plus": "^2.11.4",
"js-base64": "^3.7.8",
"jsencrypt": "^3.5.4",
"monaco-editor": "^0.54.0",
"monaco-sql-languages": "^0.15.1",
"monaco-themes": "^0.4.7",
"nprogress": "^0.2.0",
"pinia": "^2.3.1",
"pinia": "^3.0.3",
"qrcode.vue": "^3.6.0",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.6",
"splitpanes": "^3.1.8",
"sql-formatter": "^15.4.10",
"sql-formatter": "^15.6.8",
"trzsz": "^1.1.5",
"uuid": "^9.0.1",
"vue": "^3.5.13",
"vue-i18n": "^11.1.0",
"vue-router": "^4.5.0",
"vuedraggable": "^4.1.0",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"xterm-addon-search": "^0.13.0",
"xterm-addon-web-links": "^0.9.0"
"uuid": "^13.0.0",
"vue": "^v3.5.22",
"vue-i18n": "^11.1.12",
"vue-router": "^4.6.3",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.14",
"@types/crypto-js": "^4.2.2",
"@types/lodash": "^4.14.178",
"@types/node": "^18.14.0",
"@types/node": "^22.13.14",
"@types/nprogress": "^0.2.0",
"@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/compiler-sfc": "^3.5.13",
"code-inspector-plugin": "^0.4.5",
"dotenv": "^16.3.1",
"eslint": "^8.35.0",
"eslint-plugin-vue": "^9.31.0",
"prettier": "^3.2.5",
"sass": "^1.84.0",
"typescript": "^5.7.3",
"vite": "^6.1.0",
"@typescript-eslint/eslint-plugin": "^8.35.0",
"@typescript-eslint/parser": "^8.35.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/compiler-sfc": "^3.5.18",
"autoprefixer": "^10.4.21",
"code-inspector-plugin": "^1.0.4",
"eslint": "^9.29.0",
"eslint-plugin-vue": "^10.5.0",
"postcss": "^8.5.6",
"prettier": "^3.6.1",
"sass": "^1.93.2",
"tailwindcss": "^4.1.14",
"typescript": "^5.9.2",
"vite": "npm:rolldown-vite@latest",
"vite-plugin-progress": "0.0.7",
"vue-eslint-parser": "^9.4.3"
"vue-eslint-parser": "^10.2.0"
},
"browserslist": [
"> 1%",

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,39 +1,34 @@
<template>
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
<div class="h100">
<el-watermark
:zIndex="10000000"
:width="210"
v-if="themeConfig.isWatermark"
:font="{ color: 'rgba(180, 180, 180, 0.3)' }"
:content="themeConfig.watermarkText"
class="h100"
>
<router-view v-show="themeConfig.lockScreenTime !== 0" />
</el-watermark>
<router-view v-if="!themeConfig.isWatermark" v-show="themeConfig.lockScreenTime !== 0" />
<el-watermark
:zIndex="100000"
:width="210"
v-if="themeConfig.isWatermark"
:font="{ color: 'rgba(180, 180, 180, 0.3)' }"
:content="themeConfig.watermarkText"
class="!h-full"
>
<router-view />
</el-watermark>
<router-view v-if="!themeConfig.isWatermark" />
<LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime !== 0" />
</div>
<Setings />
</el-config-provider>
</template>
<script setup lang="ts" name="app">
import { ref, onMounted, onUnmounted, nextTick, watch, computed } from 'vue';
import { onMounted, nextTick, watch, computed, defineAsyncComponent } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '@/store/themeConfig';
import LockScreen from '@/layout/lockScreen/index.vue';
import Setings from '@/layout/navBars/breadcrumb/setings.vue';
import mittBus from '@/common/utils/mitt';
import { useIntervalFn } from '@vueuse/core';
import { useI18n } from 'vue-i18n';
import EnumValue from './common/Enum';
import { I18nEnum } from './common/commonEnum';
import { saveThemeConfig } from './common/utils/storage';
const setingsRef = ref();
const Setings = defineAsyncComponent(() => import('@/layout/navBars/breadcrumb/setings.vue'));
const route = useRoute();
const themeConfigStores = useThemeConfig();
@@ -42,19 +37,9 @@ const { themeConfig } = storeToRefs(themeConfigStores);
// 定义变量内容
const { locale, t } = useI18n();
// 布局配置弹窗打开
const openSetingsDrawer = () => {
setingsRef.value.openDrawer();
};
// 页面加载时
onMounted(() => {
nextTick(() => {
// 监听布局配置弹窗点击打开
mittBus.on('openSetingsDrawer', () => {
openSetingsDrawer();
});
// 初始化系统主题
themeConfigStores.initThemeConfig();
});
@@ -120,11 +105,6 @@ const refreshWatermarkTime = () => {
themeConfigStores.setWatermarkNowTime();
};
// 页面销毁时,关闭监听布局配置
onUnmounted(() => {
mittBus.off('openSetingsDrawer', () => {});
});
// 监听路由的变化,设置网站标题
watch(
() => route.path,

View File

@@ -0,0 +1 @@
<svg t="1756305127175" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22356" width="48" height="48"><path d="M959.718832 123.963683C872.444401 50.185297 704.593576 0.299912 511.850044 0.299912S151.255687 50.185297 63.981255 123.963683C23.193205 158.453578 0 198.04198 0 240.22962v543.840672c0 132.461193 229.132871 239.929708 511.850044 239.929708s511.850044-107.468515 511.850044-239.929708v-543.840672c0-42.18764-23.193205-81.776042-63.981256-116.265937zM87.774285 189.64444c19.794201-21.893586 50.685151-43.087377 89.373816-61.182075 42.287611-19.794201 92.073025-35.489603 147.956653-46.586352C384.087474 70.17944 446.869081 64.281168 511.850044 64.281168s127.76257 5.898272 186.745289 17.594845c55.883628 11.096749 105.669042 26.792151 147.956654 46.586352 38.688665 18.094699 69.579615 39.28849 89.373816 61.182075 15.795372 17.494875 23.793029 34.489896 23.793029 50.48521 0 16.095285-7.997657 33.090306-23.793029 50.485209-19.794201 21.893586-50.685151 43.087377-89.373816 61.182075-42.287611 19.894172-92.073025 35.489603-147.956654 46.586352-58.98272 11.696573-121.864298 17.594845-186.745289 17.594845s-127.76257-5.898272-186.74529-17.594845c-55.883628-11.096749-105.669042-26.792151-147.956653-46.586352-38.688665-18.094699-69.579615-39.28849-89.373816-61.182075C71.978912 273.319926 63.981255 256.324905 63.981255 240.22962s7.997657-33.090306 23.79303-50.58518zM63.981255 356.495558c87.274431 73.778385 255.125256 123.66377 447.868789 123.66377s360.594357-49.885385 447.868788-123.66377v155.254515c0 16.095285-7.997657 33.090306-23.793029 50.48521-19.794201 21.893586-50.685151 43.087377-89.373816 61.182075-42.287611 19.794201-92.073025 35.489603-147.956654 46.586352-58.98272 11.696573-121.864298 17.594845-186.745289 17.594845s-127.76257-5.898272-186.74529-17.594845c-55.883628-11.096749-105.669042-26.792151-147.956653-46.586352-38.688665-18.094699-69.579615-39.28849-89.373816-61.182075C71.978912 544.740408 63.981255 527.745387 63.981255 511.750073V356.495558z m895.737577 427.574734c0 16.095285-7.997657 33.090306-23.793029 50.485209-19.794201 21.893586-50.685151 43.087377-89.373816 61.182076-42.287611 19.894172-92.073025 35.489603-147.956654 46.586352-58.98272 11.696573-121.864298 17.594845-186.745289 17.594845s-127.76257-5.898272-186.74529-17.594845c-55.883628-11.096749-105.669042-26.792151-147.956653-46.586352-38.688665-18.094699-69.579615-39.28849-89.373816-61.182076C71.978912 817.160597 63.981255 800.165576 63.981255 784.070292V627.91604c87.274431 73.778385 255.125256 123.66377 447.868789 123.663771s360.594357-49.885385 447.868788-123.663771v156.154252z" p-id="22357"></path><path d="M167.950796 519.847701m-39.988285 0a39.988285 39.988285 0 1 0 79.976569 0 39.988285 39.988285 0 1 0-79.976569 0Z" p-id="22358"></path><path d="M167.950796 791.768037m-39.988285 0a39.988285 39.988285 0 1 0 79.976569 0 39.988285 39.988285 0 1 0-79.976569 0Z" p-id="22359"></path></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1 @@
<svg t="1756305474315" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24277" width="48" height="48"><path d="M960 0H0v1024h1024V0.146286h-64V0z m-640 960.146286h-256v-192h256v192z m0-256.146286h-256V512.146286h256v191.853714z m320 256.146286h-256v-192h256v192z m0-256.146286h-256V512.146286h256v191.853714z m320 256.146286h-256v-192h256v192z m0-256.146286h-256V512.146286h256v191.853714z m0-256h-256V256.146286H640v192h-256V256.146286h-64v192h-256V256.146286h896v191.853714z" p-id="24278"></path></svg>

After

Width:  |  Height:  |  Size: 547 B

View File

@@ -0,0 +1 @@
<svg t="1756107672203" class="icon" viewBox="0 0 1472 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5144" width="48" height="48"><path d="M1449.66628 358.737a233.848 233.848 0 0 0-166.348-35.445 268.717 268.717 0 0 0-108.127-152.273l-31.158-20.026-22.265 30.455a258.736 258.736 0 0 0-22.01 265.39 177.353 177.353 0 0 1-74.28 21.241h-24.953V309.536H830.08228V0H624.44928v154.768H287.27328v154.704H118.68528V468.08H8.44728L3.26528 504.42a493.032 493.032 0 0 0 95.97 353.3c90.149 110.11 234.232 165.964 428.284 165.964a749.848 749.848 0 0 0 585.42-255.025 804.871 804.871 0 0 0 139.86-226.874c187.718-3.391 213.246-134.359 214.27-139.99l4.863-27.447-22.01-15.61z m-766.291-49.84v-92.068h87.717v92.068h-87.717z m-337.176 154.64v-92.068h87.59v92.068h-87.59z m168.588 0v-92.068h87.589v92.068h-87.589z m168.588 0v-92.068h87.717v92.068h-87.717z m170.38-92.068h87.524v92.068h-87.525v-92.068zM683.37428 62.125h87.717v92.003h-87.717V62.125zM514.78728 216.829h87.589v92.068h-87.525v-92.068z m-168.588 0h87.59v92.068h-87.59v-92.068zM177.61228 371.47h87.525v92.068H177.61228v-92.068zM527.19928 938.4a609.348 609.348 0 0 1-235-40.564 399.493 399.493 0 0 0 151.058-66.092 44.018 44.018 0 0 0 7.87-57.582 39.54 39.54 0 0 0-54.575-11.9 375.18 375.18 0 0 1-215.998 62.508 262.639 262.639 0 0 1-19.194-21.433 392.455 392.455 0 0 1-79.591-249.523h943.9a250.035 250.035 0 0 0 155.216-62.06l4.99-4.671a682.157 682.157 0 0 1-658.42 451.636z m699.432-482.412l-25.144-1.215-15.163-21.178a186.566 186.566 0 0 1-21.626-161.358 145.619 145.619 0 0 1 42.483 100.769l-1.663 60.525 54.83-18.682a205.505 205.505 0 0 1 111.07-1.664 170.123 170.123 0 0 1-144.787 42.803zM544.41028 629.31a69.738 69.738 0 1 1-66.412 69.674 68.139 68.139 0 0 1 66.412-69.674z m0 85.413a15.74 15.74 0 1 0-14.971-15.675 15.291 15.291 0 0 0 14.97 15.675z m0 0" p-id="5145"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M96.426667 649.173333H712.96a137.173333 137.173333 0 0 0 0-274.346666H96.426667c-12.8 43.52-19.626667 89.514667-19.626667 137.173333s6.826667 93.696 19.626667 137.173333z" fill="#07A5DE" p-id="6101"></path><path d="M563.2 25.6A486.4 486.4 0 0 0 125.354667 299.946667H837.546667c52.096 0 97.450667-29.013333 120.661333-71.808A485.76 485.76 0 0 0 563.2 25.6z" fill="#EFBF19" p-id="6102"></path><path d="M942.421333 816.64a137.258667 137.258667 0 0 0-129.749333-92.586667H125.312A486.4 486.4 0 0 0 563.2 998.4c153.344 0 290.090667-70.954667 379.221333-181.76z" fill="#3EBEB1" p-id="6103"></path><path d="M506.197333 649.173333c12.8-43.52 19.626667-89.514667 19.626667-137.173333s-6.826667-93.696-19.626667-137.173333H96.469333c-12.8 43.52-19.626667 89.514667-19.626666 137.173333s6.826667 93.696 19.626666 137.173333h409.728z" fill="#231F20" p-id="6104"></path><path d="M477.269333 724.053333H125.354667a488.533333 488.533333 0 0 0 175.957333 197.888 488.533333 488.533333 0 0 0 175.957333-197.930666z" fill="#019B8F" p-id="6105"></path><path d="M301.312 102.058667a488.533333 488.533333 0 0 1 175.957333 197.930666H125.354667a488.533333 488.533333 0 0 1 175.957333-197.930666z" fill="#D8A22A" p-id="6106"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M465.664 679.168c105.301333 0.597333 172.970667 1.066667 202.922667 1.450667 20.48 0.256 36.181333 0.426667 47.274666 0.469333h3.84c45.824 0 84.096 8.533333 114.901334 25.258667 31.189333 16.938667 54.826667 42.368 70.826666 76.245333l1.152 2.517333a24.106667 24.106667 0 0 1-1.621333 3.413334c-46.336 67.84-101.034667 116.565333-164.096 146.346666-63.146667 29.824-134.613333 40.704-214.485333 32.469334-159.232-16.384-283.477333-106.24-372.352-269.994667a5.973333 5.973333 0 0 1 3.584-8.618667c13.653333-3.968 27.733333-6.528 41.941333-7.594666 91.306667-1.365333 170.538667-1.877333 238.165333-1.962667h27.946667z m44.885333 63.829333l-0.64 1.152c-3.754667 6.485333-9.386667 15.36-16.128 25.6l-2.645333 3.925334-1.578667 2.346666c-21.205333 31.445333-51.072 72.234667-70.784 94.464 64.853333 34.304 133.162667 45.44 227.157334 27.52 95.146667-18.090667 145.450667-52.565333 175.829333-114.090666-5.034667-10.581333-14.592-19.285333-31.488-27.733334-12.8-6.4-32.426667-11.050667-58.752-14.250666l-221.013333 1.066666z m-257.578666-5.546666l1.237333 1.536c21.504 26.112 67.712 72.277333 96.896 95.786666 15.146667-14.08 29.098667-29.397333 41.642667-45.824 13.952-18.261333 24.149333-32.64 35.370666-52.821333l-175.146666 1.322667z m471.296-360.874667c38.229333 5.077333 67.626667 18.944 88.448 41.301333 20.736 22.229333 33.024 52.992 36.565333 92.373334 3.626667 39.722667-5.76 71.808-27.733333 96.426666-20.906667 23.381333-53.461333 40.106667-97.877334 49.706667l-2.645333 0.597333-2.816 0.554667H144.725333a8.021333 8.021333 0 0 1-7.893333-6.485333 1545.173333 1545.173333 0 0 1-0.298667-1.578667c-12.373333-62.378667-18.517333-106.666667-18.517333-132.906667 0-38.570667 5.888-81.962667 17.706667-130.261333l1.066666-4.394667a7.082667 7.082667 0 0 1 6.826667-5.333333h580.650667zM197.546667 442.88l-0.853334 2.688c-7.509333 24.064-12.544 44.330667-12.117333 70.954667 0 30.293333 5.418667 54.272 13.653333 81.664h283.050667l0.341333-2.218667 0.469334-3.2c3.541333-24.448 4.010667-47.701333 4.010666-76.544 0-30.805333-1.066667-51.541333-6.4-75.264l-282.154666 1.92z m493.397333-3.029333l-131.797333 1.024 0.512 2.474666c4.48 22.357333 6.741333 43.861333 6.741333 73.216 0 30.421333-2.432 53.76-7.552 79.189334l134.826667-0.170667 1.962666-0.213333c28.16-2.901333 49.194667-7.210667 62.421334-23.04 11.52-13.866667 17.152-32.469333 17.152-55.765334 0-24.746667-6.272-42.624-19.456-54.826666-13.653333-12.714667-34.474667-19.2-61.994667-21.674667l-2.816-0.213333z m49.877333-342.784c63.104 29.824 117.845333 78.592 164.181334 146.346666l1.536 2.304a23.466667 23.466667 0 0 1-1.066667 3.669334c-16 33.92-39.594667 59.306667-70.784 76.245333-30.805333 16.768-69.12 25.258667-114.986667 25.258667-11.178667 0-28.16 0.213333-50.944 0.469333-45.098667 0.597333-112.597333 1.408-202.965333 1.493333h-14.122667c-70.613333 0-154.453333-0.512-251.733333-1.962666a207.061333 207.061333 0 0 1-42.24-7.594667 5.973333 5.973333 0 0 1-3.626667-8.618667C242.986667 170.922667 367.146667 81.066667 526.378667 64.682667c79.829333-8.277333 151.296 2.56 214.4 32.426666z m-102.101333 28.501333c-85.205333-15.36-143.957333-4.010667-213.717333 27.221333 11.648 13.312 26.410667 33.621333 40.874666 55.04l1.578667 2.389334 2.56 3.754666 3.498667 5.376 2.346666 3.584 1.237334 1.877334c18.688 28.757333 35.157333 56.746667 41.728 70.613333h213.674666l2.474667-0.298667 2.56-0.341333c21.290667-2.986667 38.144-10.794667 55.978667-19.754667 17.408-8.576 30.122667-18.304 40.106666-29.866666-49.493333-63.018667-108.586667-104.106667-194.901333-119.594667zM367.744 186.453333c-12.458667 10.069333-34.304 29.44-56.192 50.048l-1.706667 1.621334-3.328 3.157333-3.498666 3.328-1.877334 1.792-2.048 2.005333c-17.322667 16.64-33.578667 33.109333-44.501333 45.909334l179.797333-1.536-1.109333-1.877334a3067.264 3067.264 0 0 1-12.672-21.418666l-11.776-20.053334-2.474667-4.053333-2.56-4.266667-1.152-2.005333-1.237333-2.005333c-12.458667-20.693333-24.917333-40.405333-33.706667-50.645334z" fill="#2c2c2c" p-id="5739"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1 @@
<svg t="1756286353957" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19008" width="48" height="48"><path d="M853.333333 554.666667a128 128 0 0 1 128 128v170.666666a128 128 0 0 1-128 128H170.666667a128 128 0 0 1-128-128v-170.666666a128 128 0 0 1 128-128h682.666666z m0 85.333333H170.666667a42.666667 42.666667 0 0 0-42.368 37.674667L128 682.666667v170.666666a42.666667 42.666667 0 0 0 37.674667 42.368L170.666667 896h682.666666a42.666667 42.666667 0 0 0 42.368-37.674667L896 853.333333v-170.666666a42.666667 42.666667 0 0 0-42.666667-42.666667zM256 725.333333a42.666667 42.666667 0 1 1 0 85.333334 42.666667 42.666667 0 0 1 0-85.333334zM853.333333 42.666667a128 128 0 0 1 128 128v170.666666a128 128 0 0 1-128 128H170.666667a128 128 0 0 1-128-128V170.666667a128 128 0 0 1 128-128h682.666666z m0 85.333333H170.666667a42.666667 42.666667 0 0 0-42.368 37.674667L128 170.666667v170.666666a42.666667 42.666667 0 0 0 37.674667 42.368L170.666667 384h682.666666a42.666667 42.666667 0 0 0 42.368-37.674667L896 341.333333V170.666667a42.666667 42.666667 0 0 0-42.666667-42.666667zM256 213.333333a42.666667 42.666667 0 1 1 0 85.333334 42.666667 42.666667 0 0 1 0-85.333334z" p-id="19009"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M475.19999999 84.5568c202.7008 0 362.6496 71.0912 373.50400001 163.6608l0.40959999 4.5568h0.5632v232.2432H795.19999999V364.288c-63.1552 48.3328-175.5648 80.5888-307.5584 82.5088l-12.4416 0.0768c-133.1968 0-247.7312-30.8224-313.93279999-78.08L155.2 364.288v136.7552c0 63.5136 128.6144 126.208 319.99999999 126.208 63.1808 0 119.5264-6.8352 166.656-18.2784-4.9408 23.552-6.4 43.5968-4.4032 60.2112-48.7936 10.6752-103.7056 16.6144-162.2528 16.6144-133.1968 0-247.7312-30.7968-313.93279999-78.08l-6.0672-4.5056v125.824c0 63.5136 128.6144 126.2336 319.99999999 126.2336 74.3168 0 139.1616-9.4464 190.6688-24.7296l15.18080001 55.5008a631.04 631.04 0 0 1-89.6256 19.584 803.8656 803.8656 0 0 1-116.22400001 8.192c-206.7456 0-369.3312-73.984-374.3488-169.1392l-0.128-4.5568V252.7744h0.56320001C107.32799999 158.0032 269.1712 84.5824 475.19999999 84.5824z m335.18080001 637.696c12.3648 0 22.4 10.0608 22.39999999 22.4256l-0.0768 74.112a22.3744 22.3744 0 0 1 8.96-9.3184c15.4112-8.704 27.0336-24.6528 33.408-46.592a22.4 22.4 0 1 1 43.008 12.4928c-9.6 33.024-28.416 58.4704-54.39999999 73.1136a22.4 22.4 0 0 1-30.92480001-9.216v40.7296a22.4 22.4 0 0 1-44.79999999 0V744.704c0-12.3648 10.0608-22.4 22.4256-22.4z m-15.6672-184.7808a22.784 22.784 0 0 1 31.51359999 0.256c9.8816 9.8816 24.6528 26.624 40.06400001 47.36 25.3184 34.048 44.2624 68.4544 53.24799999 101.9136a22.4256 22.4256 0 0 1-43.3664 11.3408c-9.8816-36.6848-35.584-76.3392-65.8432-111.488-39.7824 46.1824-69.76 97.152-69.75999999 138.5984 0 36.992 13.056 67.4048 33.89439999 81.5616l5.632 5.3248a22.4 22.4 0 0 1-30.77119999 31.7696c-33.8432-22.9376-53.5552-67.3792-53.55520001-118.656 0-39.1424 18.1248-81.8944 48.2816-125.8752a461.312 461.312 0 0 1 50.688-62.1056zM475.19999999 143.0016c-187.7504 0.0512-314.7776 60.416-319.53919999 122.7264 4.8128 62.2336 131.7888 122.5984 319.53919999 122.5984s314.7776-60.3648 319.5392-122.6496C789.92639999 203.4176 662.95039999 143.0016 475.19999999 143.0016z" ></path></svg>
<svg t="1756389060526" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="29147" width="48" height="48"><path d="M465.454545 9.402182c245.697939 0 439.575273 86.171152 452.732122 198.376727l0.496485 5.523394h0.682666v281.506909H853.333333V348.470303c-76.551758 58.585212-212.805818 97.683394-372.79806 100.010667l-15.080728 0.093091c-161.450667 0-300.280242-37.360485-380.524606-94.642425L77.575758 348.470303v165.763879c0 76.986182 155.896242 152.979394 387.878787 152.979394 76.582788 0 144.880485-8.285091 202.007273-22.155637-5.988848 28.547879-7.757576 52.844606-5.337212 72.983273-59.143758 12.939636-125.703758 20.138667-196.670061 20.138667-161.450667 0-300.280242-37.329455-380.524606-94.642424l-7.354181-5.461334v152.51394c0 76.986182 155.896242 153.010424 387.878787 153.010424 90.08097 0 168.680727-11.450182 231.113697-29.975273l18.40097 67.273697a764.89697 764.89697 0 0 1-108.637091 23.738182 974.382545 974.382545 0 0 1-140.877576 9.929697c-250.600727 0-447.674182-89.677576-453.756121-205.017212l-0.155151-5.523394V213.302303h0.682666C19.549091 98.428121 215.722667 9.433212 465.454545 9.433212z m406.279758 772.964848c14.987636 0 27.151515 12.194909 27.151515 27.182546l-0.093091 89.832727a27.120485 27.120485 0 0 1 10.860606-11.29503c18.680242-10.550303 32.768-29.882182 40.494546-56.475152a27.151515 27.151515 0 1 1 52.130909 15.142788c-11.636364 40.029091-34.443636 70.873212-65.939394 88.622546a27.151515 27.151515 0 0 1-37.484606-11.17091v49.369213a27.151515 27.151515 0 0 1-54.30303 0V809.580606c0-14.987636 12.194909-27.151515 27.182545-27.151515z m-18.990545-223.976727a27.61697 27.61697 0 0 1 38.198303 0.310303c11.977697 11.977697 29.882182 32.271515 48.562424 57.406061 30.68897 41.270303 53.651394 82.97503 64.54303 123.531636a27.182545 27.182545 0 0 1-52.565333 13.746424c-11.977697-44.466424-43.132121-92.532364-79.80994-135.136969-48.221091 55.978667-84.557576 117.76-84.557575 167.99806 0 44.838788 15.825455 81.702788 41.084121 98.862546l6.826667 6.454303a27.151515 27.151515 0 0 1-37.298425 38.508606c-41.022061-27.803152-64.915394-81.671758-64.915394-143.825455 0-47.445333 21.969455-99.265939 58.523152-152.576a559.166061 559.166061 0 0 1 61.44-75.279515zM465.454545 80.244364C237.878303 80.306424 83.905939 153.475879 78.134303 229.003636 83.968 304.407273 237.878303 377.607758 465.454545 377.607758S847.003152 304.407273 852.774788 228.941576C846.941091 153.475879 693.030788 80.244364 465.454545 80.244364z" p-id="29148"></path></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M897.8125003 599.75c-0.37500029 8.58750029-11.73750029 18.18749971-35.06250058 30.375-47.99999971 25.01250029-296.84999971 127.35-349.79999942 154.95000029-52.9875 27.60000029-82.38750029 27.3375-124.23750029 7.3125-41.85-19.98749971-306.60000029-126.97499971-354.30000029-149.7375-23.81249971-11.40000029-35.96249971-20.99999971-36.37499942-30.07500029v90.97499971c0 9.07499971 12.52500029 18.71250029 36.37499942 30.11250058 47.7 22.79999971 312.48749971 129.75000029 354.30000029 149.7375 41.85 20.025 71.25000029 20.28750029 124.23750029-7.35000029 52.94999971-27.60000029 301.76250029-129.89999971 349.79999942-154.95000029 24.4125-12.7125 35.25000029-22.6125 35.25000029-31.57499971v-89.70000029l-0.18749971-0.07499971z" fill="" ></path><path d="M897.77500001 451.43749971c-0.37500029 8.58750029-11.73750029 18.15000029-35.02500029 30.33750058-47.99999971 25.01250029-296.84999971 127.35-349.79999942 154.94999942-52.9875 27.60000029-82.38750029 27.3375-124.23750029 7.35000029-41.85-19.98749971-306.60000029-126.97499971-354.30000029-149.77500029-23.81249971-11.3625-35.96249971-20.99999971-36.37499942-30.0375v90.97500058c0 9.07499971 12.52500029 18.675 36.37499942 30.07499942 47.7 22.79999971 312.45000029 129.75000029 354.30000029 149.7375 41.85 20.025 71.25000029 20.28750029 124.23750029-7.3125 52.94999971-27.60000029 301.76250029-129.9375 349.79999942-154.94999942 24.4125-12.75000029 35.25000029-22.65000029 35.25000029-31.6125v-89.70000029l-0.225-0.03750029z" fill="" ></path><path d="M897.77500001 297.61250029c0.45-9.15000029-11.51250029-17.17499971-35.58750029-26.02500029-46.8-17.13750029-294.11250029-115.57500029-341.47499942-132.93749971-47.3625-17.325-66.63750029-16.61249971-122.25000058 3.375C342.7375003 161.93750029 79.41249972 265.24999971 32.5750003 283.55000029c-23.43750029 9.225-34.875 17.73749971-34.50000058 26.81249942V401.37499971c0 9.07499971 12.52500029 18.675 36.37500029 30.07500029 47.7 22.79999971 312.45000029 129.78749971 354.30000029 149.77500029 41.85 19.98749971 71.25000029 20.25 124.23749942-7.35000029 52.94999971-27.60000029 301.76250029-129.9375 349.80000029-154.95000029 24.4125-12.75000029 35.25000029-22.65000029 35.25000029-31.6125V297.61250029h-0.30000058zM320.31250001 383.75l208.53749971-32.02499971-63 92.3625-145.49999942-60.33750029z m461.25-83.17500029l-123.33750029 48.75000029-13.3875 5.24999971-123.26249971-48.74999942 136.575-54 123.37499971 48.74999942z m-362.09999971-89.36249942l-20.17500029-37.20000058 62.92500029 24.60000058 59.32499942-19.42500058-16.04999971 38.43750058 60.45000029 22.64999942-77.9625 8.1-17.47500029 42.00000029-28.19999971-46.83750029-90-8.1 67.1625-24.22499942z m-155.3625 52.49999971c61.57500029 0 111.44999971 19.31249971 111.44999971 43.16249971s-49.87500029 43.2-111.44999971 43.2-111.4875-19.38750029-111.4875-43.2c0-23.85 49.91249971-43.2 111.4875-43.2z" fill="" ></path></svg>
<svg t="1756388835244" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="25729" width="48" height="48"><path d="M1023.786667 611.84c-0.426667 9.770667-13.354667 20.693333-39.893334 34.56-54.613333 28.458667-337.749333 144.896-397.994666 176.298667-60.288 31.402667-93.738667 31.104-141.354667 8.32-47.616-22.741333-348.842667-144.469333-403.114667-170.368-27.093333-12.970667-40.917333-23.893333-41.386666-34.218667v103.509333c0 10.325333 14.250667 21.290667 41.386666 34.261334 54.272 25.941333 355.541333 147.626667 403.114667 170.368 47.616 22.784 81.066667 23.082667 141.354667-8.362667 60.245333-31.402667 343.338667-147.797333 397.994666-176.298667 27.776-14.464 40.106667-25.728 40.106667-35.925333v-102.058667l-0.213333-0.085333z m0-168.746667c-0.512 9.770667-13.397333 20.650667-39.893334 34.517334-54.613333 28.458667-337.749333 144.896-397.994666 176.298666-60.288 31.402667-93.738667 31.104-141.354667 8.362667-47.616-22.741333-348.842667-144.469333-403.114667-170.410667-27.093333-12.928-40.917333-23.893333-41.386666-34.176v103.509334c0 10.325333 14.250667 21.248 41.386666 34.218666 54.272 25.941333 355.498667 147.626667 403.114667 170.368 47.616 22.784 81.066667 23.082667 141.354667-8.32 60.245333-31.402667 343.338667-147.84 397.994666-176.298666 27.776-14.506667 40.106667-25.770667 40.106667-35.968v-102.058667l-0.256-0.042667z m0-175.018666c0.469333-10.410667-13.141333-19.541333-40.533334-29.610667-53.248-19.498667-334.634667-131.498667-388.522666-151.253333-53.888-19.712-75.818667-18.901333-139.093334 3.84C392.234667 113.706667 92.629333 231.253333 39.338667 252.074667c-26.666667 10.496-39.68 20.181333-39.253334 30.506666V386.133333c0 10.325333 14.250667 21.248 41.386667 34.218667 54.272 25.941333 355.498667 147.669333 403.114667 170.410667 47.616 22.741333 81.066667 23.04 141.354666-8.362667 60.245333-31.402667 343.338667-147.84 397.994667-176.298667 27.776-14.506667 40.106667-25.770667 40.106667-35.968V268.074667h-0.341334zM366.677333 366.08l237.269334-36.437333-71.68 105.088-165.546667-68.650667z m524.8-94.634667l-140.330666 55.466667-15.232 5.973333-140.245334-55.466666 155.392-61.44 140.373334 55.466666z m-411.989333-101.674666l-22.954667-42.325334 71.594667 27.989334 67.498667-22.101334-18.261334 43.733334 68.778667 25.770666-88.704 9.216-19.882667 47.786667-32.085333-53.290667-102.4-9.216 76.416-27.562666z m-176.768 59.733333c70.058667 0 126.805333 21.973333 126.805333 49.109333s-56.746667 49.152-126.805333 49.152-126.848-22.058667-126.848-49.152c0-27.136 56.789333-49.152 126.848-49.152z" p-id="25730"></path></svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -1 +1,9 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621859009605" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9709" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M820.203922 812.172549H684.67451v-45.176471h112.439215V279.090196H633.47451l-85.333334 277.082353c-3.011765 10.039216-12.047059 16.062745-22.086274 16.062745-10.039216 0-19.07451-7.027451-21.082353-17.066667l-71.278431-280.094117h-180.705883V762.980392h120.470589v45.176471H229.898039c-12.047059 0-22.086275-10.039216-22.086274-22.086275V252.988235c0-12.047059 10.039216-22.086275 22.086274-22.086274H451.764706c10.039216 0 19.07451 7.027451 22.086274 17.066666l55.215687 218.854902L595.32549 250.980392c3.011765-9.035294 12.047059-16.062745 21.082353-16.062745h202.792157c12.047059 0 22.086275 10.039216 22.086275 22.086275v533.082353c1.003922 12.047059-9.035294 22.086275-21.082353 22.086274z m0 0" fill="#e25813" p-id="9710"></path><path d="M731.858824 425.662745c4.015686-12.047059-2.007843-25.098039-14.054902-29.113725-12.047059-4.015686-25.098039 2.007843-29.113726 14.054902L563.2 766.996078h-73.286275L371.45098 410.603922c-4.015686-12.047059-17.066667-18.070588-28.109804-14.054902-12.047059 4.015686-18.070588 17.066667-14.054901 28.109804l123.482352 371.45098c3.011765 9.035294 12.047059 15.058824 21.082353 15.058823h72.282353l-53.207843 160.627451 46.180392 2.007844 192.752942-548.141177z" fill="#2c2c2c" p-id="9711"></path></svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1621859009605" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="9709" xmlns:xlink="http://www.w3.org/1999/xlink"
width="200" height="200">
<defs><style type="text/css"></style></defs>
<path d="M820.203922 812.172549H684.67451v-45.176471h112.439215V279.090196H633.47451l-85.333334 277.082353c-3.011765 10.039216-12.047059 16.062745-22.086274 16.062745-10.039216 0-19.07451-7.027451-21.082353-17.066667l-71.278431-280.094117h-180.705883V762.980392h120.470589v45.176471H229.898039c-12.047059 0-22.086275-10.039216-22.086274-22.086275V252.988235c0-12.047059 10.039216-22.086275 22.086274-22.086274H451.764706c10.039216 0 19.07451 7.027451 22.086274 17.066666l55.215687 218.854902L595.32549 250.980392c3.011765-9.035294 12.047059-16.062745 21.082353-16.062745h202.792157c12.047059 0 22.086275 10.039216 22.086275 22.086275v533.082353c1.003922 12.047059-9.035294 22.086275-21.082353 22.086274z m0 0" fill="#e25813" p-id="9710" stroke-width="30" stroke="#e25813"></path>
<path d="M731.858824 425.662745c4.015686-12.047059-2.007843-25.098039-14.054902-29.113725-12.047059-4.015686-25.098039 2.007843-29.113726 14.054902L563.2 766.996078h-73.286275L371.45098 410.603922c-4.015686-12.047059-17.066667-18.070588-28.109804-14.054902-12.047059 4.015686-18.070588 17.066667-14.054901 28.109804l123.482352 371.45098c3.011765 9.035294 12.047059 15.058824 21.082353 15.058823h72.282353l-53.207843 160.627451 46.180392 2.007844 192.752942-548.141177z" fill="#2c2c2c" p-id="9711" stroke-width="30" stroke="#2c2c2c"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,5 +1,5 @@
import request from './request';
import { useApiFetch } from '@/hooks/useRequest';
import { RequestOptions, useApiFetch } from '@/hooks/useRequest';
/**
* 可用于各模块定义各自api请求
@@ -49,7 +49,7 @@ class Api {
* @param reqOptions 其他可选值
* @returns
*/
useApi<T>(params: any = null, reqOptions: RequestInit = {}) {
useApi<T>(params: any = null, reqOptions?: RequestOptions) {
return useApiFetch<T>(this, params, reqOptions);
}
@@ -59,8 +59,8 @@ class Api {
*/
async request(param: any = null, options: any = {}): Promise<any> {
const { execute, data } = this.useApi(param, options);
await execute();
return data.value;
const res = await execute();
return data.value || res;
}
/**

View File

@@ -1,43 +0,0 @@
class SocketBuilder {
websocket: WebSocket;
constructor(url: string) {
if (typeof WebSocket === 'undefined') {
throw new Error('不支持websocket');
}
if (!url) {
throw new Error('websocket url不能为空');
}
this.websocket = new WebSocket(url);
}
static builder(url: string) {
return new SocketBuilder(url);
}
open(onopen: any) {
this.websocket.onopen = onopen;
return this;
}
error(onerror: any) {
this.websocket.onerror = onerror;
return this;
}
message(onmessage: any) {
this.websocket.onmessage = onmessage;
return this;
}
close(onclose: any) {
this.websocket.onclose = onclose;
return this;
}
build() {
return this.websocket;
}
}
export default SocketBuilder;

View File

@@ -1,10 +1,12 @@
import { i18n } from '@/i18n';
import { ElMessage } from 'element-plus';
/**
* 不符合业务断言错误
*/
class AssertError extends Error {
constructor(message: string) {
ElMessage.error(message);
super(message);
// 错误类名
this.name = 'AssertError';
@@ -15,11 +17,11 @@ class AssertError extends Error {
* 断言表达式为true
*
* @param condition 条件表达式
* @param msg 错误消息
* @param msgOrI18nKey 错误消息 或者 i18n key
*/
export function isTrue(condition: boolean, msg: string) {
export function isTrue(condition: boolean, msgOrI18nKey: string) {
if (!condition) {
throw new AssertError(msg);
throw new AssertError(i18n.global.t(msgOrI18nKey));
}
}

View File

@@ -9,12 +9,20 @@ export const I18nEnum = {
En: EnumValue.of('en', 'English').setExtra({ icon: 'icon layout/en', el: enLocale }),
};
export const LinkTypeEnum = {
Iframes: EnumValue.of(1, 'ifrmaes'),
Link: EnumValue.of(2, 'link'),
};
// 资源类型
export const ResourceTypeEnum = {
Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor', iconColor: 'var(--el-color-primary)' }).tagTypeSuccess(),
Db: EnumValue.of(2, '数据库实例').setExtra({ icon: 'Coin', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
Machine: EnumValue.of(1, 'tag.machine').setExtra({ icon: 'icon machine/machine', iconColor: 'var(--el-color-primary)' }).tagTypeSuccess(),
Db: EnumValue.of(2, 'tag.db').setExtra({ icon: 'icon db/db', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'icon redis/redis', iconColor: 'var(--el-color-danger)' }).tagTypeInfo(),
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'icon mongo/mongo', iconColor: 'var(--el-color-success)' }).tagTypeDanger(),
AuthCert: EnumValue.of(5, 'ac.ac').setExtra({ icon: 'Ticket', iconColor: 'var(--el-color-success)' }),
Es: EnumValue.of(6, 'tag.es').setExtra({ icon: 'icon es/es-color', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
Container: EnumValue.of(7, 'tag.container').setExtra({ icon: 'icon docker/docker', iconColor: 'var(--el-color-primary)' }),
};
// 标签关联的资源类型
@@ -24,11 +32,13 @@ export const TagResourceTypeEnum = {
Machine: ResourceTypeEnum.Machine,
DbInstance: ResourceTypeEnum.Db,
EsInstance: ResourceTypeEnum.Es,
Redis: ResourceTypeEnum.Redis,
Mongo: ResourceTypeEnum.Mongo,
AuthCert: EnumValue.of(5, '授权凭证').setExtra({ icon: 'Ticket', iconColor: 'var(--el-color-success)' }),
AuthCert: ResourceTypeEnum.AuthCert,
Container: ResourceTypeEnum.Container,
Db: EnumValue.of(22, '数据库').setExtra({ icon: 'Coin' }),
Db: EnumValue.of(22, '数据库').setExtra({ icon: 'icon db/db' }),
};
// 标签关联的资源类型路径
@@ -37,4 +47,33 @@ export const TagResourceTypePath = {
DbInstanceAuthCert: `${TagResourceTypeEnum.DbInstance.value}/${TagResourceTypeEnum.AuthCert.value}`,
Db: `${TagResourceTypeEnum.DbInstance.value}/${TagResourceTypeEnum.AuthCert.value}/${TagResourceTypeEnum.Db.value}`,
Es: `${TagResourceTypeEnum.EsInstance.value}/${TagResourceTypeEnum.AuthCert.value}`,
};
// 消息子类型
export const MsgSubtypeEnum = {
UserLogin: EnumValue.of('user.login', 'login.login').setExtra({
notifyType: 'primary',
}),
MachineFileUploadSuccess: EnumValue.of('machine.file.upload.success', 'machine.fileUploadSuccess').setExtra({
notifyType: 'success',
}),
MachineFileUploadFail: EnumValue.of('machine.file.upload.fail', 'machine.fileUploadFail').setExtra({
notifyType: 'danger',
}),
DbDumpFail: EnumValue.of('db.dump.fail', 'db.dbDumpFail').setExtra({
notifyType: 'danger',
}),
SqlScriptRunSuccess: EnumValue.of('db.sqlscript.run.success', 'db.sqlScriptRunSuccess').setExtra({
notifyType: 'success',
}),
SqlScriptRunFail: EnumValue.of('db.sqlscript.run.fail', 'db.sqlScriptRunFail').setExtra({
notifyType: 'danger',
}),
FlowUserTaskTodo: EnumValue.of('flow.usertask.todo', 'flow.todoTask').setExtra({
notifyType: 'primary',
}),
};

View File

@@ -15,7 +15,7 @@ const config = {
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
// 系统版本
version: 'v1.9.3',
version: 'v1.10.4',
};
export default config;

View File

@@ -1,5 +1,8 @@
import CryptoJS from 'crypto-js';
import { getToken } from '@/common/utils/storage';
import openApi from './openApi';
import JSEncrypt from 'jsencrypt';
import { notBlank } from './assert';
/**
* AES 加密数据
@@ -36,3 +39,36 @@ export function AesDecrypt(word: string, key?: string): string {
return decrypted.toString(CryptoJS.enc.Base64);
}
var encryptor: any = null;
export async function getRsaPublicKey() {
let publicKey = sessionStorage.getItem('RsaPublicKey');
if (publicKey) {
return publicKey;
}
publicKey = (await openApi.getPublicKey()) as string;
sessionStorage.setItem('RsaPublicKey', publicKey);
return publicKey;
}
/**
* 公钥加密指定值
*
* @param value value
* @returns 加密后的值
*/
export async function RsaEncrypt(value: any) {
// 不存在则返回空值
if (!value) {
return '';
}
if (encryptor != null && sessionStorage.getItem('RsaPublicKey') != null) {
return encryptor.encrypt(value);
}
encryptor = new JSEncrypt();
const publicKey = (await getRsaPublicKey()) as string;
notBlank(publicKey, '获取公钥失败');
encryptor.setPublicKey(publicKey); //设置公钥
return encryptor.encrypt(value);
}

View File

@@ -1,11 +0,0 @@
import { i18n } from '@/i18n';
export const AccountUsernamePattern = {
pattern: /^[a-zA-Z0-9_]{5,16}$/g,
message: i18n.global.t('system.account.usernamePatternErrMsg'),
};
export const ResourceCodePattern = {
pattern: /^[a-zA-Z0-9_\-.:]{1,32}$/g,
message: i18n.global.t('system.menu.resourceCodePatternErrMsg'),
};

View File

@@ -204,6 +204,24 @@ function getApiUrl(url: string) {
return baseUrl + url + '?' + joinClientParams();
}
/**
* 创建 websocket
*/
export const createWebSocket = (url: string): Promise<WebSocket> => {
return new Promise<WebSocket>((resolve, reject) => {
const clientParam = (url.includes('?') ? '&' : '?') + joinClientParams();
const socket = new WebSocket(`${config.baseWsUrl}${url}${clientParam}`);
socket.onopen = () => {
resolve(socket);
};
socket.onerror = (e) => {
reject(e);
};
});
};
// 组装客户端参数,包括 token 和 clientId
export function joinClientParams(): string {
return `token=${getToken()}&clientId=${getClientId()}`;

View File

@@ -1,36 +0,0 @@
import openApi from './openApi';
import JSEncrypt from 'jsencrypt';
import { notBlank } from './assert';
var encryptor: any = null;
export async function getRsaPublicKey() {
let publicKey = sessionStorage.getItem('RsaPublicKey');
if (publicKey) {
return publicKey;
}
publicKey = (await openApi.getPublicKey()) as string;
sessionStorage.setItem('RsaPublicKey', publicKey);
return publicKey;
}
/**
* 公钥加密指定值
*
* @param value value
* @returns 加密后的值
*/
export async function RsaEncrypt(value: any) {
// 不存在则返回空值
if (!value) {
return '';
}
if (encryptor != null && sessionStorage.getItem('RsaPublicKey') != null) {
return encryptor.encrypt(value);
}
encryptor = new JSEncrypt();
const publicKey = (await getRsaPublicKey()) as string;
notBlank(publicKey, '获取公钥失败');
encryptor.setPublicKey(publicKey); //设置公钥
return encryptor.encrypt(value);
}

View File

@@ -0,0 +1,41 @@
import { useI18nPleaseInput, useI18nPleaseSelect } from '@/hooks/useI18n';
import { i18n } from '@/i18n';
/**
* 表单验证规则
* label: 支持 i18n key
*/
export const Rules = {
requiredInput: (label: string = '', trigger: string[] = ['change', 'blur']) => {
return {
required: true,
message: useI18nPleaseInput(label),
trigger: trigger,
};
},
requiredSelect: (label: string = '', trigger: string[] = ['change', 'blur']) => {
return {
required: true,
message: useI18nPleaseSelect(label),
trigger: trigger,
};
},
accountUsername: {
pattern: /^[a-zA-Z0-9_.@:-]{5,16}$/,
message: i18n.global.t('system.account.usernamePatternErrMsg'),
trigger: 'blur',
},
accountPassword: {
pattern: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[`~!@#$%^&*()_+<>?:"{},.\/\\;'[\]])[A-Za-z\d`~!@#$%^&*()_+<>?:"{},.\/\\;'[\]]{8,}$/,
message: i18n.global.t('login.passwordRuleTip'),
trigger: 'blur',
},
resourceCode: {
pattern: /^[a-zA-Z0-9_\-.:]{1,32}$/g,
message: i18n.global.t('system.menu.resourceCodePatternErrMsg'),
trigger: 'blur',
},
};

View File

@@ -4,15 +4,15 @@ import { h, reactive } from 'vue';
import { ElNotification } from 'element-plus';
import ProgressNotify from '@/components/progress-notify/progress-notify.vue';
export function initSysMsgs() {
registerDbSqlExecProgress();
export async function initSysMsgs() {
await registerDbSqlExecProgress();
}
const sqlExecNotifyMap: Map<string, any> = new Map();
function registerDbSqlExecProgress() {
syssocket.registerMsgHandler('execSqlFileProgress', function (message: any) {
const content = JSON.parse(message.msg);
async function registerDbSqlExecProgress() {
await syssocket.registerMsgHandler('sqlScriptRunProgress', function (message: any) {
const content = message.params;
const id = content.id;
let progress = sqlExecNotifyMap.get(id);
if (content.terminated) {
@@ -38,7 +38,7 @@ function registerDbSqlExecProgress() {
duration: 0,
title: message.title,
message: h(ProgressNotify, progress.props),
type: syssocket.getMsgType(message.type),
type: 'info',
showClose: false,
});
sqlExecNotifyMap.set(id, progress);

View File

@@ -1,34 +1,27 @@
import Config from './config';
import SocketBuilder from './SocketBuilder';
import { getToken } from '@/common/utils/storage';
import { joinClientParams } from './request';
import { createWebSocket } from './request';
import { ElNotification } from 'element-plus';
import { MsgSubtypeEnum } from './commonEnum';
import EnumValue from './Enum';
import { h } from 'vue';
import { MessageRenderer } from '@/components/message/message';
class SysSocket {
/**
* socket连接
*/
socket: any;
socket: WebSocket | null = null;
/**
* key -> 消息类别value -> 消息对应的处理器函数
*/
categoryHandlers: Map<string, any> = new Map();
/**
* 消息类型
*/
messageTypes: any = {
0: 'error',
1: 'success',
2: 'info',
};
/**
* 初始化全局系统消息websocket
*/
init() {
async init() {
// 存在则不需要重新建立连接
if (this.socket) {
return;
@@ -38,9 +31,9 @@ class SysSocket {
return null;
}
console.log('init system ws');
const sysMsgUrl = `${Config.baseWsUrl}/sysmsg?${joinClientParams()}`;
this.socket = SocketBuilder.builder(sysMsgUrl)
.message((event: { data: string }) => {
try {
this.socket = await createWebSocket('/sysmsg');
this.socket.onmessage = async (event: { data: string }) => {
let message;
try {
message = JSON.parse(event.data);
@@ -56,23 +49,32 @@ class SysSocket {
return;
}
// 默认通知处理
const type = this.getMsgType(message.type);
let msg = message.msg;
let duration = 0;
const msgSubtype = EnumValue.getEnumByValue(MsgSubtypeEnum, message.subtype);
if (!msgSubtype) {
console.log(`not found msg subtype: ${message.subtype}`);
return;
}
// 动态导入 i18n 或延迟获取 i18n 实例
let title = '';
try {
// 方式1: 动态导入
const { i18n } = await import('@/i18n');
title = i18n.global.t(msgSubtype?.label);
} catch (e) {
console.warn('i18n not ready, using default title');
}
ElNotification({
duration: duration,
title: message.title,
message: msg,
type: type,
duration: 0,
title,
message: h(MessageRenderer, { content: message.msg }),
type: msgSubtype?.extra.notifyType || 'info',
});
})
.open((event: any) => console.log(event))
.close(() => {
console.log('close sys socket');
this.socket = null;
})
.build();
};
} catch (e) {
console.error('open system ws error', e);
}
}
destory() {
@@ -87,8 +89,7 @@ class SysSocket {
* @param category 消息类别
* @param handlerFunc 消息处理函数
*/
registerMsgHandler(category: any, handlerFunc: any) {
this.init();
async registerMsgHandler(category: any, handlerFunc: any) {
if (this.categoryHandlers.has(category)) {
console.log(`${category}该类别消息处理器已存在...`);
return;
@@ -98,10 +99,6 @@ class SysSocket {
}
this.categoryHandlers.set(category, handlerFunc);
}
getMsgType(msgType: any) {
return this.messageTypes[msgType];
}
}
// 全局系统消息websocket;

View File

@@ -42,4 +42,5 @@ export function exportFile(filename: string, content: string) {
link.setAttribute('download', `${filename}`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // 下载完成后移除元素
}

View File

@@ -30,6 +30,18 @@ export function formatByteSize(size: number, fixed = 2) {
return parseFloat((size / Math.pow(base, exponent)).toFixed(fixed)) + units[exponent];
}
export function formatDocSize(size: number, fixed = 2) {
if (size === 0) {
return '0';
}
const units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
const base = 1000;
const exponent = Math.floor(Math.log(size) / Math.log(base));
return parseFloat((size / Math.pow(base, exponent)).toFixed(fixed)) + units[exponent];
}
/**
* 容量转为对应的字节大小,如 1KB转为 1024
* @param sizeString 1kb 1gb等
@@ -86,8 +98,8 @@ export function formatTime(time: number, unit: string = 's') {
let result = '';
const timeUnits = Object.entries(units).map(([unit, duration]) => {
const value = Math.floor(seconds / duration);
seconds %= duration;
const value = Math.floor(seconds / (duration as any));
seconds %= duration as any;
return { value, unit };
});

View File

@@ -1,5 +1,7 @@
import { nextTick } from 'vue';
import '@/theme/loading.scss';
import { useThemeConfig } from '@/store/themeConfig';
import { storeToRefs } from 'pinia';
/**
* 页面全局 Loading
@@ -9,33 +11,57 @@ import '@/theme/loading.scss';
export const NextLoading = {
// 创建 loading
start: () => {
// 如果已经存在loading元素则不重复创建
if (document.querySelector('.loading-next')) {
return;
}
const bodys: Element = document.body;
const div = <HTMLElement>document.createElement('div');
div.setAttribute('class', 'loading-next');
const { themeConfig } = storeToRefs(useThemeConfig());
if (themeConfig.value.isDark) {
div.classList.add('dark');
}
const htmls = `
<div class="loading-next-box">
<div class="loading-next-box-warp">
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
</div>
</div>
`;
<div class="loading-next-box">
<div class="loading-next-box-warp">
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
</div>
</div>
`;
div.innerHTML = htmls;
bodys.insertBefore(div, bodys.childNodes[0]);
// 插入到body的第一个子元素之前避免影响布局
if (bodys.firstChild) {
bodys.insertBefore(div, bodys.firstChild);
} else {
bodys.appendChild(div);
}
},
// 移除 loading
done: (time: number = 1000) => {
done: (time: number = 500) => {
nextTick(() => {
setTimeout(() => {
const el = <HTMLElement>document.querySelector('.loading-next');
el?.parentNode?.removeChild(el);
if (el) {
// 添加淡出效果
el.style.transition = 'opacity 0.3s ease-out';
el.style.opacity = '0';
setTimeout(() => {
el?.parentNode?.removeChild(el);
}, 300);
}
}, time);
});
},

View File

@@ -1,8 +0,0 @@
// https://www.npmjs.com/package/mitt
import mitt, { Emitter } from 'mitt';
// 类型
const emitter: Emitter<any> = mitt<any>();
// 导出
export default emitter;

View File

@@ -12,7 +12,13 @@ import { ElMessage } from 'element-plus';
export function templateResolve(template: string, param: any) {
return template.replace(/\{\w+\}/g, (word) => {
const key = word.substring(1, word.length - 1);
const value = param[key];
let value;
// 兼容FormData类型的参数
if (param instanceof FormData) {
value = param.get(key);
} else {
value = param[key];
}
if (value != null || value != undefined) {
return value;
}

View File

@@ -1,241 +0,0 @@
/**
* 2020.11.29 lyt 整理
* 工具类集合,适用于平时开发
*/
// 小数或整数(不可以负数)
export function verifyNumberIntegerAndFloat(val: string) {
// 匹配空格
let v = val.replace(/(^\s*)|(\s*$)/g, '');
// 只能是数字和小数点,不能是其他输入
v = v.replace(/[^\d.]/g, '');
// 以0开始只能输入一个
v = v.replace(/^0{2}$/g, '0');
// 保证第一位只能是数字,不能是点
v = v.replace(/^\./g, '');
// 小数只能出现1位
v = v.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
// 小数点后面保留2位
v = v.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
// 返回结果
return v;
}
// 正整数验证
export function verifiyNumberInteger(val: string) {
// 匹配空格
let v = val.replace(/(^\s*)|(\s*$)/g, '');
// 去掉 '.' , 防止贴贴的时候出现问题 如 0.1.12.12
v = v.replace(/[\.]*/g, '');
// 去掉以 0 开始后面的数, 防止贴贴的时候出现问题 如 00121323
v = v.replace(/(^0[\d]*)$/g, '0');
// 首位是0,只能出现一次
v = v.replace(/^0\d$/g, '0');
// 只匹配数字
v = v.replace(/[^\d]/g, '');
// 返回结果
return v;
}
// 去掉中文及空格
export function verifyCnAndSpace(val: string) {
// 匹配中文与空格
let v = val.replace(/[\u4e00-\u9fa5\s]+/g, '');
// 匹配空格
v = v.replace(/(^\s*)|(\s*$)/g, '');
// 返回结果
return v;
}
// 去掉英文及空格
export function verifyEnAndSpace(val: string) {
// 匹配英文与空格
let v = val.replace(/[a-zA-Z]+/g, '');
// 匹配空格
v = v.replace(/(^\s*)|(\s*$)/g, '');
// 返回结果
return v;
}
// 禁止输入空格
export function verifyAndSpace(val: string) {
// 匹配空格
let v = val.replace(/(^\s*)|(\s*$)/g, '');
// 返回结果
return v;
}
// 金额用 `,` 区分开
export function verifyNumberComma(val: string) {
// 调用小数或整数(不可以负数)方法
let v: any = verifyNumberIntegerAndFloat(val);
// 字符串转成数组
v = v.toString().split('.');
// \B 匹配非单词边界,两边都是单词字符或者两边都是非单词字符
v[0] = v[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// 数组转字符串
v = v.join('.');
// 返回结果
return v;
}
// 匹配文字变色(搜索时)
export function verifyTextColor(val: string, text = '', color = 'red') {
// 返回内容,添加颜色
let v = text.replace(new RegExp(val, 'gi'), `<span style='color: ${color}'>${val}</span>`);
// 返回结果
return v;
}
// 数字转中文大写
export function verifyNumberCnUppercase(val: any, unit = '仟佰拾亿仟佰拾万仟佰拾元角分', v = '') {
// 当前内容字符串添加 2个0为什么??
val += '00';
// 返回某个指定的字符串值在字符串中首次出现的位置,没有出现,则该方法返回 -1
let lookup = val.indexOf('.');
// substring不包含结束下标内容substr包含结束下标内容
if (lookup >= 0) val = val.substring(0, lookup) + val.substr(lookup + 1, 2);
// 根据内容 val 的长度,截取返回对应大写
unit = unit.substr(unit.length - val.length);
// 循环截取拼接大写
for (let i = 0; i < val.length; i++) {
v += '零壹贰叁肆伍陆柒捌玖'.substr(val.substr(i, 1), 1) + unit.substr(i, 1);
}
// 正则处理
v = v
.replace(/零角零分$/, '整')
.replace(/零[仟佰拾]/g, '零')
.replace(/零{2,}/g, '零')
.replace(/零([亿|万])/g, '$1')
.replace(/零+元/, '元')
.replace(/亿零{0,3}万/, '亿')
.replace(/^元/, '零元');
// 返回结果
return v;
}
// 手机号码
export function verifyPhone(val: string) {
// false: 手机号码不正确
if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\d{8}$/.test(val)) return false;
// true: 手机号码正确
else return true;
}
// 国内电话号码
export function verifyTelPhone(val: string) {
// false: 国内电话号码不正确
if (!/\d{3}-\d{8}|\d{4}-\d{7}/.test(val)) return false;
// true: 国内电话号码正确
else return true;
}
// 登录账号 (字母开头允许5-16字节允许字母数字下划线)
export function verifyAccount(val: string) {
// false: 登录账号不正确
if (!/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/.test(val)) return false;
// true: 登录账号正确
else return true;
}
// 密码 (以字母开头长度在6~16之间只能包含字母、数字和下划线)
export function verifyPassword(val: string) {
// false: 密码不正确
if (!/^[a-zA-Z]\w{5,15}$/.test(val)) return false;
// true: 密码正确
else return true;
}
// 强密码 (字母+数字+特殊字符长度在6-16之间)
export function verifyPasswordPowerful(val: string) {
// false: 强密码不正确
if (!/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val))
return false;
// true: 强密码正确
else return true;
}
// 密码强度
export function verifyPasswordStrength(val: string) {
let v = '';
// 弱:纯数字,纯字母,纯特殊字符
if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\.*]+){6,16}$/.test(val)) v = '弱';
// 中:字母+数字,字母+特殊字符,数字+特殊字符
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) v = '中';
// 强:字母+数字+特殊字符
if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\.*]+$)(?![\d!@#$%^&\.*]+$)[a-zA-Z\d!@#$%^&\.*]{6,16}$/.test(val)) v = '强';
// 返回结果
return v;
}
// IP地址
export function verifyIPAddress(val: string) {
// false: IP地址不正确
if (!/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test(val))
return false;
// true: IP地址正确
else return true;
}
// 邮箱
export function verifyEmail(val: string) {
// false: 邮箱不正确
if (
!/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
val
)
)
return false;
// true: 邮箱正确
else return true;
}
// 身份证
export function verifyIdCard(val: string) {
// false: 身份证不正确
if (!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(val)) return false;
// true: 身份证正确
else return true;
}
// 姓名
export function verifyFullName(val: string) {
// false: 姓名不正确
if (!/^[\u4e00-\u9fa5]{1,6}(·[\u4e00-\u9fa5]{1,6}){0,2}$/.test(val)) return false;
// true: 姓名正确
else return true;
}
// 邮政编码
export function verifyPostalCode(val: string) {
// false: 邮政编码不正确
if (!/^[1-9][0-9]{5}$/.test(val)) return false;
// true: 邮政编码正确
else return true;
}
// url
export function verifyUrl(val: string) {
// false: url不正确
if (
!/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
val
)
)
return false;
// true: url正确
else return true;
}
// 车牌号
export function verifyCarNum(val: string) {
// false: 车牌号不正确
if (
!/^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(
val
)
)
return false;
// true车牌号正确
else return true;
}

View File

@@ -1,13 +0,0 @@
const mode = import.meta.env.VITE_ROUTER_MODE;
/**
* @description 获取不同路由模式所对应的 url
* @returns {String}
*/
export function getNowUrl() {
const url = {
hash: location.hash.substring(1),
history: location.pathname + location.search,
};
return url[mode];
}

View File

@@ -1,27 +0,0 @@
// vite 打包相关
import dotenv from 'dotenv';
export interface ViteEnv {
VITE_PORT: number;
VITE_OPEN: boolean;
VITE_PUBLIC_PATH: string;
VITE_EDITOR: string;
}
export function loadEnv(): ViteEnv {
const env = process.env.NODE_ENV;
const ret: any = {};
const envList = [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env', ,];
envList.forEach((e) => {
dotenv.config({ path: e });
});
for (const envName of Object.keys(process.env)) {
console.log(envName);
let realName = (process.env as any)[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') realName = Number(realName);
if (envName === 'VITE_OPEN') realName = Boolean(realName);
ret[envName] = realName;
process.env[envName] = realName;
}
return ret;
}

View File

@@ -5,7 +5,7 @@ import { useUserInfo } from '@/store/userInfo';
* @param code 权限code
* @returns
*/
export function hasPerm(code: string) {
export function hasPerm(code: string): boolean {
if (!code) {
return true;
}
@@ -17,7 +17,7 @@ export function hasPerm(code: string) {
* @returns {"xxx:save": true} key->permission code
* @param permCodes
*/
export function hasPerms(permCodes: any[]) {
export function hasPerms(permCodes: any[]): Record<string, boolean> {
const res = {} as { [key: string]: boolean };
for (let permCode of permCodes) {
if (hasPerm(permCode)) {

View File

@@ -1,13 +1,14 @@
<template>
<transition @enter="onEnter" name="el-zoom-in-center">
<div
aria-hidden="true"
:aria-hidden="state.isShow ? 'false' : 'true'"
class="el-dropdown__popper el-popper is-light is-pure custom-contextmenu"
role="tooltip"
data-popper-placement="bottom"
:style="`top: ${state.dropdown.y + 5}px;left: ${state.dropdown.x}px;`"
:key="Math.random()"
v-show="state.isShow && !allHide"
@contextmenu="headerContextmenuClick"
>
<ul class="el-dropdown-menu">
<template v-for="(v, k) in state.dropdownList">
@@ -125,6 +126,10 @@ const onCurrentContextmenuClick = (ci: ContextmenuItem) => {
emit('currentContextmenuClick', { id: ci.clickId, item: state.item });
};
const headerContextmenuClick = (event: any) => {
event.preventDefault(); // 阻止默认的右击菜单行为
};
// 打开右键菜单:判断是否固定,固定则不显示关闭按钮
const openContextmenu = (item: any) => {
state.item = item;

View File

@@ -37,9 +37,9 @@
</el-form-item>
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="7" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 7" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="7" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 7" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="item in 31" :key="item" :value="`${item}`">{{ item }}</el-option>
</el-select>
</div>

View File

@@ -22,9 +22,9 @@
</el-form-item>
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="item in 60" :key="item" :value="`${item - 1}`">{{ item - 1 }}</el-option>
</el-select>
</div>

View File

@@ -22,9 +22,9 @@
</el-form-item>
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="item in 60" :key="item" :value="`${item - 1}`">{{ item - 1 }}</el-option>
</el-select>
</div>

View File

@@ -22,9 +22,9 @@
</el-form-item>
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="item in 12" :key="item" :value="`${item}`">{{ item }}</el-option>
</el-select>
</div>

View File

@@ -22,9 +22,9 @@
</el-form-item>
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="4" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="4" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 4" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="item in 60" :key="item" :value="`${item - 1}`">{{ item - 1 }}</el-option>
</el-select>
</div>

View File

@@ -32,9 +32,9 @@
</el-form-item> -->
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="6" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 6" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="6" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 6" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="(item, index) of weekList" :label="item" :key="index" :value="`${index + 1}`">{{ $t(item) }}</el-option>
</el-select>
</div>

View File

@@ -26,9 +26,9 @@
</el-form-item>
<el-form-item>
<div class="flex-align-center w100">
<el-radio v-model="radioValue" :label="5" class="mr5"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 5" class="w100" clearable v-model="checkboxList" multiple>
<div class="flex items-center w-full">
<el-radio v-model="radioValue" :label="5" class="mr-1"> {{ $t('components.crontab.appoint') }} </el-radio>
<el-select @click="radioValue = 5" class="!w-full" clearable v-model="checkboxList" multiple>
<el-option v-for="item in 9" :key="item" :value="`${item - 1 + fullYear}`" :label="item - 1 + fullYear" />
</el-select>
</div>

View File

@@ -1,145 +0,0 @@
<template>
<div>
<el-dialog title="更换头像" v-model="isShowDialog" width="769px">
<div class="cropper-warp">
<div class="cropper-warp-left">
<img :src="cropperImg" class="cropper-warp-left-img" />
</div>
<div class="cropper-warp-right">
<div class="cropper-warp-right-title">预览</div>
<div class="cropper-warp-right-item">
<div class="cropper-warp-right-value">
<img :src="cropperImgBase64" class="cropper-warp-right-value-img" />
</div>
<div class="cropper-warp-right-label">100 x 100</div>
</div>
<div class="cropper-warp-right-item">
<div class="cropper-warp-right-value">
<img :src="cropperImgBase64" class="cropper-warp-right-value-img cropper-size" />
</div>
<div class="cropper-warp-right-label">50 x 50</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, nextTick } from 'vue';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
export default {
name: 'cropperIndex',
setup() {
const state = reactive({
isShowDialog: false,
cropperImg: '',
cropperImgBase64: '',
});
// 打开弹窗
const openDialog = (imgs: any) => {
state.cropperImg = imgs;
state.isShowDialog = true;
nextTick(() => {
initCropper();
});
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {};
// 初始化cropperjs图片裁剪
const initCropper = () => {
const letImg: any = document.querySelector('.cropper-warp-left-img');
const cropper = new Cropper(letImg, {
viewMode: 1,
dragMode: 'none',
initialAspectRatio: 1,
aspectRatio: 1,
preview: '.before',
background: false,
autoCropArea: 0.6,
zoomOnWheel: false,
crop: () => {
state.cropperImgBase64 = cropper.getCroppedCanvas().toDataURL('image/jpeg');
},
});
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
initCropper,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.cropper-warp {
display: flex;
.cropper-warp-left {
position: relative;
display: inline-block;
height: 350px;
flex: 1;
border: 1px solid #ebeef5;
background: #fff;
overflow: hidden;
background-repeat: no-repeat;
cursor: move;
border-radius: 3px;
.cropper-warp-left-img {
width: 100%;
height: 100%;
}
}
.cropper-warp-right {
width: 150px;
height: 350px;
.cropper-warp-right-title {
text-align: center;
height: 20px;
line-height: 20px;
}
.cropper-warp-right-item {
margin: 15px 0;
.cropper-warp-right-value {
display: flex;
.cropper-warp-right-value-img {
width: 100px;
height: 100px;
border-radius: 100%;
margin: auto;
}
.cropper-size {
width: 50px;
height: 50px;
}
}
.cropper-warp-right-label {
text-align: center;
font-size: 12px;
color: #666666;
height: 30px;
line-height: 30px;
}
}
}
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<FormDesign ref="makingForm" upload preview generate-code generate-json clearable> </FormDesign>
<!-- <dev></dev> -->
</template>
<script lang="ts" setup>
// import { FormDesign } from 'mayfly-lc';
</script>

View File

@@ -1,10 +1,10 @@
<template>
<div class="dynamic-form-edit w100">
<el-table :data="formItems" stripe class="w100">
<div class="dynamic-form-edit !w-full">
<el-table :data="formItems" stripe class="!w-full">
<el-table-column prop="name" label="model" min-width="100px">
<template #header>
<el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="addItem()"> </el-button>
<span class="ml10">model field</span>
<span class="ml-2">model field</span>
</template>
<template #default="scope">
<el-input v-model="scope.row['model']" :placeholder="$t('components.df.fieldModelPlaceholder')" clearable> </el-input>

View File

@@ -7,7 +7,7 @@
<script lang="ts" setup>
const props = defineProps({
enums: {
type: Object, // 需要为EnumValue类型
type: Object || Array, // 需要为EnumValue类型
required: true,
},
});

View File

@@ -12,8 +12,9 @@ const props = defineProps({
required: true,
},
value: {
type: [Object, String, Number],
type: [Object, String, Number, null, Boolean],
required: true,
default: () => null,
},
});
@@ -40,7 +41,7 @@ onMounted(() => {
});
const convert = (value: any) => {
const enumValue = EnumValue.getEnumByValue(props.enums, value) as any;
const enumValue = EnumValue.getEnumByValue(props.enums, value);
if (!enumValue) {
state.enumLabel = '-';
state.type = 'danger';
@@ -50,8 +51,8 @@ const convert = (value: any) => {
state.enumLabel = enumValue?.label || '';
if (enumValue.tag) {
state.color = enumValue.tag.color;
state.type = enumValue.tag.type;
state.color = enumValue.tag.color || '';
state.type = enumValue.tag.type || defaultType;
} else {
state.type = defaultType;
}

View File

@@ -1,14 +1,16 @@
<template>
<el-form-item v-bind="$attrs">
<template #label>
{{ props.label }}
<div class="flex items-center">
{{ props.label }}
<el-tooltip :placement="props.placement">
<template #content>
<span v-html="props.tooltip"></span>
</template>
<SvgIcon name="QuestionFilled" />
</el-tooltip>
<el-tooltip :placement="props.placement">
<template #content>
<span v-html="props.tooltip"></span>
</template>
<SvgIcon name="QuestionFilled" class="ml-1" />
</el-tooltip>
</div>
</template>
<!-- 遍历父组件传入的 solts 透传给子组件 -->
@@ -24,11 +26,11 @@ import { useSlots } from 'vue';
const props = defineProps({
label: {
type: String,
require: true,
required: true,
},
tooltip: {
type: String,
require: true,
required: true,
},
placement: {
type: String,

View File

@@ -1,5 +1,5 @@
<template>
<div class="icon-selector w100 h100">
<div class="icon-selector !w-full !h-full">
<el-input
v-model="state.fontIconSearch"
:placeholder="state.fontIconPlaceholder"
@@ -12,7 +12,7 @@
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon :name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14" />
<SvgIcon :name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="!text-[14px]" />
</template>
</el-input>
<el-popover
@@ -25,7 +25,7 @@
virtual-triggering
>
<template #default>
<div class="ml5 mt5">{{ $t(title) }}</div>
<div class="ml-1 mt-1">{{ $t(title) }}</div>
<div class="icon-selector-warp">
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
<el-tab-pane lazy label="ele" name="ele">

View File

@@ -0,0 +1,129 @@
import { ElLink, ElText } from 'element-plus';
import { defineAsyncComponent, defineComponent, h } from 'vue';
type Size = 'large' | 'default' | 'small';
interface ComponentConfig {
component: any;
getDefaultProps?: (size: Size) => Record<string, any>;
}
const linkConf = {
component: ElLink,
getDefaultProps: (size: Size) => {
return {
type: 'primary',
verticalAlign: 'baseline',
style: {
fontSize: size === 'small' ? '12px' : size === 'large' ? '16px' : '14px',
verticalAlign: 'baseline',
},
};
},
};
const components = {
'el-link': linkConf,
a: linkConf,
'error-text': {
component: ElText,
getDefaultProps: (size: Size) => {
return {
type: 'danger',
size,
};
},
},
'machine-info': {
component: defineAsyncComponent(() => import('@/views/ops/machine/component/MachineDetail.vue')),
getDefaultProps: (size: Size) => {
return {
size,
};
},
},
'db-info': {
component: defineAsyncComponent(() => import('@/views/ops/db/component/DbDetail.vue')),
getDefaultProps: (size: Size) => {
return {
size,
};
},
},
} as Record<string, ComponentConfig>;
export const MessageRenderer = defineComponent({
props: {
content: String,
size: {
type: String as () => Size,
default: 'default',
},
},
setup(props) {
const parseContent = (content: string) => {
if (!content) {
return [h('span', '')];
}
// 创建一个包装容器来处理HTML内容
const container = document.createElement('div');
container.innerHTML = content;
const parseNode = (node: Node): any => {
if (node.nodeType === Node.TEXT_NODE) {
return node.textContent;
}
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as HTMLElement;
const tagName = element.tagName.toLowerCase();
let attrs: Record<string, any> = {};
// 提取属性
for (let i = 0; i < element.attributes.length; i++) {
const attr = element.attributes[i];
attrs[attr.name] = attr.value;
}
const componentConf = components[tagName];
if (!componentConf) {
return h(tagName, attrs, Array.from(element.childNodes).map(parseNode));
}
// 存在默认组件配置,则合并
if (componentConf.getDefaultProps) {
const defaultProps = componentConf.getDefaultProps(props.size);
attrs = {
...defaultProps,
...attrs,
};
}
return h(componentConf.component, attrs, {
default: () => Array.from(element.childNodes).map(parseNode),
});
}
return '';
};
return Array.from(container.childNodes).map(parseNode);
};
return () => {
// 根据 size 属性确定根元素的 class
const rootClass = props.size === 'small' ? 'text-sm' : props.size === 'large' ? 'text-lg' : 'text-base';
try {
const elements = parseContent(props.content || '');
return h('div', { class: rootClass }, elements);
} catch (e) {
console.error('消息渲染失败:', e);
return h('div', { class: rootClass }, props.content || '');
}
};
},
});

View File

@@ -1,15 +1,15 @@
<template>
<div class="monaco-editor" style="border: 1px solid var(--el-border-color-light, #ebeef5); height: 100%">
<div class="monaco-editor-custom relative h-full">
<div class="monaco-editor-content" ref="monacoTextareaRef" :style="{ height: height }"></div>
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage" filterable>
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value" />
</el-select>
</div>
</template>
<script lang="ts" setup>
import { watch, toRefs, reactive, onMounted, onBeforeUnmount, useTemplateRef, Ref } from 'vue';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import * as monaco from 'monaco-editor';
// 相关语言
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution.js';
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js';
@@ -31,7 +31,6 @@ import 'monaco-editor/esm/vs/editor/contrib/format//browser/formatActions.js';
// 提示
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js';
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestInlineCompletions.js';
import { editor, languages } from 'monaco-editor';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
@@ -134,6 +133,7 @@ const defaultOptions = {
theme: 'SolarizedLight',
automaticLayout: true, //自适应宽高布局
foldingStrategy: 'indentation', //代码可分小段折叠
folding: true,
roundedSelection: false, // 禁用选择文本背景的圆角
matchBrackets: 'near',
linkedEditing: true,
@@ -149,7 +149,14 @@ const defaultOptions = {
minimap: {
enabled: false, // 不要小地图
},
};
renderLineHighlight: 'all',
selectOnLineNumbers: false,
readOnly: false,
scrollBeyondLastLine: false,
lineNumbers: 'on',
lineNumbersMinChars: 3,
fixedOverflowWidgets: true, // 使弹出层不被容器限制
} as editor.IStandaloneEditorConstructionOptions;
const monacoTextareaRef: Ref<any> = useTemplateRef('monacoTextareaRef');
@@ -221,7 +228,8 @@ const initMonacoEditorIns = () => {
monaco.editor.defineTheme('SolarizedLight', SolarizedLight);
defaultOptions.language = state.languageMode;
defaultOptions.theme = themeConfig.value.editorTheme;
monacoEditorIns = monaco.editor.create(monacoTextareaRef.value, Object.assign(defaultOptions, props.options as any));
let options = Object.assign(defaultOptions, props.options as any);
monacoEditorIns = monaco.editor.create(monacoTextareaRef.value, options);
// 监听内容改变,双向绑定
monacoEditorIns.onDidChangeModelContent(() => {
@@ -309,7 +317,7 @@ defineExpose({ getEditor, format, focus });
</script>
<style lang="scss">
.monaco-editor {
.monaco-editor-custom {
.code-mode-select {
position: absolute;
z-index: 2;
@@ -317,5 +325,8 @@ defineExpose({ getEditor, format, focus });
top: 10px;
max-width: 130px;
}
border: 1px solid var(--el-border-color-light, #ebeef5);
width: 100%;
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<div class="h-full">
<monaco-editor
ref="editorRef"
:height="props.height"
class="editor"
language="text"
v-model="modelValue"
:options="{
readOnly: true,
}"
:can-change-mode="false"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, useTemplateRef, watch } from 'vue';
import { useWebSocket } from '@vueuse/core';
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
const props = defineProps({
height: {
type: String,
default: '100%',
},
wsUrl: {
type: String,
default: '',
},
});
const websocketUrl = ref(props.wsUrl);
const { data } = useWebSocket(websocketUrl);
const editorRef: any = useTemplateRef('editorRef');
const modelValue = defineModel<string>('modelValue', {
type: String,
default: '',
});
watch(data, (value) => {
// eslint-disable-next-line no-control-regex
modelValue.value = modelValue.value + value.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '');
setTimeout(() => {
revealLastLine();
}, 200);
});
const reload = (wsUrl: string) => {
modelValue.value = '';
websocketUrl.value = wsUrl;
revealLastLine();
};
const revealLastLine = () => {
const editor = editorRef.value.getEditor();
const lineCount = editor?.getModel().getLineCount();
editor.revealLine(lineCount);
};
defineExpose({
reload,
});
</script>
<style lang="scss" scoped>
.editor {
font-size: 9pt;
font-weight: 600;
}
</style>

View File

@@ -1,9 +1,8 @@
<template>
<div>
<transition name="el-zoom-in-top">
<div class="h-full flex flex-col flex-1 overflow-hidden">
<transition name="page-table-search-form">
<!-- 查询表单 -->
<SearchForm v-if="isShowSearch" :items="tableSearchItems" v-model="queryForm" :search="search"
:reset="reset" :search-col="searchCol">
<SearchForm v-if="isShowSearch" :items="tableSearchItems" v-model="queryForm" :search="search" :reset="reset" :search-col="searchCol">
<!-- 遍历父组件传入的 solts 透传给子组件 -->
<template v-for="(_, key) in useSlots()" v-slot:[key]>
<slot :name="key"></slot>
@@ -11,83 +10,104 @@
</SearchForm>
</transition>
<div class="card">
<div class="table-main">
<!-- 表格头部 操作按钮 -->
<div class="table-header">
<div class="header-button-lf">
<slot name="tableHeader" />
</div>
<div v-if="toolButton" class="header-button-ri">
<slot name="toolButton">
<div class="tool-button">
<!-- 简易单个搜索项 -->
<div v-if="nowSearchItem" class="simple-search-form">
<el-dropdown v-if="searchItems?.length > 1">
<SvgIcon :size="16" name="CaretBottom" class="mr4 mt6 simple-search-form-btn" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="searchItem in searchItems"
:key="searchItem.prop" @click="changeSimpleFormItem(searchItem)">
{{ $t(searchItem.label) }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="simple-search-form-label mt5">
<el-text truncated tag="b">{{ `${$t(nowSearchItem?.label)} : ` }}</el-text>
</div>
<el-form-item style="width: 200px" :key="nowSearchItem.prop">
<SearchFormItem @keyup.enter.native="searchFormItemKeyUpEnter"
v-if="!nowSearchItem.slot" :item="nowSearchItem"
v-model="queryForm[nowSearchItem.prop]" />
<slot @keyup.enter.native="searchFormItemKeyUpEnter" v-else
:name="nowSearchItem.slot">
</slot>
</el-form-item>
</div>
<div>
<el-button v-if="showToolButton('search') && searchItems?.length" icon="Search"
circle @click="search" />
<!-- <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" /> -->
<el-button v-if="showToolButton('search') && searchItems?.length > 1"
:icon="isShowSearch ? 'ArrowDown' : 'ArrowUp'" circle
@click="isShowSearch = !isShowSearch" />
<el-popover placement="bottom" title="表格配置"
popper-style="max-height: 550px; overflow: auto; max-width: 450px" width="auto"
trigger="click">
<div v-for="(item, index) in tableColumns" :key="index">
<el-checkbox v-model="item.show" :label="item.label" :true-value="true"
:false-value="false" />
</div>
<template #reference>
<el-button icon="Operation" circle :size="props.size"></el-button>
</template>
</el-popover>
</div>
</div>
</slot>
</div>
<el-card class="h-full" body-class="h-full flex flex-col">
<!-- 表格头部 操作按钮 -->
<div class="flex justify-between">
<div>
<slot name="tableHeader" />
</div>
<el-table ref="tableRef" v-bind="$attrs" :max-height="tableMaxHeight"
@selection-change="handleSelectionChange" :data="tableData" highlight-current-row
v-loading="loading" :size="props.size as any" :border="border">
<slot v-if="toolButton" name="toolButton">
<div class="flex">
<!-- 简易单个搜索项 -->
<div v-if="nowSearchItem" class="flex">
<el-dropdown v-if="props.searchItems?.length > 1">
<SvgIcon :size="16" name="CaretBottom" class="!mr-1 !mt-1.5 simple-search-form-btn" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="searchItem in searchItems" :key="searchItem.prop" @click="changeSimpleFormItem(searchItem)">
{{ $t(searchItem.label) }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="text-right mr-1.5 mt-1">
<el-text truncated tag="b">{{ `${$t(nowSearchItem?.label)} : ` }}</el-text>
</div>
<el-form-item class="w-[200px]" :key="nowSearchItem.prop">
<SearchFormItem
@keyup.enter.native="searchFormItemKeyUpEnter"
v-if="!nowSearchItem.slot"
:item="nowSearchItem"
v-model="queryForm[nowSearchItem.prop]"
/>
<slot @keyup.enter.native="searchFormItemKeyUpEnter" v-else :name="nowSearchItem.slot"> </slot>
</el-form-item>
</div>
<div class="ml-2">
<el-button v-if="showToolButton('search') && searchItems?.length" icon="Search" circle @click="search" />
<!-- <el-button v-if="showToolButton('refresh')" icon="Refresh" circle @click="execQuery()" /> -->
<el-button
v-if="showToolButton('search') && props.searchItems?.length > 1"
:icon="isShowSearch ? 'ArrowDown' : 'ArrowUp'"
circle
@click="isShowSearch = !isShowSearch"
/>
<el-popover
placement="bottom"
title="表格配置"
popper-style="max-height: 550px; overflow: auto; max-width: 450px"
width="auto"
trigger="click"
>
<div v-for="(item, index) in tableColumns" :key="index">
<el-checkbox v-model="item.show" :label="$t(item.label)" :true-value="1" :false-value="0" />
</div>
<template #reference>
<el-button icon="Operation" circle :size="props.size"></el-button>
</template>
</el-popover>
</div>
</div>
</slot>
</div>
<div class="flex-1 overflow-auto">
<el-table
v-show="showTable"
ref="tableRef"
v-bind="$attrs"
height="100%"
@selection-change="handleSelectionChange"
:data="tableData"
highlight-current-row
v-loading="loading"
:size="props.size as any"
:border="border"
>
<el-table-column v-if="props.showSelection" :selectable="selectable" type="selection" width="40" />
<template v-for="(item, index) in tableColumns">
<el-table-column :key="index" v-if="item.show" :prop="item.prop" :label="$t(item.label)"
:fixed="item.fixed" :align="item.align" :show-overflow-tooltip="item.showOverflowTooltip"
:min-width="item.minWidth" :sortable="item.sortable || false" :type="item.type"
:width="item.width">
<el-table-column
:key="index"
v-if="item.show"
:prop="item.prop"
:label="$t(item.label)"
:fixed="item.fixed"
:align="item.align"
:show-overflow-tooltip="item.showOverflowTooltip"
:min-width="item.minWidth"
:sortable="item.sortable || false"
:type="item.type"
:width="item.width"
>
<!-- 插槽预留功能 -->
<template #default="scope" v-if="item.slot">
<slot :name="item.slotName ? item.slotName : item.prop" :data="scope.row"></slot>
@@ -95,21 +115,29 @@
<!-- 枚举类型使用tab展示 -->
<template #default="scope" v-else-if="item.type == 'tag'">
<enum-tag :size="props.size" :enums="item.typeParam"
:value="item.getValueByData(scope.row)"></enum-tag>
<enum-tag :size="props.size" :enums="item.typeParam" :value="item.getValueByData(scope.row)"></enum-tag>
</template>
<template #default="scope" v-else>
<!-- 配置了美化文本按钮以及文本内容大于指定长度则显示美化按钮 -->
<el-popover v-if="item.isBeautify && item.getValueByData(scope.row)?.length > 35"
effect="light" trigger="click" placement="top" width="600px">
<el-popover
v-if="item.isBeautify && item.getValueByData(scope.row)?.length > 35"
effect="light"
trigger="click"
placement="top"
width="600px"
>
<template #default>
<el-input :autosize="{ minRows: 3, maxRows: 15 }" disabled v-model="formatVal"
type="textarea" />
<el-input :autosize="{ minRows: 3, maxRows: 15 }" disabled v-model="formatVal" type="textarea" />
</template>
<template #reference>
<el-link @click="formatText(item.getValueByData(scope.row))" :underline="false"
type="success" icon="MagicStick" class="mr5"></el-link>
<el-link
@click="formatText(item.getValueByData(scope.row))"
underline="never"
type="success"
icon="MagicStick"
class="mr-1"
></el-link>
</template>
</el-popover>
@@ -120,38 +148,42 @@
</el-table>
</div>
<el-row v-if="props.pageable" class="mt20" type="flex" justify="end">
<el-pagination :small="props.size == 'small'" @current-change="pageNumChange"
@size-change="pageSizeChange" style="text-align: right" layout="prev, pager, next, total, sizes"
:total="total" v-model:current-page="queryForm.pageNum" v-model:page-size="queryForm.pageSize"
:page-sizes="pageSizes" />
<el-row v-if="props.pageable" class="mt-4" type="flex" justify="end">
<el-pagination
:small="props.size == 'small'"
@current-change="pageNumChange"
@size-change="pageSizeChange"
layout="prev, pager, next, total, sizes"
:total="total"
v-model:current-page="queryForm.pageNum"
v-model:page-size="queryForm.pageSize"
:page-sizes="pageSizes"
/>
</el-row>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue } from 'vue';
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue, h } from 'vue';
import { TableColumn } from './index';
import EnumTag from '@/components/enumtag/EnumTag.vue';
import { useThemeConfig } from '@/store/themeConfig';
import { storeToRefs } from 'pinia';
import { useEventListener } from '@vueuse/core';
import Api from '@/common/Api';
import SearchForm from '@/components/SearchForm/index.vue';
import { SearchItem } from '../SearchForm/index';
import SearchFormItem from '../SearchForm/components/SearchFormItem.vue';
import SearchForm from '@/components/pagetable/SearchForm/index.vue';
import { SearchItem } from './SearchForm/index';
import SearchFormItem from './SearchForm/components/SearchFormItem.vue';
import SvgIcon from '@/components/svgIcon/index.vue';
import { usePageTable } from '@/hooks/usePageTable';
import { ElTable } from 'element-plus';
import { ElInput, ElTable } from 'element-plus';
const emit = defineEmits(['update:selectionData', 'pageSizeChange', 'pageNumChange']);
export interface PageTableProps {
size?: string;
pageApi?: Api; // 请求表格数据的 api
columns: TableColumn[]; // 列配置项 ==> 必传
columns: TableColumn[] | any[]; // 列配置项 ==> 必传
showSelection?: boolean;
selectable?: (row: any) => boolean; // 是否可选
pageable?: boolean;
@@ -208,6 +240,10 @@ const showToolButton = (key: 'setting' | 'search') => {
const nowSearchItem: Ref<SearchItem> = ref(null) as any;
// 是否已经计算列宽度
const isCalculatedWidth: Ref<boolean> = ref(false);
const showTable: Ref<boolean> = ref(false);
/**
* 改变当前的搜索项
* @param searchItem 当前点击的搜索项
@@ -239,24 +275,35 @@ const state = reactive({
pageSizes: [] as any, // 可选每页显示的数据量
// 输入框宽度
formatVal: '', // 格式化后的值
tableMaxHeight: '500px',
});
const { pageSizes, formatVal, tableMaxHeight } = toRefs(state);
const { pageSizes, formatVal } = toRefs(state);
watch(tableData, (newValue: any) => {
if (newValue && newValue.length > 0) {
props.columns.forEach((item) => {
if (item.autoWidth && item.show) {
item.autoCalculateMinWidth(tableData.value);
}
});
calculateTableColumnMinWidth();
// 需要计算完才能显示表格,否则会有表格闪烁的问题
if (!showTable.value) {
showTable.value = true;
}
});
watch(isShowSearch, () => {
calcuTableHeight();
});
/**
* 计算表格列宽
*/
const calculateTableColumnMinWidth = () => {
if (isCalculatedWidth.value || !tableData.value || tableData.value.length === 0) {
return;
}
// 计算表格列宽
props.columns.forEach((item) => {
if (item.autoWidth && item.show) {
item.autoCalculateMinWidth(tableData.value);
}
});
isCalculatedWidth.value = true;
};
watch(
() => props.data,
@@ -266,9 +313,6 @@ watch(
);
onMounted(async () => {
calcuTableHeight();
useEventListener(window, 'resize', calcuTableHeight);
if (props.searchItems.length > 0) {
nowSearchItem.value = props.searchItems[0];
}
@@ -292,11 +336,6 @@ onMounted(async () => {
}
});
const calcuTableHeight = () => {
const headerHeight = isShowSearch.value ? 330 : 250;
state.tableMaxHeight = window.innerHeight - headerHeight + 'px';
};
const searchFormItemKeyUpEnter = (event: any) => {
event.preventDefault();
search();
@@ -327,113 +366,21 @@ defineExpose({
});
</script>
<style scoped lang="scss">
.table-box,
.table-main {
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
height: 100%;
.page-table-search-form-enter-active {
transition: all 0.3s ease-out;
}
// 表格 header 样式
.table-header {
width: 100%;
.page-table-search-form-leave-active {
transition: all 0.3s ease-in;
}
.header-button-lf {
float: left;
}
.page-table-search-form-enter-from {
opacity: 0;
transform: translateY(-30px) scale(0.95);
}
.header-button-ri {
float: right;
.tool-button {
display: flex;
justify-content: space-between;
}
.simple-search-form {
margin-right: 10px;
display: flex;
justify-content: space-between;
::v-deep(.el-form-item__content > *) {
width: 100% !important;
}
.simple-search-form-label {
text-align: right;
margin-right: 5px;
}
.simple-search-form-btn:hover {
color: var(--el-color-primary);
}
}
}
.el-button {
margin-bottom: 10px;
}
}
// el-table 表格样式
.el-table {
flex: 1;
// 修复 safari 浏览器表格错位 https://github.com/HalseySpicy/Geeker-Admin/issues/83
table {
width: 100%;
}
// .el-table__header th {
// height: 45px;
// font-size: 15px;
// font-weight: bold;
// color: var(--el-text-color-primary);
// background: var(--el-fill-color-light);
// }
// .el-table__row {
// height: 45px;
// font-size: 14px;
// .move {
// cursor: move;
// .el-icon {
// cursor: move;
// }
// }
// }
// 设置 el-table 中 header 文字不换行,并省略
.el-table__header .el-table__cell>.cell {
// white-space: nowrap;
white-space: wrap;
}
// 解决表格数据为空时样式不居中问题(仅在element-plus中)
// .el-table__empty-block {
// position: absolute;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -50%);
// .table-empty {
// line-height: 30px;
// }
// }
// table 中 image 图片样式
.table-image {
width: 50px;
height: 50px;
border-radius: 50%;
}
}
::v-deep(.el-form-item__label) {
font-weight: bold;
}
.page-table-search-form-leave-to {
opacity: 0;
transform: translateY(-30px) scale(0.95);
}
</style>

View File

@@ -37,11 +37,11 @@
</template>
<script setup lang="ts" name="SearchForm">
import { computed, ref } from 'vue';
import { BreakPoint } from '@/components/Grid/interface/index';
import { BreakPoint } from '@/components/pagetable/Grid/interface/index';
import { Delete, Search, ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import SearchFormItem from './components/SearchFormItem.vue';
import Grid from '@/components/Grid/index.vue';
import GridItem from '@/components/Grid/components/GridItem.vue';
import Grid from '@/components/pagetable/Grid/index.vue';
import GridItem from '@/components/pagetable/Grid/components/GridItem.vue';
import SvgIcon from '@/components/svgIcon/index.vue';
import { SearchItem } from './index';
@@ -107,7 +107,6 @@ const handleItemKeyupEnter = (item: SearchItem) => {
margin-bottom: 10px;
box-sizing: border-box;
overflow-x: hidden;
background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color-light);
border-radius: 6px;

View File

@@ -71,9 +71,9 @@ export class TableColumn {
formatFunc: Function;
/**
* 是否显示该列
* 是否显示该列,1显示 0不显示
*/
show: boolean = true;
show: number = 1;
/**
* 是否展示美化按钮主要用于美化json文本等

View File

@@ -108,5 +108,7 @@ const setIconSvgInsStyle = computed(() => {
align-items: center;
cursor: pointer;
vertical-align: middle;
height: 100%; /* 确保高度与父元素一致 */
line-height: 1; /* 确保行高与高度一致 */
}
</style>

View File

@@ -3,12 +3,12 @@
<div ref="viewportRef" class="viewport" :style="{ width: state.size.width + 'px', height: state.size.height + 'px' }">
<div ref="displayRef" class="display" tabindex="0" />
<div class="btn-box">
<SvgIcon name="DocumentCopy" @click="openPaste" :size="20" class="pointer-icon mr10" title="剪贴板" />
<SvgIcon name="FolderOpened" @click="openFilesystem" :size="20" class="pointer-icon mr10" title="文件管理" />
<SvgIcon name="FullScreen" @click="state.fullscreen ? closeFullScreen() : openFullScreen()" :size="20" class="pointer-icon mr10" title="全屏" />
<SvgIcon name="DocumentCopy" @click="openPaste" :size="20" class="pointer-icon mr-2" title="剪贴板" />
<SvgIcon name="FolderOpened" @click="openFilesystem" :size="20" class="pointer-icon mr-2" title="文件管理" />
<SvgIcon name="FullScreen" @click="state.fullscreen ? closeFullScreen() : openFullScreen()" :size="20" class="pointer-icon mr-2" title="全屏" />
<el-dropdown>
<SvgIcon name="Monitor" :size="20" class="pointer-icon mr10" title="发送快捷键" style="color: #fff" />
<SvgIcon name="Monitor" :size="20" class="pointer-icon mr-2" title="发送快捷键" style="color: #fff" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openSendKeyboard(['65507', '65513', '65535'])"> Ctrl + Alt + Delete </el-dropdown-item>
@@ -21,7 +21,7 @@
</template>
</el-dropdown>
<SvgIcon name="Refresh" @click="connect(0, 0)" :size="20" class="pointer-icon mr10" title="重新连接" />
<SvgIcon name="Refresh" @click="connect(0, 0)" :size="20" class="pointer-icon mr-2" title="重新连接" />
</div>
<clipboard-dialog ref="clipboardRef" v-model:visible="state.clipboardDialog.visible" @close="closePaste" @submit="onsubmitClipboard" />
@@ -73,8 +73,7 @@ import { TerminalExpose } from '@/components/terminal-rdp/index';
import SvgIcon from '@/components/svgIcon/index.vue';
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
import { exitFullscreen, launchIntoFullscreen, unWatchFullscreenChange, watchFullscreenChange } from '@/components/terminal-rdp/guac/screen';
import { useEventListener } from '@vueuse/core';
import { debounce } from 'lodash';
import { useDebounceFn, useEventListener } from '@vueuse/core';
import { ClientState, TunnelState } from '@/components/terminal-rdp/guac/states';
import { ElMessage } from 'element-plus';
import { joinClientParams } from '@/common/request';
@@ -191,7 +190,7 @@ const installClipboard = () => {
const installResize = () => {
// 在resize事件结束后300毫秒执行
useEventListener('resize', debounce(resize, 300));
useEventListener('resize', useDebounceFn(resize, 300));
};
const installDisplay = () => {

View File

@@ -24,7 +24,7 @@
<div class="title-right-fixed">
<el-popconfirm @confirm="connect(true)" title="确认重新连接?">
<template #reference>
<div class="mr10 pointer">
<div class="mr-2 cursor-pointer">
<el-tag v-if="state.status == TerminalStatus.Connected" type="success" effect="light" round> 已连接 </el-tag>
<el-tag v-else type="danger" effect="light" round> 未连接点击重连 </el-tag>
</div>

View File

@@ -1,28 +1,28 @@
<template>
<div id="terminal-body" :style="{ height }">
<div ref="terminalRef" class="terminal" />
<div class="h-full w-full flex">
<div ref="terminalRef" class="h-full w-full" :style="{ background: getTerminalTheme().background }" />
<TerminalSearch ref="terminalSearchRef" :search-addon="state.addon.search" @close="focus" />
</div>
</template>
<script lang="ts" setup>
import 'xterm/css/xterm.css';
import { Terminal, ITheme } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { SearchAddon } from 'xterm-addon-search';
import { WebLinksAddon } from 'xterm-addon-web-links';
import '@xterm/xterm/css/xterm.css';
import { Terminal, ITheme } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { SearchAddon } from '@xterm/addon-search';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '@/store/themeConfig';
import { ref, nextTick, reactive, onMounted, onBeforeUnmount, watch } from 'vue';
import TerminalSearch from './TerminalSearch.vue';
import { debounce } from 'lodash';
import { TerminalStatus } from './common';
import { useEventListener } from '@vueuse/core';
import { useDebounceFn, useEventListener, useIntervalFn } from '@vueuse/core';
import themes from './themes';
import { TrzszFilter } from 'trzsz';
import { useI18n } from 'vue-i18n';
import { createWebSocket } from '@/common/request';
const { t } = useI18n();
@@ -42,13 +42,6 @@ const props = defineProps({
socketUrl: {
type: String,
},
/**
* 高度
*/
height: {
type: [String, Number],
default: '100%',
},
});
const emit = defineEmits(['statusChange']);
@@ -61,7 +54,6 @@ const { themeConfig } = storeToRefs(useThemeConfig());
// 终端实例
let term: Terminal;
let socket: WebSocket;
let pingInterval: any;
const state = reactive({
// 插件
@@ -90,7 +82,9 @@ watch(
watch(
() => themeConfig.value.terminalTheme,
() => {
term.options.theme = getTerminalTheme();
if (term) {
term.options.theme = getTerminalTheme();
}
}
);
@@ -98,7 +92,7 @@ onBeforeUnmount(() => {
close();
});
function init() {
const init = () => {
state.status = TerminalStatus.NoConnected;
if (term) {
console.log('重新连接...');
@@ -107,9 +101,9 @@ function init() {
nextTick(() => {
initTerm();
});
}
};
async function initTerm() {
const initTerm = async () => {
term = new Terminal({
fontSize: themeConfig.value.terminalFontSize || 15,
fontWeight: themeConfig.value.terminalFontWeight || 'normal',
@@ -129,9 +123,9 @@ async function initTerm() {
term.loadAddon(fitAddon);
fitTerminal();
// 注册窗口大小监听器
useEventListener('resize', debounce(fitTerminal, 400));
useEventListener('resize', useDebounceFn(fitTerminal, 400));
initSocket();
await initSocket();
// 注册其他插件
loadAddon();
@@ -145,42 +139,41 @@ async function initTerm() {
return true;
});
}
};
function initSocket() {
const initSocket = async () => {
if (!props.socketUrl) {
return;
}
socket = new WebSocket(`${props.socketUrl}&rows=${term?.rows}&cols=${term?.cols}`);
// 监听socket连接
socket.onopen = () => {
// 注册心跳
pingInterval = setInterval(sendPing, 15000);
state.status = TerminalStatus.Connected;
focus();
fitTerminal();
// 如果有初始要执行的命令,则发送执行命令
if (props.cmd) {
sendCmd(props.cmd + ' \r');
}
};
// 监听socket错误信息
socket.onerror = (e: Event) => {
try {
socket = await createWebSocket(`${props.socketUrl}${props.socketUrl.includes('?') ? '&' : '?'}rows=${term?.rows}&cols=${term?.cols}`);
} catch (e) {
term.writeln(`\r\n\x1b[31m${t('components.terminal.connErrMsg')}`);
state.status = TerminalStatus.Error;
console.log('连接错误', e);
};
return;
}
// 注册心跳
useIntervalFn(sendPing, 15000);
state.status = TerminalStatus.Connected;
focus();
fitTerminal();
// 如果有初始要执行的命令,则发送执行命令
if (props.cmd) {
sendData(props.cmd + ' \r');
}
socket.onclose = (e: CloseEvent) => {
console.log('terminal socket close...', e.reason);
state.status = TerminalStatus.Disconnected;
};
}
};
function loadAddon() {
const loadAddon = () => {
// 注册搜索组件
const searchAddon = new SearchAddon();
state.addon.search = searchAddon;
@@ -197,7 +190,7 @@ function loadAddon() {
// write the server output to the terminal
writeToTerminal: (data: any) => term.write(typeof data === 'string' ? data : new Uint8Array(data)),
// send the user input to the server
sendToServer: sendCmd,
sendToServer: sendData,
// the terminal columns
terminalColumns: term.cols,
// there is a windows shell
@@ -223,7 +216,7 @@ function loadAddon() {
.then(() => console.log('upload success'))
.catch((err: any) => console.log(err));
});
}
};
// 写入内容至终端
const write2Term = (data: any) => {
@@ -238,7 +231,7 @@ const getTerminalTheme = () => {
const terminalTheme = themeConfig.value.terminalTheme;
// 如果不是自定义主题,则返回内置主题
if (terminalTheme != 'custom') {
return themes[terminalTheme];
return (themes as any)[terminalTheme];
}
// 自定义主题
@@ -271,30 +264,28 @@ enum MsgType {
Ping = 3,
}
const send = (msg: any) => {
state.status == TerminalStatus.Connected && socket?.send(msg);
const send2Socket = (data: any) => {
state.status == TerminalStatus.Connected && socket?.send(data);
};
const sendResize = (cols: number, rows: number) => {
send(`${MsgType.Resize}|${rows}|${cols}`);
send2Socket(`${MsgType.Resize}|${rows}|${cols}`);
};
const sendPing = () => {
send(`${MsgType.Ping}|ping`);
send2Socket(`${MsgType.Ping}|ping`);
};
function sendCmd(key: any) {
send(`${MsgType.Data}|${key}`);
}
const sendData = (key: any) => {
send2Socket(`${MsgType.Data}|${key}`);
};
function closeSocket() {
const closeSocket = () => {
// 关闭 websocket
socket && socket.readyState === 1 && socket.close();
// 清除 ping
pingInterval && clearInterval(pingInterval);
}
};
function close() {
const close = () => {
console.log('in terminal body close');
closeSocket();
if (term) {
@@ -303,7 +294,7 @@ function close() {
state.addon.weblinks.dispose();
term.dispose();
}
}
};
const getStatus = (): TerminalStatus => {
return state.status;
@@ -311,17 +302,4 @@ const getStatus = (): TerminalStatus => {
defineExpose({ init, fitTerminal, focus, clear, close, getStatus, sendResize, write2Term, writeln2Term });
</script>
<style lang="scss">
#terminal-body {
width: 100%;
.terminal {
width: 100%;
height: 100%;
// .xterm .xterm-viewport {
// overflow-y: hidden;
// }
}
}
</style>
<style lang="scss"></style>

View File

@@ -28,7 +28,7 @@
<div class="title-right-fixed">
<el-popconfirm @confirm="reConnect(openTerminal.terminalId)" :title="$t('components.terminal.connConfirm')">
<template #reference>
<div class="mr15 pointer">
<div class="mr-4 cursor-pointer">
<el-tag v-if="openTerminal.status == TerminalStatus.Connected" type="success" effect="light" round>
{{ $t('components.terminal.connected') }}
</el-tag>
@@ -39,10 +39,10 @@
<el-popover placement="bottom" :width="200" trigger="hover">
<template #reference>
<SvgIcon name="QuestionFilled" :size="20" class="pointer-icon mr10" />
<SvgIcon name="QuestionFilled" :size="20" class="pointer-icon !mr-2" />
</template>
<div>ctrl | command + f ({{ $t('components.terminal.search') }})</div>
<div class="mt5">{{ $t('components.terminal.reConnTips') }}</div>
<div class="mt-1">{{ $t('components.terminal.reConnTips') }}</div>
</el-popover>
<SvgIcon
@@ -50,7 +50,7 @@
v-if="props.visibleMinimize"
@click="minimize(openTerminal.terminalId)"
:size="20"
class="pointer-icon mr10"
class="pointer-icon mr-2"
:title="$t('components.terminal.minimize')"
/>
@@ -58,7 +58,7 @@
name="FullScreen"
@click="handlerFullScreen(openTerminal)"
:size="20"
class="pointer-icon mr10"
class="pointer-icon mr-2"
:title="$t('components.terminal.fullScreenTitle')"
/>
@@ -88,7 +88,7 @@
<el-card
v-for="minimizeTerminal of minimizeTerminals"
:key="minimizeTerminal.terminalId"
:class="`terminal-minimize-item pointer ${minimizeTerminal.styleClass}`"
:class="`terminal-minimize-item cursor-pointer ${minimizeTerminal.styleClass}`"
size="small"
@click="maximize(minimizeTerminal.terminalId)"
>
@@ -99,7 +99,7 @@
</el-tooltip>
<!-- 关闭按钮 -->
<SvgIcon name="CloseBold" @click.stop="closeMinimizeTerminal(minimizeTerminal.terminalId)" class="ml10 pointer-icon fr" :size="20" />
<SvgIcon name="CloseBold" @click.stop="closeMinimizeTerminal(minimizeTerminal.terminalId)" class="ml-2 pointer-icon float-right" :size="20" />
</el-card>
</div>
</div>

View File

@@ -1,19 +1,19 @@
<template>
<div>
<el-drawer v-model="visible" :before-close="cancel" size="50%">
<el-drawer v-model="visible" :before-close="cancel" size="50%" body-class="flex flex-col">
<template #header>
<DrawerHeader :header="props.title" :back="cancel">
<template #extra>
<EnumTag :enums="LogTypeEnum" :value="log?.type" class="mr20" />
<EnumTag :enums="LogTypeEnum" :value="log?.type" class="mr-4.5" />
</template>
</DrawerHeader>
</template>
<el-descriptions class="mb10" :column="1" border v-if="extra">
<el-descriptions class="mb-2" :column="1" border v-if="extra">
<el-descriptions-item v-for="(value, key) in extra" :key="key" :span="1" :label="key">{{ value }}</el-descriptions-item>
</el-descriptions>
<TerminalBody class="mb10" ref="terminalRef" height="calc(100vh - 220px)" />
<TerminalBody class="mb-2 flex-1" ref="terminalRef" />
</el-drawer>
</div>
</template>

View File

@@ -46,7 +46,7 @@
<script lang="ts" setup>
import { ref, toRefs, nextTick, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import { SearchAddon, ISearchOptions } from 'xterm-addon-search';
import { SearchAddon, ISearchOptions } from '@xterm/addon-search';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();

View File

@@ -0,0 +1,20 @@
import { ref } from 'vue';
export function useDataState<KeyType, ValueType extends number | boolean | string>() {
const dataState = ref(new Map<KeyType, ValueType>());
const setState = (key: KeyType, value: ValueType) => {
dataState.value.set(key, value as any);
};
const getState = (key: KeyType): ValueType => {
const result = dataState.value.get(key);
return result as ValueType;
};
return {
dataState,
setState,
getState,
};
}

View File

@@ -79,16 +79,29 @@ export function useI18nDetailTitle(i18nKey: string) {
}
export function useI18nOperateSuccessMsg() {
const t = i18n.global.t;
ElMessage.success(t('common.operateSuccess'));
MsgSuccess('common.operateSuccess');
}
export function useI18nSaveSuccessMsg() {
const t = i18n.global.t;
ElMessage.success(t('common.saveSuccess'));
MsgSuccess('common.saveSuccess');
}
export function useI18nDeleteSuccessMsg() {
const t = i18n.global.t;
ElMessage.success(t('common.deleteSuccess'));
MsgSuccess('common.deleteSuccess');
}
/**
* error msg
* @param msg msg(支持i8n msgkey)
*/
export function MsgError(msg: string) {
ElMessage.error(i18n.global.t(msg));
}
/**
* success msg
* @param msg msg(支持i8n msgkey)
*/
export function MsgSuccess(msg: string) {
ElMessage.success(i18n.global.t(msg));
}

View File

@@ -22,7 +22,7 @@ export const usePageTable = (
) => {
const state = reactive({
// 表格数据
tableData: [],
tableData: [{}],
// 总数量
total: 0,
// 查询参数,包含分页参数

View File

@@ -2,11 +2,11 @@ import router from '@/router';
import { clearUser, getClientId, getRefreshToken, getToken, saveRefreshToken, saveToken } from '@/common/utils/storage';
import { templateResolve } from '@/common/utils/string';
import { ElMessage } from 'element-plus';
import { createFetch } from '@vueuse/core';
import { createFetch, UseFetchReturn } from '@vueuse/core';
import Api from '@/common/Api';
import { Result, ResultEnum } from '@/common/request';
import config from '@/common/config';
import { unref } from 'vue';
import { ref, unref } from 'vue';
import { URL_401 } from '@/router/staticRouter';
import openApi from '@/common/openApi';
import { useThemeConfig } from '@/store/themeConfig';
@@ -38,14 +38,19 @@ const useCustomFetch = createFetch({
return { options };
},
async afterFetch(ctx) {
const result: Result = await ctx.response.json();
ctx.data = result;
ctx.data = await ctx.response.json();
return ctx;
},
},
});
export function useApiFetch<T>(api: Api, params: any = null, reqOptions: RequestInit = {}) {
interface EsReq {
esProxyReq: boolean;
}
export interface RequestOptions extends RequestInit, EsReq {}
export function useApiFetch<T>(api: Api, params: any = null, reqOptions?: RequestOptions) {
const uaf = useCustomFetch<T>(api.url, {
async beforeFetch({ url, options }) {
options.method = api.method;
@@ -90,14 +95,24 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
},
};
},
});
onFetchError: (ctx) => {
if (reqOptions?.esProxyReq) {
uaf.data = { value: JSON.parse(ctx.data) };
return Promise.resolve(uaf.data);
}
return ctx;
},
}) as any;
// 统一处理后的返回结果如果直接使用uaf.data则数据会出现由{code: x, data: {}} -> data 的变化导致某些结果绑定报错
const data = ref<T | null>(null);
return {
execute: async function () {
return execCustomFetch(uaf);
await execCustomFetch(uaf, reqOptions);
data.value = uaf.data.value;
},
isFetching: uaf.isFetching,
data: uaf.data,
data: data,
abort: uaf.abort,
};
}
@@ -105,37 +120,44 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
let refreshingToken = false;
let queue: any[] = [];
async function execCustomFetch(uaf: any) {
async function execCustomFetch(uaf: UseFetchReturn<any>, reqOptions?: RequestOptions) {
try {
await uaf.execute(true);
} catch (e: any) {
const rejectPromise = Promise.reject(e);
if (!reqOptions?.esProxyReq) {
const rejectPromise = Promise.reject(e);
if (e?.name == 'AbortError') {
console.log('请求已取消');
if (e?.name == 'AbortError') {
console.log('请求已取消');
return rejectPromise;
}
const respStatus = uaf.response.value?.status;
if (respStatus == 404) {
ElMessage.error('url not found');
return rejectPromise;
}
if (respStatus == 500) {
ElMessage.error('server error');
return rejectPromise;
}
console.error(e);
ElMessage.error('network error');
return rejectPromise;
}
const respStatus = uaf.response.value?.status;
if (respStatus == 404) {
ElMessage.error('请求接口不存在');
return rejectPromise;
}
if (respStatus == 500) {
ElMessage.error('服务器响应异常');
return rejectPromise;
}
console.error(e);
ElMessage.error('网络请求错误');
return rejectPromise;
}
const result: Result = uaf.data.value as any;
const result: Result & { error: any; status: number } = uaf.data.value as any;
if (!result) {
ElMessage.error('网络请求失败');
ElMessage.error('network request failed');
return Promise.reject(result);
}
// es代理请求
if (reqOptions?.esProxyReq) {
uaf.data.value = result;
return Promise.resolve(result);
}
const resultCode = result.code;
@@ -151,7 +173,7 @@ async function execCustomFetch(uaf: any) {
// 请求加入队列等待, 防止并发多次请求refreshToken
return new Promise((resolve) => {
queue.push(() => {
resolve(execCustomFetch(uaf));
resolve(execCustomFetch(uaf, reqOptions));
});
});
}
@@ -175,13 +197,13 @@ async function execCustomFetch(uaf: any) {
queue = [];
}
await execCustomFetch(uaf);
await execCustomFetch(uaf, reqOptions);
return;
}
// 如果提示没有权限,则跳转至无权限页面
if (resultCode === ResultEnum.NO_PERMISSION) {
router.push({
await router.push({
path: URL_401,
});
return Promise.reject(result);

View File

@@ -7,22 +7,29 @@ export default {
detail: 'Details',
add: 'Add',
save: 'Save',
close: 'Close',
download: 'Download',
upload: 'Upload',
remove: 'Remove',
confirm: 'Confirm',
cancel: 'Cancel',
submit: 'Submit',
operation: 'Operations',
name: 'Name',
version: 'Version',
code: 'Code',
remark: 'Remark',
status: 'Status',
username: 'Username',
mobile: 'Mobile',
email: 'Email',
role: 'Role',
msg: 'Message',
type: 'Type',
time: 'Time',
account: 'Account',
password: 'Password',
captcha: 'Captcha',
createTime: 'Create Time',
creator: 'Creator',
updateTime: 'Update Time',
@@ -45,9 +52,11 @@ export default {
previousStep: 'Previous Step',
nextStep: 'Next Step',
copy: 'Copy',
copyCell: 'Copy Cell',
search: 'Search',
pleaseInput: 'Please enter {label}',
pleaseSelect: 'Please select {label}',
pleaseSelectOne: 'Please select Only One Data',
formValidationError: 'Please check the form',
createTitle: 'Create {name}',
editTitle: 'Edit {name}',
@@ -58,6 +67,9 @@ export default {
deleteSuccess: 'delete successfully',
operateSuccess: 'operate successfully',
fieldNotEmpty: '{field} cannot be empty',
selectAll: 'Select all',
MultiPlaceholder: 'Multiple are separated by commas',
appSlogan: 'Simple, efficient and secure',
},
layout: {
user: {
@@ -139,8 +151,6 @@ export default {
isUniqueOpened: 'Menu accordion',
isFixedHeader: 'Fixed header',
isClassicSplitMenu: 'Classic layout split menu',
isLockScreen: 'Open the lock screen',
lockScreenTime: 'screen locking(s/s)',
interfaceDisplay: 'Interface display',
isShowLogo: 'Sidebar logo',
isBreadcrumb: 'Open breadcrumb',
@@ -181,134 +191,6 @@ export default {
btnTwo: 'Update now',
btnTwoLoading: 'Updating',
},
menu: {
index: 'Home Page',
personalCenter: 'Personal Center',
tag: 'Tag',
tagTree: 'Tag Tree',
tagSave: 'Save Tag',
tagDelete: 'Delete Tag',
authorization: 'Authorization',
authorizationBase: 'Base Permission',
authorizationSave: 'Save Authorization',
authorizationDelete: 'Delete Authorization',
team: 'Team',
teamSave: 'Save Team',
teamDelete: 'Delete Team',
teamMemberAdd: 'Add Member',
teamMemberDelete: 'Delete Member',
teamTagSave: 'Save Team Tag',
machine: 'Machine',
machineOp: 'Machine Operation',
machineOpBase: 'Base Permission',
machineList: 'Machine List',
machineBase: 'Base Permission',
machineCreate: 'Create Machine',
machineEdit: 'Edit Machine',
machineDelete: 'Delete Machine',
machineTerminal: 'Machine Terminal',
machineFileConf: 'File',
machineFileConfCreate: 'File-Add Config',
machineFileConfDelete: 'File-Delete Config',
machineFileCreate: 'File-Create',
machineFileDelete: 'File-Delete',
machineFileWrite: 'File-Write',
machineFileUpload: 'File-Upload',
machineScript: 'Script',
machineScriptSave: 'Script-Save',
machineScriptDelete: 'Script-Delete',
machineScriptRun: 'Script-Run',
machineKillprocess: 'Kill Process',
machineCronJob: 'Cron Job',
machineCronJobSvae: 'Cron Job-Save',
machineCronJobDelete: 'Cron Job-Delete',
machineSecurityConfig: 'Security Config',
machineSecurityCmdSvae: 'Cmd Config-Save',
machineSecurityCmdDelete: 'Cmd Config-Delete',
dbms: 'DBMS',
dbDataOp: 'Data Operation',
dbDataOpBase: 'Base Permission',
dbDataOpSqlScriptRun: 'SQL Script Run',
dbInstance: 'DB Instance',
dbInstanceBase: 'Base Permission',
dbInstanceSave: 'Save Instance',
dbInstanceDelete: 'Delete Instance',
dbBase: 'Db Base Permission',
dbSave: 'Save Db',
dbDelete: 'Delete Db',
dbDataSync: 'Data Sync',
dbDataSyncBase: 'Base Permission',
dbDataSyncSave: 'Save Sync Task',
dbDataSyncDelete: 'Delete Sync Task',
dbDataSyncChangeStatus: 'Enable/Disable Sync Task',
dbDataSyncLog: 'Sync Log',
dbTransfer: 'DB Transfer',
dbTransferBase: 'Base Permission',
dbTransferSave: 'Save Transfer Task',
dbTransferDelete: 'Delete Transfer Task',
dbTransferChangeStatus: 'Enable/Disable Transfer Task',
dbTransferRun: 'Run Transfer Task',
dbTransferRunLog: 'Transfer Log',
dbTransferFileShow: 'ransfer File-Show',
dbTransferFileDelete: 'Transfer File-Delete',
dbTransferFileDownload: 'Transfer File-Download',
dbTransferFileRun: 'Transfer File-Run',
redis: 'Redis',
redisDataOp: 'Data Operation',
redisDataOpBase: 'Base Permission',
redisDataOpSave: 'Save Data',
redisDataOpDelete: 'Delete Data',
redisManage: 'Redis Manage',
redisManageBase: 'Base Permission',
mongo: 'Mongo',
mongoDataOp: 'Data Operation',
mongoDataOpBase: 'Base Permission',
mongoDataOpSave: 'Save Data',
mongoDataOpDelete: 'Delete Data',
mongoManage: 'Mongo Manage',
mongoManageBase: 'Base Permission',
flow: 'Flow',
myTask: 'My Task',
myFlow: 'My Flow',
flowProcDef: 'Process Define',
flowProcDefSave: 'Save Process Define',
flowProcDefDelete: 'Delete Process Define',
system: 'System',
menuPermission: 'Menu & Permission',
menuPermissionBase: 'Base Permission',
menuPermissionAdd: 'Add Menu Permission',
menuPermissionEdit: 'Edit Menu Permission',
menuPermissionDelete: 'Delete Menu Permission',
menuPermissionEnableDisable: 'Enable/Disable Menu Permission',
account: 'Account',
accountBase: 'Base Permission',
accountAdd: 'Add Account',
accountEdit: 'Edit Account',
accountDelete: 'Delete Account',
accountEnableDisable: 'Enable/Disable Account',
accountRoleAllocation: 'Role Allocation',
role: 'Role',
roleBase: 'Base Permission',
roleAdd: 'Add Role',
roleEdit: 'Edit Role',
roleDelete: 'Delete Role',
roleMenuPermissionAllocation: 'Menu & Permission Allocation',
sysConf: 'System Config',
sysConfBase: 'Base Permission',
sysConfSave: 'Save System Config',
opLog: 'Operation Log',
opLogBase: 'Base Permission',
noPagePermission: 'No Page Permission',
authcertShowciphertext: 'Show Ciphertext',
},
home: {
personalInfo: 'Personal Information',
welcomeMsg: `Hello, {name}, no matter how bad life gets, it doesn't prevent me from getting better!`,
@@ -338,25 +220,19 @@ export default {
accountPasswordLogin: 'Account Password Login',
thirdPartyLogin: 'Third Party login',
ldapLogin: 'LDAP Login',
inputUsernamePlaceholder: 'Please enter your username',
inputPasswordPlaceholder: 'Please enter your password',
inputCaptchaPlaceholder: 'Please enter the verification code',
login: 'Login',
loginFailTip: 'Tip: If you fail to log in more than {loginFailCount} times, you will not be allowed to log in again for {loginFailMin} minutes',
loginSuccessTip: 'welcome back!',
changePassword: 'Change Password',
oldPassword: 'Old Password',
newPassword: 'New Password',
inputNewPasswordPlaceholder: 'Please enter a new password',
passwordRuleTip: 'Must be at least 8 characters long and contain upper/lower case + number + special symbol',
passwordChangeSuccessTip: 'The password has been changed successfully, and the new password has been filled in the login password box',
otpValidation: 'OTP validation',
qrCode: 'QR code',
enterOtpCodeTip: 'Enter the authorization code shown in the Token APP',
inputOtpCodePlaceholder: 'Please enter the OTP authorization code',
updateBasicInfo: 'Modifying basic information',
name: 'Name',
inputNamePlaceholder: 'Please enter your name',
},
components: {
df: {

View File

@@ -16,6 +16,7 @@ export default {
dbFilterPlaceholder: 'DB name: Input filterable',
sqlRecord: 'SQL records',
dump: 'Export',
dbDumpFail: 'DB export failed',
dumpContent: 'Export Content',
structure: 'Structure',
data: 'Data',
@@ -55,6 +56,8 @@ export default {
execSuccess: 'Successful execution',
execFail: 'Execution failure',
sqlScriptRun: 'Run SQL Script',
sqlScriptRunSuccess: 'SQL script executed successfully',
sqlScriptRunFail: 'SQL script execution failed',
saveSql: 'Save SQL',
execInfo: 'Execution info',
result: 'Result',
@@ -62,7 +65,7 @@ export default {
resultSet: 'Result Set',
tableDataEmptyTextTips:
'tips: Single table query at the beginning of select * or click the default query data of the table name, double-click the data online modification',
noSelctRunSqlMsg: 'Select the sql you want to execute',
noSelectRunSqlMsg: 'Select the sql you want to execute or move the cursor near the sql you want to execute',
enterExecRemarkTips: 'Please enter remark',
execRemarkPlaceholder: 'Enter the remark to execute the sql',
currentSqlTabIsRunning: 'The current result set tab is being executed, please use the new TAB to execute',
@@ -219,4 +222,11 @@ export default {
running: 'Running',
waitRun: 'Wait Run',
},
es: {
keywordPlaceholder: 'host / name / code',
port: 'Port',
acName: 'Credential',
dbInst: 'Es Instance',
connSuccess: 'be connected successfully',
},
};

View File

@@ -0,0 +1,83 @@
export default {
docker: {
containerConf: 'Container Config',
addr: 'Address',
addrTips: 'eg: unix:///var/run/docker.sock 、tcp://192.168.1.1',
container: 'Container',
containerName: 'Container Name',
running: 'Running',
stopped: 'Stopped',
name: 'Container Name',
ip: 'IP Address',
status: 'Status',
stats: 'Stats',
memory: 'Memory',
stop: 'Stop',
stopContainerConfirm: 'Are you sure to stop container [{name}] ?',
removeContainerConfirm: 'Are you sure to remove container [{name}] ?',
restart: 'Restart',
createContainer: 'Create Container',
mount: 'Mount',
hostDir: 'Host Directory',
containerDir: 'Container Directory',
permission: 'Permission',
rw: 'RW',
ro: 'RO',
port: 'Port',
image: 'Image',
tag: 'Tag',
size: 'Size',
used: 'Used',
unUsed: 'UnUsed',
imageName: 'Image Name',
log: 'Log',
lines: 'Lines',
follow: 'Follow',
stopImageConfirm: 'Are you sure to stop image [{name}] ?',
export: 'Export',
imageUploading: 'Image uploading, please wait...',
imageTips: 'Support manual input and select',
forcePull: 'Force Pull Image',
hostPortPlaceholder: '80',
forcePullTips: 'Ignore the server existing image, pull again',
server: 'Server',
protocol: 'Protocol',
networkMode: 'Network Mode',
consoleTerminal: 'Console Terminal',
otherOption: 'Other Option',
tty: 'tty',
openStdin: 'stdin (-i)',
privileged: 'Privileged',
restartPolicy: 'Restart Policy',
noRestart: 'No Restart',
alwaysRestart: 'Always Restart',
onFailure: 'On Failure',
unlessStopped: 'Unless Stopped',
cpuShare: 'CPU Share',
cpuShareTips: 'The default container share is 1024 cpus, and increasing it will give the current container more CPU time',
cpuQuota: 'CPU Quota',
cpuLimitTips: 'A CPU limit of 0 turns off the limit',
cpuCanUseTips: 'The maximum available is {cpuTotal} cores',
core: 'Core',
memoryLimit: 'Memory Limit',
memoryLimitTips: 'A memory limit of 0 turns off the limit',
shmSize: 'Shm Size',
memoryCanUseTips: 'Maximum available {memTotal}',
tagTips: `One in a row, for example:
tag1=value1
tag2=value2`,
envParam: 'Env Param',
envParamTips: `One in a row, for example:
env1=value1
env2=value2`,
device: 'Device',
driver: 'Driver',
driverTips: 'Device drivers to be used by the container, e.g. : nvidia, etc',
count: 'Count',
capabilitie: 'Capabilitie',
deviceId: 'Device ID',
capabilitiePlaceholder: 'eg: gpu',
},
};

122
frontend/src/i18n/en/es.ts Normal file
View File

@@ -0,0 +1,122 @@
export default {
es: {
keywordPlaceholder: 'host / name / code',
protocol: 'Protocol',
port: 'Port',
size: 'size',
docs: 'docs',
health: 'health',
aliases: 'Aliases',
addAlias: 'Add Alias',
specifyIdAdd: 'Specify the ID added, if id exists, then update',
addIndex: 'Add Index',
editIndex: 'Edit Index',
status: 'status',
acName: 'Credential',
emptyTable: 'data not fund',
connSuccess: 'be connected successfully',
shouldTestConn: 'please test connection first',
instance: 'ES Instance',
instanceSave: 'ES-Save Instance',
instanceDel: 'Es-Delete Instance',
operation: 'Es-Data Operation',
dataSave: 'Es-Data Save',
dataDel: 'Es-Data Del',
indexName: 'Index Name',
requireIndexName: 'Index Name Is Required',
indexDetail: 'Index Detail',
indexMapping: 'Mappings',
indexStats: 'Stats',
opViewColumns: 'Option View Columns',
opIndex: 'Index Management',
opSearch: 'Search',
searchParamsPreview: 'Search Params Preview',
opBasicSearch: 'Basic Search',
opSeniorSearch: 'Senior Search',
sampleMappings: 'Sample Mappings',
copyMappings: 'Copy Mappings',
readonlyMsg: 'The content is readOnly',
opDashboard: 'Dashboard',
opSettings: 'Settings',
templates: 'Templates',
availableSettingFields: 'Available Setting Fields',
Reindex: 'Reindex',
ReindexTargetIdx: 'Target Index',
ReindexIsSync: 'Sync Able',
ReindexDescription:
"If a field in Mapping has been defined, you can't modify the type of the field, and you can't change the number of shards, you can use the Reindex API to solve this problem.",
ReindexSyncDescription: 'If the amount of index data is large, we recommend that you enable asynchronous data to avoid request timeouts.',
ReindexToOtherInst: 'To other Instance',
ReindexSyncTask: 'Sync Task',
makeSearchParam: 'Make Search Params',
filterColumn: 'Filter Columns',
searchParams: 'Search',
searchParamsDesc: 'If no field is selected or no condition value is set, it will not take effect',
standardSearch: 'Standard Search',
AggregationSearch: 'Aggregation Search',
SqlSearch: 'Sql Search',
searchError: 'Search Error',
execError: 'Exec Error',
docJsonError: 'Document JSON Format Error',
sortParams: 'Sort',
otherParams: 'Other',
closeIndexConfirm: 'This operation will close index [{name}]. Do you want to continue?',
openIndexConfirm: 'This operation will open index [{name}]. Do you want to continue?',
clearCacheConfirm: 'This operation will clear index [{name}] cache. Do you want to continue?',
page: {
home: 'First Page',
prev: 'Prev Page',
next: 'Next Page',
total: 'Total Count',
changeSize: 'Change Page Size',
},
temp: {
addTemp: 'Add template',
view: 'Template Detail',
name: 'name',
priority: 'priority',
index_patterns: 'patterns',
content: 'content',
showHide: 'show system templates',
description: 'description',
filter: 'filter name / description',
versionAlert: 'Versions prior to 7.8 are not supported',
note: `1、When creating a new index, if the index name matches the wildcard of the index template, the index template's settings (_setting, _mapping, etc.) are used。
2、Templates take effect only when an index is created, and modifying a template does not affect existing indexes。
3、You can specify the value of "priority", which was "order" before version 7.8, and if the new index name matches multiple templates, the one with the lowest priority will be used first.`,
},
dashboard: {
instInfo: 'Instance Info',
clusterHealth: 'Cluster Health',
nodes: 'Nodes Info',
sysMem: 'System Mem',
jvmMem: 'JVM Mem',
fileSystem: 'File System',
analyze: 'Analyze',
idxName: 'Index Name',
field: 'Field',
text: 'Text',
startAnalyze: 'Start Analyze',
},
contextmenu: {
index: {
addIndex: 'Add Index',
showSys: 'Show System Index',
copyName: 'Copy Name',
refresh: 'Refresh Index',
flush: 'Flush Index',
clearCache: 'Clear Index Cache',
addAlias: 'Add Alias',
Close: 'Close',
Open: 'Open',
Delete: 'Delete Index',
edit: 'Edit Index',
DeleteSelectLine: 'Copy Selected Line Json',
BaseSearch: 'Base Search',
SeniorSearch: 'Senior Search',
copyLineJson: 'Copy Line Json',
copySelectLineJson: 'Copy Selected Line Json',
},
},
},
};

View File

@@ -6,23 +6,23 @@ export default {
triggeringCondition: 'Condition',
triggeringConditionTips: 'go template syntax. If the output is 1, the approval process is triggered',
conditionPlaceholder: 'Trigger condition, return value =1, means to trigger the approval process',
conditionDefault: `{{/* DBMS- Run Sql rules The param parameter is described as follows */}}
{{/* stmtType: select / read / insert / update / delete / ddl ; */}}
{{ if eq .bizType "db_sql_exec_flow"}}
{{/* Enable process approval when select and read statements are not available */}}
{{ if and (ne .param.stmtType "select") (ne .param.stmtType "read") }}
1
{{ end }}
{{ end }}
conditionDefault: `{'{{'}/* DBMS- Run Sql rules The param parameter is described as follows */{'}}'}
{'{{'}/* stmtType: select / read / insert / update / delete / ddl ; */{'}}'}
{'{{'} if eq .bizType "db_sql_exec_flow"{'}}'}
{'{{'}/* Enable process approval when select and read statements are not available */{'}}'}
{'{{'} if and (ne .param.stmtType "select") (ne .param.stmtType "read") {'}}'}
1
{'{{'} end {'}}'}
{'{{'} end {'}}'}
{{/* Redis-Run Cmd rules; param: parameter is described as follows */}}
{{/* cmdType: read(Read cmd) / write(Write cmd); */}}
{{/* cmd: get/set/hset... */}}
{{ if eq .bizType "redis_run_cmd_flow"}}
{{ if eq .param.cmdType "write" }}
1
{{ end }}
{{ end }}`,
{'{{'}/* Redis-Run Cmd rules; param: parameter is described as follows */{'}}'}
{'{{'}/* cmdType: read(Read cmd) / write(Write cmd); */{'}}'}
{'{{'}/* cmd: get/set/hset... */{'}}'}
{'{{'} if eq .bizType "redis_run_cmd_flow"{'}}'}
{'{{'} if eq .param.cmdType "write" {'}}'}
1
{'{{'} end {'}}'}
{'{{'} end {'}}'}`,
nodeName: 'Node Name',
nodeNameTips: 'Click the specified node to drag and drop sort',
auditor: 'Auditor',
@@ -32,6 +32,27 @@ export default {
enable: 'Enable',
disable: 'Disable',
todoTask: 'Pending Tasks',
doneTask: 'Completed Tasks',
flowDesign: 'Flow Design',
clear: 'Clear',
approvalMode: 'Approval Mode',
andSign: 'All Approve (AND)',
orSign: 'Any Approve (OR)',
voteSign: 'Vote Approval',
taskCandidate: 'Task Assignees',
mustOneStartNode: 'There must be one start node in the flow',
mustOneEndNode: 'There must be one end node in the flow',
mustOneOutEdgeForStartNode: 'The start node must have at least one outgoing edge',
mustOneInEdgeForEndNode: 'The end node must have at least one incoming edge',
approvalRecord: 'Approval Records',
start: 'Start',
end: 'End',
usertask: 'User Task', // 建议拼写修正为 userTask
serial: 'Exclusive Gateway',
parallel: 'Parallel Gateway',
flowEdge: 'Sequence Flow',
// procinst
startProcess: 'Start Process',
cancelProcessConfirm: 'Confirm canceling the process?',
@@ -80,14 +101,17 @@ export default {
redisRunCmd: 'Redis-Run Cmd',
// task
approveNode: 'Approve Node',
approveForm: 'Approve Form',
approveResult: 'Result',
approveNode: 'Approval Node',
approveForm: 'Approval Form',
approveResult: 'Approval Result',
approvalRemark: 'Approval Comments',
approver: 'Approver',
audit: 'Audit',
procinstStatus: 'Process status',
taskStatus: 'Task status',
procinstStatus: 'Process Status',
taskStatus: 'Task Status',
taskName: 'Task Name',
taskBeginTime: 'Begin Time',
flowAudit: 'Approval Process',
taskBeginTime: 'Start Time',
flowAudit: 'Process Audit',
notify: 'Notify',
},
};

View File

@@ -65,7 +65,7 @@ export default {
processName: 'Process Name',
selectSortType: 'Please select a sort type',
selectProcessNum: 'Please select the number of processes',
cpuDesc: 'CUP descending',
cpuDesc: 'CPU descending',
memDesc: 'Memory descending',
virtualMemory: 'Virtual Memory',
fixedMemory: 'Fixed Memory',
@@ -87,6 +87,8 @@ export default {
scriptResultEnumRealTime: 'Real-time',
scriptTypeEnumPrivate: 'Private',
scriptTypeEnumPublic: 'Public',
category: 'Category',
categoryTips: 'support input new category and selection',
// security
cmdConfig: 'Command Config',
@@ -136,5 +138,7 @@ export default {
fileTooLargeTips: 'The file is too large, please download and use it',
uploadSuccess: 'Upload successfully',
fileExceedsSysConf: 'The uploaded file exceeds the system configuration [{uploadMaxFileSize}]',
fileUploadSuccess: 'Machine file upload successful',
fileUploadFail: 'Machine file upload failed',
},
};

View File

@@ -0,0 +1,144 @@
export default {
menu: {
index: 'Home',
personalCenter: 'Personal Center',
myResource: 'Resource',
tag: 'Resource',
tagTree: 'Resource Tree',
tagSave: 'Save Tag',
tagDelete: 'Delete Tag',
authorization: 'Authorization',
authorizationBase: 'Base Permission',
authorizationSave: 'Save Authorization',
authorizationDelete: 'Delete Authorization',
team: 'Team',
teamSave: 'Save Team',
teamDelete: 'Delete Team',
teamMemberAdd: 'Add Member',
teamMemberDelete: 'Delete Member',
teamTagSave: 'Save Team Tag',
machine: 'Machine',
machineOp: 'Machine Operation',
machineOpBase: 'Base Permission',
machineList: 'Machine List',
machineBase: 'Base Permission',
machineCreate: 'Create Machine',
machineEdit: 'Edit Machine',
machineDelete: 'Delete Machine',
machineTerminal: 'Machine Terminal',
machineFileConf: 'File',
machineFileConfCreate: 'File-Add Config',
machineFileConfDelete: 'File-Delete Config',
machineFileCreate: 'File-Create',
machineFileDelete: 'File-Delete',
machineFileWrite: 'File-Write',
machineFileUpload: 'File-Upload',
machineScript: 'Script',
machineScriptSave: 'Script-Save',
machineScriptDelete: 'Script-Delete',
machineScriptRun: 'Script-Run',
machineKillprocess: 'Kill Process',
machineCronJob: 'Cron Job',
machineCronJobSvae: 'Cron Job-Save',
machineCronJobDelete: 'Cron Job-Delete',
machineSecurityConfig: 'Security Config',
machineSecurityCmdSvae: 'Cmd Config-Save',
machineSecurityCmdDelete: 'Cmd Config-Delete',
dbms: 'DBMS',
dbDataOp: 'Data Operation',
dbDataOpBase: 'Base Permission',
dbDataOpSqlScriptRun: 'SQL Script Run',
dbInstance: 'DB Instance',
dbInstanceBase: 'Base Permission',
dbInstanceSave: 'Save Instance',
dbInstanceDelete: 'Delete Instance',
dbBase: 'Db Base Permission',
dbSave: 'Save Db',
dbDelete: 'Delete Db',
dbDataSync: 'Data Sync',
dbDataSyncBase: 'Base Permission',
dbDataSyncSave: 'Save Sync Task',
dbDataSyncDelete: 'Delete Sync Task',
dbDataSyncChangeStatus: 'Enable/Disable Sync Task',
dbDataSyncLog: 'Sync Log',
dbTransfer: 'DB Transfer',
dbTransferBase: 'Base Permission',
dbTransferSave: 'Save Transfer Task',
dbTransferDelete: 'Delete Transfer Task',
dbTransferChangeStatus: 'Enable/Disable Transfer Task',
dbTransferRun: 'Run Transfer Task',
dbTransferRunLog: 'Transfer Log',
dbTransferFileShow: 'ransfer File-Show',
dbTransferFileDelete: 'Transfer File-Delete',
dbTransferFileDownload: 'Transfer File-Download',
dbTransferFileRun: 'Transfer File-Run',
redis: 'Redis',
redisDataOp: 'Data Operation',
redisDataOpBase: 'Base Permission',
redisDataOpSave: 'Save Data',
redisDataOpDelete: 'Delete Data',
redisManage: 'Redis Manage',
redisManageBase: 'Base Permission',
mongo: 'Mongo',
mongoDataOp: 'Data Operation',
mongoDataOpBase: 'Base Permission',
mongoDataOpSave: 'Save Data',
mongoDataOpDelete: 'Delete Data',
mongoManage: 'Mongo Manage',
mongoManageBase: 'Base Permission',
containerManageBase: 'Container Manage - Base Permission',
flow: 'Flow',
myTask: 'My Task',
myFlow: 'My Flow',
flowProcDef: 'Process Define',
flowProcDefSave: 'Save Process Define',
flowProcDefDelete: 'Delete Process Define',
msgManage: 'Message',
channel: 'Message Channel',
msgChannelBase: 'Base Permission',
saveMsgChannel: 'Save Message Channel',
delMsgChannel: 'Delete Message Channel',
msgTmpl: 'Message Template',
msgTmplBase: 'Base Permission',
saveMsgTmpl: 'Save Message Template',
delMsgTmpl: 'Delete Message Template',
sendMsg: 'Send Message',
system: 'System',
menuPermission: 'Menu & Permission',
menuPermissionBase: 'Base Permission',
menuPermissionAdd: 'Add Menu Permission',
menuPermissionEdit: 'Edit Menu Permission',
menuPermissionDelete: 'Delete Menu Permission',
menuPermissionEnableDisable: 'Enable/Disable Menu Permission',
account: 'Account',
accountBase: 'Base Permission',
accountAdd: 'Add Account',
accountEdit: 'Edit Account',
accountDelete: 'Delete Account',
accountEnableDisable: 'Enable/Disable Account',
accountRoleAllocation: 'Role Allocation',
role: 'Role',
roleBase: 'Base Permission',
roleAdd: 'Add Role',
roleEdit: 'Edit Role',
roleDelete: 'Delete Role',
roleMenuPermissionAllocation: 'Menu & Permission Allocation',
sysConf: 'System Config',
sysConfBase: 'Base Permission',
sysConfSave: 'Save System Config',
opLog: 'Operation Log',
opLogBase: 'Base Permission',
noPagePermission: 'No Page Permission',
authcertShowciphertext: 'Show Ciphertext',
},
};

View File

@@ -0,0 +1,21 @@
export default {
msg: {
name: 'Name',
email: 'Email',
dingBot: 'DingTalk Bot',
qywxBot: 'WeChat Work Bot',
feishuBot: 'Feishu Bot',
msgChannel: 'Message Channel',
smtpAccount: 'SMTP Account',
smtpPassword: 'SMTP Password',
msgTmpl: 'Message Template',
relateChannel: 'Related Channel',
title: 'Title',
tmpl: 'Template',
send: 'Send',
sendMsg: 'Send Message',
selectTmplPlaceholder: 'Select message template, support fuzzy search by number',
msgTmplTooltip:
"Message template supports variable replacement, the variable format is {'{{.variable}}'} </br> the following are common built-in variables <br/>{'{{.receiver}}'}: used to {'@'} specify the recipient",
},
};

View File

@@ -25,8 +25,8 @@ export default {
success: 'Success',
menuCodeTips: `The menu type is the access path (if the menu path does not begin with '/', the access address will automatically concatenate the parent menu path), otherwise it is the unique code of the resource`,
menuCodePlaceholder: `A menu that does not begin with '/' will automatically concatenate the parent menu path`,
routerNameTips: 'For component caching to work, match the vue component name, such as ResourceLis',
componentPathTips: 'Access path components, such as: ` system/resource/ResourceList `, default in ` views ` directory',
routerNameTips:
'For component caching to work, the key for route.ts in the frontend module should match the vue component name, such as ResourceList',
isCacheTips: `If yes is selected, it will be 'keepalive' cached (reentering the page without refreshing the page and requesting data again), and needs the route name to match the vue component name`,
isHideTips:
'Select Hide and the route will not appear in the menu bar, but it will still be accessible. Disabled will not be able to access and operate',
@@ -34,8 +34,6 @@ export default {
inline: 'Embedded',
linkAddress: 'Link Address',
linkPlaceholder: 'External/embedded links (http://xxx.com)',
menuNameRuleMsg: 'Please enter a resource name',
codeRuleMsg: 'Please enter a resource code',
routeNameNotEmpty: 'Route names cannot be empty',
resourceCodePatternErrMsg: 'Only 1-32 uppercase letters, numbers, and -.: characters are allowed',
assignedRole: 'Assigned Role',
@@ -50,7 +48,7 @@ export default {
lastLoginTime: 'Last Login Time',
usernamePlacholder: '5-16 uppercase letters, numbers, -.: characters',
random: 'Random',
usernamePatternErrMsg: 'Only 5-16 uppercase letters, numbers, and -.: characters are allowed',
usernamePatternErrMsg: "Only 5-16 uppercase letters, numbers, and -.:{'@'} characters are allowed",
accountSearchPlaceholder: 'Enter account fuzzy search and select',
accountInfo: 'Account Information',
allocateRoleTitle: 'Allocate the `{name}` role',

View File

@@ -7,6 +7,7 @@ export default {
tagTips1: '1. Used to group assets',
tagTips2: '2. Can be allocated in team management for resource isolation',
tagTips3: '3. Team members who own a parent tag have access to resources that manipulate their own or child tag associations',
tagTips4: '4. Right-click nodes to edit or add child tags',
machine: 'Machine',
db: 'Db',
code: 'Code',
@@ -14,6 +15,12 @@ export default {
createSubTagTitle: 'Creates a child tag for {codePath}',
rootTag: 'Root Tag',
selectTagPlaceholder: 'Select the associated tag',
machineOp: 'Machine Operation',
dbDataOp: 'Db Operation',
redisDataOp: 'Redis Operation',
esDataOp: 'Es Operation',
mongoDataOp: 'Mongo Operation',
allResource: 'All Resource',
},
team: {
team: 'Team',

View File

@@ -7,22 +7,29 @@ export default {
detail: '详情',
add: '添加',
save: '保存',
close: '关闭',
download: '下载',
upload: '上传',
remove: '移除',
confirm: '确定',
cancel: '取消',
submit: '提交',
operation: '操作',
name: '名称',
version: '版本',
code: '编号',
remark: '备注',
status: '状态',
username: '用户名',
mobile: '手机号',
email: '邮箱',
role: '角色',
msg: '消息',
type: '类型',
time: '时间',
account: '账号',
password: '密码',
captcha: '验证码',
createTime: '创建时间',
creator: '创建者',
updateTime: '更新时间',
@@ -45,9 +52,11 @@ export default {
previousStep: '上一步',
nextStep: '下一步',
copy: '复制',
copyCell: '复制单元格',
search: '搜索',
pleaseInput: '请输入{label}',
pleaseSelect: '请选择{label}',
pleaseSelectOne: '请选择一条数据',
formValidationError: '信息填写有误,请检查',
createTitle: '创建{name}',
editTitle: '编辑{name}',
@@ -58,6 +67,9 @@ export default {
deleteSuccess: '删除成功',
operateSuccess: '操作成功',
fieldNotEmpty: '{field}不能为空',
selectAll: '全选',
MultiPlaceholder: '多个用逗号隔开',
appSlogan: '简洁 · 高效 · 安全',
},
layout: {
user: {
@@ -141,8 +153,6 @@ export default {
isUniqueOpened: '菜单手风琴',
isFixedHeader: '固定 Header',
isClassicSplitMenu: '经典布局分割菜单',
isLockScreen: '开启锁屏',
lockScreenTime: '自动锁屏(s/秒)',
interfaceDisplay: '界面显示',
isShowLogo: '侧边栏 Logo',
isBreadcrumb: '开启 Breadcrumb',
@@ -191,134 +201,6 @@ export default {
btnTwo: '马上更新',
btnTwoLoading: '更新中',
},
menu: {
index: '首页',
personalCenter: '个人中心',
tag: '标签管理',
tagTree: '标签树',
tagSave: '保存标签',
tagDelete: '删除标签',
authorization: '授权凭证',
authorizationBase: '基础权限',
authorizationSave: '保存权限',
authorizationDelete: '删除权限',
team: '团队管理',
teamSave: '保存团队',
teamDelete: '删除团队',
teamMemberAdd: '添加成员',
teamMemberDelete: '删除成员',
teamTagSave: '保存团队标签',
machine: '机器管理',
machineOp: '机器操作',
machineOpBase: '基本权限',
machineList: '机器列表',
machineBase: '基本权限',
machineCreate: '创建机器',
machineEdit: '编辑机器',
machineDelete: '删除机器',
machineTerminal: '机器终端',
machineFileConf: '文件管理',
machineFileConfCreate: '文件-添加配置',
machineFileConfDelete: '文件-删除配置',
machineFileCreate: '文件-创建',
machineFileDelete: '文件-删除',
machineFileWrite: '文件-写入',
machineFileUpload: '文件-上传',
machineScript: '脚本管理',
machineScriptSave: '脚本-保存',
machineScriptDelete: '脚本-删除',
machineScriptRun: '脚本-执行',
machineKillprocess: '终止进程',
machineCronJob: '计划任务',
machineCronJobSvae: '计划任务-保存',
machineCronJobDelete: '计划任务-删除',
machineSecurityConfig: '安全配置',
machineSecurityCmdSvae: '命令配置-保存',
machineSecurityCmdDelete: '命令配置-删除',
dbms: 'DBMS',
dbDataOp: '数据操作',
dbDataOpBase: '基本权限',
dbDataOpSqlScriptRun: 'SQL脚本执行',
dbInstance: '数据库实例',
dbInstanceBase: '基本权限',
dbInstanceSave: '保存实例',
dbInstanceDelete: '删除实例',
dbBase: '数据库基本权限',
dbSave: '保存数据库',
dbDelete: '删除数据库',
dbDataSync: '数据同步',
dbDataSyncBase: '基本权限',
dbDataSyncSave: '保存同步',
dbDataSyncDelete: '删除同步',
dbDataSyncChangeStatus: '启用停用',
dbDataSyncLog: '同步日志',
dbTransfer: '数据库迁移',
dbTransferBase: '基本权限',
dbTransferSave: '保存迁移任务',
dbTransferDelete: '删除迁移任务',
dbTransferChangeStatus: '启用停用',
dbTransferRun: '执行迁移任务',
dbTransferRunLog: '迁移日志查看',
dbTransferFileShow: '迁移文件-查看',
dbTransferFileDelete: '迁移文件-删除',
dbTransferFileDownload: '迁移文件-下载',
dbTransferFileRun: '迁移文件-执行',
redis: 'Redis',
redisDataOp: '数据操作',
redisDataOpBase: '基本权限',
redisDataOpSave: '数据保存',
redisDataOpDelete: '数据删除',
redisManage: 'Redis管理',
redisManageBase: '基本权限',
mongo: 'Mongo',
mongoDataOp: '数据操作',
mongoDataOpBase: '基本权限',
mongoDataOpSave: '数据保存',
mongoDataOpDelete: '数据删除',
mongoManage: 'Mongo管理',
mongoManageBase: '基本权限',
flow: '工单流程',
myTask: '我的任务',
myFlow: '我的流程',
flowProcDef: '流程定义',
flowProcDefSave: '保存流程定义',
flowProcDefDelete: '删除流程定义',
system: '系统管理',
menuPermission: '菜单权限',
menuPermissionBase: '基本权限',
menuPermissionAdd: '添加菜单权限',
menuPermissionEdit: '编辑菜单权限',
menuPermissionDelete: '删除菜单权限',
menuPermissionEnableDisable: '启用/禁用菜单权限',
account: '账号管理',
accountBase: '基本权限',
accountAdd: '添加账号',
accountEdit: '编辑账号',
accountDelete: '删除账号',
accountEnableDisable: '启用/禁用账号',
accountRoleAllocation: '角色分配',
role: '角色管理',
roleBase: '基本权限',
roleAdd: '添加角色',
roleEdit: '编辑角色',
roleDelete: '删除角色',
roleMenuPermissionAllocation: '菜单权限分配',
sysConf: '系统配置',
sysConfBase: '基本权限',
sysConfSave: '保存配置',
opLog: '操作日志',
opLogBase: '基本权限',
noPagePermission: '无页面权限',
authcertShowciphertext: '授权凭证密文查看',
},
home: {
personalInfo: '个人信息',
welcomeMsg: '您好, {name},生活变的再糟糕,也不妨碍我变得更好!',
@@ -348,9 +230,6 @@ export default {
accountPasswordLogin: '账号密码登录',
thirdPartyLogin: '第三方登录',
ldapLogin: 'LDAP 登录',
inputUsernamePlaceholder: '请输入用户名',
inputPasswordPlaceholder: '请输入密码',
inputCaptchaPlaceholder: '请输入验证码',
login: '登 录',
loginFailTip: '提示:登录失败超过{loginFailCount}次后将被限制{loginFailMin}分钟内不可再次登录',
loginSuccessTip: '欢迎回来!',
@@ -364,7 +243,6 @@ export default {
enterOtpCodeTip: '请输入令牌APP中显示的授权码',
updateBasicInfo: '修改基本信息',
name: '姓名',
inputNamePlaceholder: '请输入姓名',
},
components: {
df: {

View File

@@ -16,6 +16,7 @@ export default {
dbFilterPlaceholder: '库名: 输入可过滤',
sqlRecord: 'SQL记录',
dump: '导出',
dbDumpFail: '数据库导出失败',
dumpContent: '导出内容',
structure: '结构',
data: '数据',
@@ -55,13 +56,15 @@ export default {
execSuccess: '执行成功',
execFail: '执行失败',
sqlScriptRun: 'SQL脚本执行',
sqlScriptRunSuccess: 'SQL脚本执行成功',
sqlScriptRunFail: 'SQL脚本执行失败',
saveSql: '保存SQL',
execInfo: '执行信息',
result: '结果',
times: '耗时',
resultSet: '结果集',
tableDataEmptyTextTips: 'tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改',
noSelctRunSqlMsg: '请选中需要执行的sql',
noSelectRunSqlMsg: '请选中需要执行的sql或将光标移动到要执行sql附近',
enterExecRemarkTips: '请输入备注',
execRemarkPlaceholder: '输入执行该sql的备注信息',
currentSqlTabIsRunning: '当前结果集tab正在执行, 请使用新标签执行',

View File

@@ -0,0 +1,83 @@
export default {
docker: {
containerConf: '容器配置',
addr: '地址',
addrTips: '如unix:///var/run/docker.sock 、tcp://192.168.1.1',
container: '容器',
containerName: '容器名',
running: '运行中',
stopped: '已停止',
name: '容器名',
ip: 'IP地址',
status: '状态',
stats: '资源使用率',
memory: '内存',
stop: '停止',
stopContainerConfirm: '确定停止容器 [{name}] ',
removeContainerConfirm: '确定删除容器 [{name}] ',
restart: '重启',
createContainer: '创建容器',
mount: '挂载',
hostDir: '本机目录',
containerDir: '容器目录',
permission: '权限',
rw: '读写',
ro: '只读',
port: '端口',
image: '镜像',
tag: '标签',
size: '大小',
used: '已使用',
unUsed: '未使用',
imageName: '镜像名',
log: '日志',
lines: '行数',
follow: '实时',
stopImageConfirm: '确定删除该镜像?',
export: '导出',
imageUploading: '镜像导入中,请稍后...',
imageTips: '支持手动输入并选择',
forcePull: '强制拉取镜像',
hostPortPlaceholder: '80',
forcePullTips: '忽略服务器已存在的镜像,重新拉取一次',
server: '服务器',
protocol: '协议',
networkMode: '网络模式',
consoleTerminal: '控制台交互',
otherOption: '其他可选项',
tty: '伪终端 (-t)',
openStdin: '标准输入 (-i)',
privileged: '特权模式',
restartPolicy: '重启策略',
noRestart: '不重启',
alwaysRestart: '一直重启',
onFailure: '失败后重启',
unlessStopped: '未手动停止则重启',
cpuShare: 'CPU权重',
cpuShareTips: '容器默认份额为 1024 个 CPU增大可使当前容器获得更多的 CPU 时间',
cpuQuota: 'CPU 限制',
cpuLimitTips: 'CPU限制为 0 则关闭限制',
cpuCanUseTips: '最大可用为{cpuTotal}核',
core: '核',
memoryLimit: '内存限制',
memoryLimitTips: '内存限制为 0 则关闭限制',
shmSize: '共享内存',
memoryCanUseTips: '最大可用为{memTotal}',
tagTips: `一行一个,例如:
tag1=value1
tag2=value2`,
envParam: '环境变量',
envParamTips: `一行一个,例如:
env1=value1
env2=value2`,
device: '设备',
driver: '驱动',
driverTips: '容器需要使用的设备驱动程序,如: nvidia 等',
count: '数量',
capabilitie: '能力',
deviceId: '设备ID',
capabilitiePlaceholder: '如: gpu',
},
};

View File

@@ -0,0 +1,121 @@
export default {
es: {
keywordPlaceholder: 'host / 名称 / 编号',
protocol: '协议',
port: '端口',
size: '存储大小',
docs: '文档数',
health: '健康',
aliases: '别名',
addAlias: '添加别名',
specifyIdAdd: '可指定_id添加如果_id已存在则修改',
addIndex: '添加索引',
editIndex: '编辑索引',
status: '状态',
acName: '授权凭证',
emptyTable: '无数据',
connSuccess: '连接成功',
shouldTestConn: '请先测试连接可用性',
instance: 'ES实例',
instanceSave: 'Es-实例保存',
instanceDel: 'Es-实例删除',
operation: 'Es-数据操作',
dataSave: 'Es-数据保存',
dataDel: 'Es-数据删除',
indexName: '索引名',
requireIndexName: '请填写索引名',
indexDetail: '索引详情',
indexMapping: '映射',
indexStats: '统计信息',
opViewColumns: '设置显示字段',
opIndex: '索引管理',
opSearch: '搜索',
searchParamsPreview: '搜索条件预览',
opBasicSearch: '基础搜索',
opSeniorSearch: '高级搜索',
sampleMappings: 'Mapping示例',
copyMappings: '拷贝Mapping',
readonlyMsg: '该内容不可修改',
opDashboard: '仪表盘',
opSettings: '设置',
templates: '模板管理',
availableSettingFields: '支持修改的字段',
Reindex: '索引迁移',
ReindexTargetIdx: '目标索引',
ReindexIsSync: '是否异步',
ReindexDescription: '如果 Mapping 中字段已经定义就不能修改其字段的类型等属性了,同时也不能改变分片的数量, 可以使用 Reindex API 来解决这个问题。',
ReindexSyncDescription: '如果索引数据量较大,建议开启异步,以免造成请求超时。',
ReindexToOtherInst: '迁移到其他实例',
ReindexSyncTask: '异步任务',
makeSearchParam: '组装搜索条件',
filterColumn: '过滤列名',
searchParams: '查询',
searchParamsDesc: '未选择字段,或未设置条件值,则不生效',
standardSearch: '标准查询',
AggregationSearch: '聚合查询',
SqlSearch: 'Sql查询',
searchError: '查询错误',
execError: '执行错误',
docJsonError: '文档JSON格式错误',
sortParams: '排序',
otherParams: '其他',
closeIndexConfirm: '将会关闭索引:[{name}]。 确认继续吗?',
openIndexConfirm: '将会打开索引:[{name}]。 确认继续吗?',
clearCacheConfirm: '将会清除索引:[{name}]缓存。 确认继续吗?',
page: {
home: '首页',
prev: '上一页',
next: '下一页',
total: '点击切换总条数',
changeSize: '修改每页条数',
},
temp: {
addTemp: '添加模板',
view: '模板详情',
name: '模板名',
priority: '优先级',
index_patterns: '匹配模式',
content: '模板内容',
showHide: '显示隐藏模板',
description: '描述信息',
filter: '模糊过滤名字和描述',
versionAlert: '暂不支持 7.8 以前的版本',
note: `1、在新建索引时如果索引名与索引模板的通配符匹配那么就使用索引模板的设置_setting、_mapping等
2、模板仅在索引创建时才会生效而且修改模板不会影响现有的索引。
3、可以指定"priority"的数值7.8版本前是"order"如果新建的索引名匹配到了多个模板则优先使用priority最小的那个。`,
},
dashboard: {
instInfo: '实例信息',
clusterHealth: '集群健康',
nodes: '节点信息',
sysMem: '系统内存',
jvmMem: 'JVM内存',
fileSystem: '文件系统',
analyze: '字段分析',
idxName: '索引名',
field: '字段名',
text: '文本',
startAnalyze: '开始分析',
},
contextmenu: {
index: {
addIndex: '添加索引',
showSys: '显示系统索引',
copyName: '复制名字',
refresh: '刷新索引',
flush: 'flush索引',
clearCache: '清除索引缓存',
addAlias: '添加别名',
Close: '关闭索引',
Open: '打开索引',
Delete: '删除索引',
edit: '编辑索引',
DeleteSelectLine: '删除选中行',
BaseSearch: '基本搜索',
SeniorSearch: '高级搜索',
copyLineJson: '复制整行JSON',
copySelectLineJson: '复制选中行JSON',
},
},
},
};

View File

@@ -6,23 +6,23 @@ export default {
triggeringCondition: '触发条件',
triggeringConditionTips: 'go template语法。若输出结果为1则表示触发该审批流程',
conditionPlaceholder: '触发条件, 返回值=1, 则表示触发该审批流程',
conditionDefault: `{{/* DBMS-执行sql规则; param参数描述如下 */}}
{{/* stmtType: select / read / insert / update / delete / ddl ; */}}
{{ if eq .bizType "db_sql_exec_flow"}}
{{/* 不是select和read语句时开启流程审批 */}}
{{ if and (ne .param.stmtType "select") (ne .param.stmtType "read") }}
conditionDefault: `{'{{'}/* DBMS-执行sql规则; param参数描述如下 */{'}}'}
{'{{'}/* stmtType: select / read / insert / update / delete / ddl ; */{'}}'}
{'{{'} if eq .bizType "db_sql_exec_flow"{'}}'}
{'{{'}/* 不是select和read语句时开启流程审批 */{'}}'}
{'{{'} if and (ne .param.stmtType "select") (ne .param.stmtType "read"){'}}'}
1
{{ end }}
{{ end }}
{'{{'} end {'}}'}
{'{{'} end {'}}'}
{{/* Redis-执行命令规则; param参数描述如下 */}}
{{/* cmdType: read(读命令) / write(写命令); */}}
{{/* cmd: get/set/hset...等 */}}
{{ if eq .bizType "redis_run_cmd_flow"}}
{{ if eq .param.cmdType "write" }}
{'{{'}/* Redis-执行命令规则; param参数描述如下 */{'}}'}
{'{{'}/* cmdType: read(读命令) / write(写命令); */{'}}'}
{'{{'}/* cmd: get/set/hset...等 */{'}}'}
{'{{'} if eq .bizType "redis_run_cmd_flow"{'}}'}
{'{{'} if eq .param.cmdType "write" {'}}'}
1
{{ end }}
{{ end }}`,
{'{{'} end {'}}'}
{'{{'} end {'}}'}`,
nodeName: '节点名称',
nodeNameTips: '点击指定节点可进行拖拽排序',
auditor: '审核人员',
@@ -32,11 +32,32 @@ export default {
enable: '启用',
disable: '禁用',
todoTask: '待办任务',
doneTask: '已办任务',
flowDesign: '流程设计',
clear: '清空',
approvalMode: '审批模式',
andSign: '会签',
orSign: '或签',
voteSign: '票签',
taskCandidate: '处理候选人',
mustOneStartNode: '流程必须要有一个开始节点',
mustOneEndNode: '流程必须要有一个结束节点',
mustOneOutEdgeForStartNode: '开始节点必须有出线',
mustOneInEdgeForEndNode: '结束节点必须有入线',
approvalRecord: '审批记录',
start: '开始',
end: '结束',
usertask: '用户任务',
serial: '互斥网关',
parallel: '并行网关',
flowEdge: '流程线',
// procinst
startProcess: '发起流程',
cancelProcessConfirm: '确认取消该流程?',
bizType: '业务类型',
bizKey: '业务Key',
bizKey: '业务编号',
initiator: '发起人',
procdefName: '流程名',
bizStatus: '业务状态',
@@ -57,7 +78,7 @@ export default {
selectRedisPlaceholder: '请选择Redis实例与库',
cmdPlaceholder: `如: SET 'key' 'value'; 多条命令;分割`,
// ProcinstStatusEnum
active: '执行中',
active: '审批中',
completed: '完成',
suspended: '挂起',
terminated: '终止',
@@ -83,11 +104,14 @@ export default {
approveNode: '审批节点',
approveForm: '审批表单',
approveResult: '审批结果',
approvalRemark: '审批意见',
approver: '审批人',
audit: '审核',
procinstStatus: '流程状态',
taskStatus: '任务状态',
taskName: '当前节点',
taskName: '任务名',
taskBeginTime: '开始时间',
flowAudit: '流程审批',
notify: '通知',
},
};

View File

@@ -66,7 +66,7 @@ export default {
processName: '进程名',
selectSortType: '请选择排序类型',
selectProcessNum: '请选择进程个数',
cpuDesc: 'CUP降序',
cpuDesc: 'CPU降序',
memDesc: '内存降序',
virtualMemory: '虚拟内存',
fixedMemory: '固定内存',
@@ -88,6 +88,8 @@ export default {
scriptResultEnumRealTime: '实时交互',
scriptTypeEnumPrivate: '私有',
scriptTypeEnumPublic: '公共',
category: '分类',
categoryTips: '支持输入新分类并选择',
// security
cmdConfig: '命令配置',
@@ -137,5 +139,7 @@ export default {
fileTooLargeTips: '文件太大, 请下载使用',
uploadSuccess: '上传成功',
fileExceedsSysConf: '上传的文件超过系统配置的【{uploadMaxFileSize}】',
fileUploadSuccess: '机器文件上传成功',
fileUploadFail: '机器文件上传失败',
},
};

View File

@@ -0,0 +1,144 @@
export default {
menu: {
index: '首页',
personalCenter: '个人中心',
myResource: '我的资源',
tag: '资源',
tagTree: '资源树',
tagSave: '保存标签',
tagDelete: '删除标签',
authorization: '授权凭证',
authorizationBase: '基础权限',
authorizationSave: '保存权限',
authorizationDelete: '删除权限',
team: '团队',
teamSave: '保存团队',
teamDelete: '删除团队',
teamMemberAdd: '添加成员',
teamMemberDelete: '删除成员',
teamTagSave: '保存团队标签',
machine: '机器',
machineOp: '机器操作',
machineOpBase: '机器操作-基本权限',
machineList: '机器列表',
machineBase: '机器-基本权限',
machineCreate: '机器-创建机器',
machineEdit: '机器-编辑机器',
machineDelete: '机器-删除机器',
machineTerminal: '机器-机器终端',
machineFileConf: '机器-文件管理',
machineFileConfCreate: '机器-文件-添加配置',
machineFileConfDelete: '机器-文件-删除配置',
machineFileCreate: '机器-文件-创建',
machineFileDelete: '机器-文件-删除',
machineFileWrite: '机器-文件-写入',
machineFileUpload: '机器-文件-上传',
machineScript: '机器-脚本管理',
machineScriptSave: '机器-脚本-保存',
machineScriptDelete: '机器-脚本-删除',
machineScriptRun: '机器-脚本-执行',
machineKillprocess: '机器-终止进程',
machineCronJob: '计划任务',
machineCronJobSvae: '机器-计划任务-保存',
machineCronJobDelete: '机器-计划任务-删除',
machineSecurityConfig: '安全配置',
machineSecurityCmdSvae: '机器-命令配置-保存',
machineSecurityCmdDelete: '机器-命令配置-删除',
dbms: 'DBMS',
dbDataOp: '数据操作',
dbDataOpBase: 'Db-数据操作-基本权限',
dbDataOpSqlScriptRun: 'Db-SQL脚本执行',
dbInstance: '数据库实例',
dbInstanceBase: 'Db-基本权限',
dbInstanceSave: 'Db-保存实例',
dbInstanceDelete: 'Db-删除实例',
dbBase: '数据库基本权限',
dbSave: 'Db-保存数据库',
dbDelete: 'Db-删除数据库',
dbDataSync: '数据同步',
dbDataSyncBase: '基本权限',
dbDataSyncSave: '保存同步',
dbDataSyncDelete: '删除同步',
dbDataSyncChangeStatus: '启用停用',
dbDataSyncLog: '同步日志',
dbTransfer: '数据库迁移',
dbTransferBase: '基本权限',
dbTransferSave: '保存迁移任务',
dbTransferDelete: '删除迁移任务',
dbTransferChangeStatus: '启用停用',
dbTransferRun: '执行迁移任务',
dbTransferRunLog: '迁移日志查看',
dbTransferFileShow: '迁移文件-查看',
dbTransferFileDelete: '迁移文件-删除',
dbTransferFileDownload: '迁移文件-下载',
dbTransferFileRun: '迁移文件-执行',
redis: 'Redis',
redisDataOp: 'Redis-数据操作',
redisDataOpBase: 'Redis-数据操作-基本权限',
redisDataOpSave: 'Redis-数据操作-数据保存',
redisDataOpDelete: 'Redis-数据操作-数据删除',
redisManage: 'Redis管理',
redisManageBase: 'Redis-管理-基本权限',
mongo: 'Mongo',
mongoDataOp: '数据操作',
mongoDataOpBase: 'Mongo-数据操作-基本权限',
mongoDataOpSave: 'Mongo-数据操作-数据保存',
mongoDataOpDelete: 'Mongo-数据操作-数据删除',
mongoManage: 'Mongo管理',
mongoManageBase: 'Mongo-管理-基本权限',
containerManageBase: '容器-管理-基本权限',
flow: '工单流程',
myTask: '我的任务',
myFlow: '我的流程',
flowProcDef: '流程定义',
flowProcDefSave: '保存流程定义',
flowProcDefDelete: '删除流程定义',
msgManage: '消息',
channel: '消息渠道',
msgChannelBase: '基础权限',
saveMsgChannel: '保存消息渠道',
delMsgChannel: '删除消息渠道',
msgTmpl: '消息模板',
msgTmplBase: '基础权限',
saveMsgTmpl: '保存消息模板',
delMsgTmpl: '删除消息模板',
sendMsg: '发送消息',
system: '系统管理',
menuPermission: '菜单权限',
menuPermissionBase: '基本权限',
menuPermissionAdd: '添加菜单权限',
menuPermissionEdit: '编辑菜单权限',
menuPermissionDelete: '删除菜单权限',
menuPermissionEnableDisable: '启用/禁用菜单权限',
account: '账号管理',
accountBase: '基本权限',
accountAdd: '添加账号',
accountEdit: '编辑账号',
accountDelete: '删除账号',
accountEnableDisable: '启用/禁用账号',
accountRoleAllocation: '角色分配',
role: '角色管理',
roleBase: '基本权限',
roleAdd: '添加角色',
roleEdit: '编辑角色',
roleDelete: '删除角色',
roleMenuPermissionAllocation: '菜单权限分配',
sysConf: '系统配置',
sysConfBase: '基本权限',
sysConfSave: '保存配置',
opLog: '操作日志',
opLogBase: '基本权限',
noPagePermission: '无页面权限',
authcertShowciphertext: '授权凭证密文查看',
},
};

View File

@@ -0,0 +1,20 @@
export default {
msg: {
name: '名称',
email: '邮箱',
dingBot: '钉钉机器人',
qywxBot: '企微机器人',
feishuBot: '飞书机器人',
msgChannel: '消息渠道',
smtpAccount: 'SMTP账号',
smtpPassword: 'SMTP密码',
msgTmpl: '消息模板',
relateChannel: '关联渠道',
title: '标题',
tmpl: '模板',
send: '发送',
sendMsg: '发送消息',
selectTmplPlaceholder: '选择消息模板,支持编号模糊搜索',
msgTmplTooltip: "消息模板支持变量替换,变量格式为{'{{.变量名}}'},以下为通用内置变量 <br/>{'{{.receiver}}'}:用于{'@'}指定接收人",
},
};

View File

@@ -25,16 +25,13 @@ export default {
success: '成功',
menuCodeTips: `菜单类型则为访问路径(若菜单路径不以'/'开头则访问地址会自动拼接父菜单路径)、否则为资源唯一编码`,
menuCodePlaceholder: `菜单不以'/'开头则自动拼接父菜单路径`,
routerNameTips: '与vue的组件名一致才可使组件缓存生效如ResourceLis',
componentPathTips: '访问的组件路径,如:`system/resource/ResourceList`,默认在`views`目录下',
routerNameTips: '前端模块下route.ts中对应的key与vue的组件名一致才可使组件缓存生效如ResourceList',
isCacheTips: '选择是则会被`keep-alive`缓存(重新进入页面不会刷新页面及重新请求数据)需要路由名与vue的组件名一致',
isHideTips: '选择隐藏则路由将不会出现在菜单栏中,但仍然可以访问。禁用则不可访问与操作',
externalLinkTips: '内嵌: 以iframe展示、外链: 新标签打开',
inline: '内嵌',
linkAddress: '链接地址',
linkPlaceholder: '外链/内嵌的链接地址http://xxx.com',
menuNameRuleMsg: '请输入资源名称',
codeRuleMsg: '请输入code',
routeNameNotEmpty: '路由名不能为空',
resourceCodePatternErrMsg: '只允许输入1-32位大小写字母、数字、_-.:',
assignedRole: '已分配角色',
@@ -49,7 +46,7 @@ export default {
deleteAccountConfirm: '确定删除【{name}】的账号?',
usernamePlacholder: '5-16位大小写字母、数字、_-.:',
random: '随机',
usernamePatternErrMsg: '只允许输入5-16位大小写字母、数字、_-.:',
usernamePatternErrMsg: "只允许输入5-16位大小写字母、数字、_-.:{'@'}",
accountSearchPlaceholder: '输入账号模糊搜索并选择',
accountInfo: '账号信息',
allocateRoleTitle: '分配 `{name}` 的角色',
@@ -62,6 +59,9 @@ export default {
userMenuTitle: '`{name}` 的菜单&权限',
statusEnable: '启用',
statusDisable: '禁用',
qywxUserId: '企微UserId',
dingUserId: '钉钉UserId',
feishuUserId: '飞书UserId',
},
role: {
permissionDetail: '权限详情',

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