Compare commits

...

25 Commits

Author SHA1 Message Date
疯狂的狮子Li
d0d4519676 !273 发布 v5.6.2-v2.6.2 版本 依赖升级
* 🦁🦁🦁发布 v5.6.2-v2.6.2 版本 依赖升级
* 🦁🦁🦁发布 v5.6.2-v2.6.2 版本 依赖升级
* !269 fix 修复上传文件图标问题
* fix 修复上传文件图标问题
* update 优化 黑夜模式选中
* !265 修复深色模式按钮悬浮样式异常
* 修复深色模式按钮悬浮样式异常
* fix 修复 类型问题导致的警告与字典样式问题
* fix 修复 删除无用代码
* fix 修复 前端输入性CVE漏洞 禁止使用代码生成到本地路径
2026-06-17 05:05:10 +00:00
疯狂的狮子Li
9fd2b6f137
!263 发布 v5.6.1-v2.6.1 版本 依赖升级漏洞修复
Merge pull request !263 from 疯狂的狮子Li/dev
2026-04-24 01:50:59 +00:00
疯狂的狮子Li
2a6c440593 🦁🦁🦁发布 v5.6.1-v2.6.1 版本 依赖升级漏洞修复 2026-04-24 09:45:41 +08:00
疯狂的狮子Li
0ddf391de3 fix 修复 租户选择框样式问题 2026-04-14 10:32:46 +08:00
疯狂的狮子Li
f599aba890 update 优化 调整页面画风 不要过于生硬圆滑 2026-04-14 10:11:43 +08:00
疯狂的狮子Li
38a15e6cd3 update 页面圆角大小 默认调整为10 2026-04-14 09:19:51 +08:00
疯狂的狮子Li
e77fe0e618 update 布局设置增加 页面圆角大小 控制器 可以根据喜好自行设置页面圆角样式 2026-04-10 11:32:16 +08:00
疯狂的狮子Li
493d1131bf update vite 7.3.2 修复漏洞问题 2026-04-10 09:49:15 +08:00
疯狂的狮子Li
f37af6f48c update 将富文本编辑器重新改为oss存储(大家还是希望用oss) 2026-04-09 11:34:03 +08:00
疯狂的狮子Li
a79f2fb6c8 fix 修复 插件冲突导致样式一直重复加载问题 2026-03-31 09:24:53 +08:00
疯狂的狮子Li
eb6827765c fix 修复 搜索弹窗样式错误 2026-03-27 16:28:14 +08:00
疯狂的狮子Li
6bc3c618fe
!262 发布 v5.6.0-v2.6.0 版本 新年第一版
Merge pull request !262 from 疯狂的狮子Li/dev
2026-03-24 03:49:37 +00:00
疯狂的狮子Li
0076f5f6f7 🦁🦁🦁发布 v5.6.0-v2.6.0 版本 新年第一版 2026-03-24 11:47:41 +08:00
疯狂的狮子Li
ef5ea98a03 update 替代有问题的插件 2026-03-19 15:55:39 +08:00
gssong
73f2374c72 add 增加流程实例权限 2026-03-19 10:27:06 +08:00
gssong
9dcb392220 Merge remote-tracking branch 'origin/dev' into dev 2026-03-19 09:58:02 +08:00
gssong
54636ac14f add 补充流程定义权限 2026-03-19 09:57:56 +08:00
Lau
51a852caea
update 修改vben5前端仓库地址 2026-03-13 14:25:48 +00:00
疯狂的狮子Li
5c9c940588
!257 发布 5.5.3-2.5.3 版本 提前祝大家新年快乐
Merge pull request !257 from 疯狂的狮子Li/dev
2026-01-23 06:04:29 +00:00
疯狂的狮子Li
dfd1dc29d1
!256 fix 修复 代码漏改问题
Merge pull request !256 from 疯狂的狮子Li/dev
2025-12-23 05:56:54 +00:00
疯狂的狮子Li
b411505b19
!255 发布 5.5.2-2.5.2 版本 2025年最后一版
Merge pull request !255 from 疯狂的狮子Li/dev
2025-12-23 01:41:45 +00:00
疯狂的狮子Li
52ea8895d6
!246 发布 5.5.1-2.5.1 日常依赖升级bug修复
Merge pull request !246 from 疯狂的狮子Li/dev
2025-10-28 03:22:49 +00:00
疯狂的狮子Li
1b46739799
!241 发布 5.5.0-2.5.0 喜迎国庆
Merge pull request !241 from 疯狂的狮子Li/dev
2025-09-22 03:18:41 +00:00
疯狂的狮子Li
b000788785
!222 发布 5.4.1-2.4.1 小步迭代修复问题
Merge pull request !222 from 疯狂的狮子Li/dev
2025-07-01 01:13:49 +00:00
疯狂的狮子Li
2dc094c1db
!212 发布 5.4.0-2.4.0 正式版
Merge pull request !212 from 疯狂的狮子Li/dev
2025-05-29 03:18:30 +00:00
28 changed files with 381 additions and 233 deletions

View File

@ -1,7 +1,7 @@
## 平台简介 ## 平台简介
- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。 - 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
- 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) - 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://github.com/imdap/ruoyi-plus-vben5)
- 成员项目: 基于soybean 的前端项目 [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean) - 成员项目: 基于soybean 的前端项目 [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
## 配套后端代码仓库地址 ## 配套后端代码仓库地址

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package", "$schema": "https://json.schemastore.org/package",
"name": "ruoyi-vue-plus", "name": "ruoyi-vue-plus",
"version": "5.5.3-2.5.3", "version": "5.6.2-2.6.2",
"description": "RuoYi-Vue-Plus多租户管理系统", "description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi", "author": "LionLi",
"license": "MIT", "license": "MIT",
@ -73,8 +73,7 @@
"unplugin-icons": "23.0.1", "unplugin-icons": "23.0.1",
"unplugin-vue-components": "31.0.0", "unplugin-vue-components": "31.0.0",
"unplugin-vue-setup-extend-plus": "1.0.1", "unplugin-vue-setup-extend-plus": "1.0.1",
"vite": "7.3.1", "vite": "7.3.2",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons-ng": "^1.5.2", "vite-plugin-svg-icons-ng": "^1.5.2",
"vite-plugin-vue-devtools": "8.0.7", "vite-plugin-vue-devtools": "8.0.7",
"vitest": "4.0.18", "vitest": "4.0.18",

View File

@ -61,14 +61,6 @@ export const delTable = (tableId: string | number | Array<string | number>) => {
}); });
}; };
// 生成代码(自定义路径)
export const genCode = (tableId: string | number) => {
return request({
url: '/tool/gen/genCode/' + tableId,
method: 'get'
});
};
// 同步数据库 // 同步数据库
export const synchDb = (tableId: string | number) => { export const synchDb = (tableId: string | number) => {
return request({ return request({

View File

@ -13,8 +13,6 @@ export interface TableVO extends BaseEntity {
businessName: string; businessName: string;
functionName: string; functionName: string;
functionAuthor: string; functionAuthor: string;
genType: string;
genPath: string;
pkColumn?: any; pkColumn?: any;
columns?: any; columns?: any;
options?: any; options?: any;
@ -81,8 +79,6 @@ export interface DbTableVO {
businessName?: any; businessName?: any;
functionName?: any; functionName?: any;
functionAuthor?: any; functionAuthor?: any;
genType?: any;
genPath?: any;
pkColumn?: any; pkColumn?: any;
columns: DbColumnVO[]; columns: DbColumnVO[];
options?: any; options?: any;
@ -162,8 +158,6 @@ export interface DbTableForm extends BaseEntity {
businessName: string; businessName: string;
functionName: string; functionName: string;
functionAuthor: string; functionAuthor: string;
genType: string;
genPath: string;
pkColumn?: any; pkColumn?: any;
columns: DbColumnForm[]; columns: DbColumnForm[];
options: string; options: string;

View File

@ -74,7 +74,7 @@
.el-dialog { .el-dialog {
margin: 0 auto !important; margin: 0 auto !important;
border-radius: var(--app-radius-lg); border-radius: var(--app-radius-base);
box-shadow: var(--app-shadow-md); box-shadow: var(--app-shadow-md);
overflow: hidden; overflow: hidden;
@ -158,7 +158,7 @@
} }
.el-message-box { .el-message-box {
border-radius: var(--app-radius-lg); border-radius: var(--app-radius-base);
box-shadow: var(--app-shadow-md); box-shadow: var(--app-shadow-md);
} }
@ -192,7 +192,7 @@
// Buttons // Buttons
.el-button { .el-button {
border-radius: var(--app-radius-md); border-radius: var(--el-border-radius-base);
transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease; transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
} }
@ -215,7 +215,7 @@
} }
.el-drawer { .el-drawer {
border-radius: var(--app-radius-lg); border-radius: var(--app-radius-base);
box-shadow: var(--app-shadow-md); box-shadow: var(--app-shadow-md);
} }
@ -227,7 +227,7 @@
// Table polish // Table polish
.el-table { .el-table {
border-radius: var(--app-radius-lg); border-radius: var(--app-radius-base);
overflow: hidden; overflow: hidden;
box-shadow: var(--app-shadow-sm); box-shadow: var(--app-shadow-sm);
border: 1px solid var(--el-border-color-lighter); border: 1px solid var(--el-border-color-lighter);

View File

@ -115,7 +115,7 @@ div:focus {
} }
aside { aside {
background: #eef1f6; background: var(--el-fill-color-light);
padding: 8px 24px; padding: 8px 24px;
margin-bottom: 20px; margin-bottom: 20px;
border-radius: var(--app-radius-md); border-radius: var(--app-radius-md);
@ -131,44 +131,43 @@ aside {
'Hiragino Sans GB', 'Hiragino Sans GB',
'Microsoft YaHei', 'Microsoft YaHei',
sans-serif; sans-serif;
color: #2c3e50; color: var(--el-text-color-primary);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
a { a {
color: #337ab7; color: var(--el-color-primary);
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: rgb(32, 160, 255); color: var(--el-color-primary-light-3);
} }
} }
} }
//main-container全局样式 //main-container全局样式
.app-container { .app-container {
padding: 20px; padding: 16px;
background: var(--app-surface-bg); background: transparent;
border: 1px solid var(--app-surface-border); border: none;
border-radius: var(--app-radius-lg); border-radius: 0;
box-shadow: var(--app-shadow-sm); box-shadow: none;
} }
// search面板样式 // search面板样式
.panel, .panel,
.search { .search {
margin-bottom: 0.75rem; margin-bottom: 12px;
border-radius: var(--app-radius-lg); border-radius: var(--app-radius-base);
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-lighter);
background-color: var(--el-bg-color-overlay); padding: 16px;
padding: 0.75rem; background: var(--app-surface-bg);
box-shadow: 0 0 0 rgba(0, 0, 0, 0); box-shadow: var(--app-shadow-sm);
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease; transition: box-shadow 0.2s ease, border-color 0.2s ease;
&:hover { &:hover {
box-shadow: var(--app-shadow-sm); box-shadow: var(--app-shadow-md);
border-color: var(--el-border-color); border-color: var(--el-border-color-light);
transform: translateY(-1px);
} }
} }

View File

@ -77,7 +77,7 @@ h6 {
font-weight: 600; font-weight: 600;
} }
.el-dialog:not(.is-fullscreen) { .el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important; margin-top: 0 !important;
} }
.el-dialog.scrollbar .el-dialog__body { .el-dialog.scrollbar .el-dialog__body {
@ -125,8 +125,8 @@ h6 {
/* tree border */ /* tree border */
.tree-border { .tree-border {
margin-top: 5px; margin-top: 5px;
border: 1px solid #e5e6e7; border: 1px solid var(--el-border-color-light);
background: #ffffff none; background: var(--el-bg-color);
border-radius: var(--app-radius-md); border-radius: var(--app-radius-md);
width: 100%; width: 100%;
} }
@ -203,12 +203,11 @@ h6 {
box-shadow: var(--app-shadow-sm); box-shadow: var(--app-shadow-sm);
border-color: var(--el-border-color-lighter); border-color: var(--el-border-color-lighter);
overflow: hidden; overflow: hidden;
transition: box-shadow 0.2s ease, transform 0.2s ease; transition: box-shadow 0.2s ease;
} }
.el-card:hover { .el-card:hover {
box-shadow: var(--app-shadow-md); box-shadow: var(--app-shadow-md);
transform: translateY(-1px);
} }
.card-box { .card-box {

View File

@ -27,9 +27,11 @@
--tags-view-active-border-color: var(--el-color-primary); --tags-view-active-border-color: var(--el-color-primary);
// Modern rounded style + soft shadows // Modern rounded style + soft shadows
--app-radius-sm: 6px; --app-radius-base: 8px;
--app-radius-md: 10px; --app-radius-sm: calc(var(--app-radius-base) * 0.6);
--app-radius-lg: 14px; --app-radius-md: var(--app-radius-base);
--app-radius-lg: calc(var(--app-radius-base) * 1.4);
--app-radius-lg: var(--app-radius-base);
--app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.08); --app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.08);
--app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.12); --app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.12);
--app-shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.16); --app-shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.16);
@ -52,7 +54,7 @@ html.dark {
--menuHover: #171819; --menuHover: #171819;
--subMenuBg: #1d1e1f; --subMenuBg: #1d1e1f;
--subMenuActiveText: #1d1e1f; --subMenuActiveText: #f4f4f5;
--subMenuHover: #171819; --subMenuHover: #171819;
--subMenuTitleHover: #171819; --subMenuTitleHover: #171819;
@ -65,6 +67,29 @@ html.dark {
--el-color-primary-light-9: #262727; --el-color-primary-light-9: #262727;
} }
.el-table {
--el-table-current-row-bg-color: var(--el-color-primary-dark-7)
}
.el-dropdown__popper {
--el-dropdown-menuItem-hover-fill: var(--el-color-primary-dark-7)
}
.el-button {
--el-button-hover-bg-color: var(--el-color-primary-dark-9);
--el-button-hover-border-color: var(--el-color-primary-dark-7);
}
.el-button.is-plain {
--el-button-hover-bg-color: var(--el-fill-color-light);
--el-button-hover-border-color: var(--el-color-primary);
}
.el-button.is-dashed {
--el-button-hover-bg-color: var(--el-fill-color-light);
--el-button-hover-border-color: var(--el-color-primary);
}
.el-button--primary { .el-button--primary {
--el-button-bg-color: #2b6bd3; --el-button-bg-color: #2b6bd3;
--el-button-border-color: #3a7be8; --el-button-border-color: #3a7be8;
@ -79,6 +104,18 @@ html.dark {
--el-button-bg-color: rgba(43, 107, 211, 0.12); --el-button-bg-color: rgba(43, 107, 211, 0.12);
--el-button-border-color: rgba(58, 123, 232, 0.5); --el-button-border-color: rgba(58, 123, 232, 0.5);
--el-button-text-color: #dbe8ff; --el-button-text-color: #dbe8ff;
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-bg-color: var(--el-color-primary);
--el-button-hover-border-color: var(--el-color-primary);
--el-button-active-bg-color: rgba(43, 107, 211, 0.28);
--el-button-active-border-color: rgba(43, 107, 211, 0.8);
}
.el-button--primary.is-dashed {
--el-button-bg-color: rgba(43, 107, 211, 0.12);
--el-button-border-color: rgba(58, 123, 232, 0.5);
--el-button-text-color: #dbe8ff;
--el-button-hover-text-color: #dbe8ff;
--el-button-hover-bg-color: rgba(58, 123, 232, 0.2); --el-button-hover-bg-color: rgba(58, 123, 232, 0.2);
--el-button-hover-border-color: rgba(58, 123, 232, 0.7); --el-button-hover-border-color: rgba(58, 123, 232, 0.7);
--el-button-active-bg-color: rgba(43, 107, 211, 0.28); --el-button-active-bg-color: rgba(43, 107, 211, 0.28);
@ -99,6 +136,18 @@ html.dark {
--el-button-bg-color: rgba(31, 138, 90, 0.12); --el-button-bg-color: rgba(31, 138, 90, 0.12);
--el-button-border-color: rgba(41, 164, 109, 0.5); --el-button-border-color: rgba(41, 164, 109, 0.5);
--el-button-text-color: #dbf6e8; --el-button-text-color: #dbf6e8;
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-bg-color: var(--el-color-success);
--el-button-hover-border-color: var(--el-color-success);
--el-button-active-bg-color: rgba(31, 138, 90, 0.28);
--el-button-active-border-color: rgba(31, 138, 90, 0.8);
}
.el-button--success.is-dashed {
--el-button-bg-color: rgba(31, 138, 90, 0.12);
--el-button-border-color: rgba(41, 164, 109, 0.5);
--el-button-text-color: #dbf6e8;
--el-button-hover-text-color: #dbf6e8;
--el-button-hover-bg-color: rgba(41, 164, 109, 0.2); --el-button-hover-bg-color: rgba(41, 164, 109, 0.2);
--el-button-hover-border-color: rgba(41, 164, 109, 0.7); --el-button-hover-border-color: rgba(41, 164, 109, 0.7);
--el-button-active-bg-color: rgba(31, 138, 90, 0.28); --el-button-active-bg-color: rgba(31, 138, 90, 0.28);
@ -119,6 +168,18 @@ html.dark {
--el-button-bg-color: rgba(184, 121, 34, 0.12); --el-button-bg-color: rgba(184, 121, 34, 0.12);
--el-button-border-color: rgba(214, 149, 59, 0.5); --el-button-border-color: rgba(214, 149, 59, 0.5);
--el-button-text-color: #ffecc8; --el-button-text-color: #ffecc8;
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-bg-color: var(--el-color-warning);
--el-button-hover-border-color: var(--el-color-warning);
--el-button-active-bg-color: rgba(184, 121, 34, 0.28);
--el-button-active-border-color: rgba(184, 121, 34, 0.8);
}
.el-button--warning.is-dashed {
--el-button-bg-color: rgba(184, 121, 34, 0.12);
--el-button-border-color: rgba(214, 149, 59, 0.5);
--el-button-text-color: #ffecc8;
--el-button-hover-text-color: #ffecc8;
--el-button-hover-bg-color: rgba(214, 149, 59, 0.2); --el-button-hover-bg-color: rgba(214, 149, 59, 0.2);
--el-button-hover-border-color: rgba(214, 149, 59, 0.7); --el-button-hover-border-color: rgba(214, 149, 59, 0.7);
--el-button-active-bg-color: rgba(184, 121, 34, 0.28); --el-button-active-bg-color: rgba(184, 121, 34, 0.28);
@ -139,6 +200,18 @@ html.dark {
--el-button-bg-color: rgba(178, 74, 74, 0.12); --el-button-bg-color: rgba(178, 74, 74, 0.12);
--el-button-border-color: rgba(209, 96, 96, 0.5); --el-button-border-color: rgba(209, 96, 96, 0.5);
--el-button-text-color: #ffd6d6; --el-button-text-color: #ffd6d6;
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-bg-color: var(--el-color-danger);
--el-button-hover-border-color: var(--el-color-danger);
--el-button-active-bg-color: rgba(178, 74, 74, 0.28);
--el-button-active-border-color: rgba(178, 74, 74, 0.8);
}
.el-button--danger.is-dashed {
--el-button-bg-color: rgba(178, 74, 74, 0.12);
--el-button-border-color: rgba(209, 96, 96, 0.5);
--el-button-text-color: #ffd6d6;
--el-button-hover-text-color: #ffd6d6;
--el-button-hover-bg-color: rgba(209, 96, 96, 0.2); --el-button-hover-bg-color: rgba(209, 96, 96, 0.2);
--el-button-hover-border-color: rgba(209, 96, 96, 0.7); --el-button-hover-border-color: rgba(209, 96, 96, 0.7);
--el-button-active-bg-color: rgba(178, 74, 74, 0.28); --el-button-active-bg-color: rgba(178, 74, 74, 0.28);
@ -159,6 +232,18 @@ html.dark {
--el-button-bg-color: rgba(75, 85, 99, 0.16); --el-button-bg-color: rgba(75, 85, 99, 0.16);
--el-button-border-color: rgba(102, 112, 133, 0.55); --el-button-border-color: rgba(102, 112, 133, 0.55);
--el-button-text-color: #e5e7eb; --el-button-text-color: #e5e7eb;
--el-button-hover-text-color: var(--el-color-white);
--el-button-hover-bg-color: var(--el-color-info);
--el-button-hover-border-color: var(--el-color-info);
--el-button-active-bg-color: rgba(75, 85, 99, 0.3);
--el-button-active-border-color: rgba(75, 85, 99, 0.85);
}
.el-button--info.is-dashed {
--el-button-bg-color: rgba(75, 85, 99, 0.16);
--el-button-border-color: rgba(102, 112, 133, 0.55);
--el-button-text-color: #e5e7eb;
--el-button-hover-text-color: #e5e7eb;
--el-button-hover-bg-color: rgba(102, 112, 133, 0.22); --el-button-hover-bg-color: rgba(102, 112, 133, 0.22);
--el-button-hover-border-color: rgba(102, 112, 133, 0.75); --el-button-hover-border-color: rgba(102, 112, 133, 0.75);
--el-button-active-bg-color: rgba(75, 85, 99, 0.3); --el-button-active-bg-color: rgba(75, 85, 99, 0.3);

View File

@ -1,13 +1,15 @@
<template> <template>
<div> <div>
<el-upload <el-upload
v-if="type" v-if="type === 'url'"
action="" :action="upload.url"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
:http-request="handleUploadRequest" :on-success="handleUploadSuccess"
:on-error="handleUploadError"
class="editor-img-uploader" class="editor-img-uploader"
name="file" name="file"
:show-file-list="false" :show-file-list="false"
:headers="upload.headers"
> >
<i ref="uploadRef"></i> <i ref="uploadRef"></i>
</el-upload> </el-upload>
@ -29,7 +31,7 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { QuillEditor, Quill } from '@vueup/vue-quill'; import { QuillEditor, Quill } from '@vueup/vue-quill';
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
import type { UploadRequestHandler, UploadRequestOptions } from 'element-plus'; import { globalHeaders } from '@/utils/request';
defineEmits(['update:modelValue']); defineEmits(['update:modelValue']);
@ -45,11 +47,15 @@ const props = defineProps({
/* 上传文件大小限制(MB) */ /* 上传文件大小限制(MB) */
fileSize: propTypes.number.def(5), fileSize: propTypes.number.def(5),
/* 类型base64格式、url格式 */ /* 类型base64格式、url格式 */
type: propTypes.string.def('base64') type: propTypes.string.def('url')
}); });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const upload = reactive<UploadOption>({
headers: globalHeaders(),
url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
});
const quillEditorRef = ref(); const quillEditorRef = ref();
const uploadRef = ref<HTMLDivElement>(); const uploadRef = ref<HTMLDivElement>();
@ -110,9 +116,28 @@ watch(
{ immediate: true } { immediate: true }
); );
//
const handleUploadSuccess = (res: any) => {
//
if (res.code === 200) {
//
const quill = toRaw(quillEditorRef.value).getQuill();
//
const length = quill.selection.savedRange.index;
// res
quill.insertEmbed(length, 'image', res.data.url);
//
quill.setSelection(length + 1);
proxy?.$modal.closeLoading();
} else {
proxy?.$modal.msgError('图片插入失败');
proxy?.$modal.closeLoading();
}
};
// //
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg', 'image/svg+xml']; const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];
const isJPG = type.includes(file.type); const isJPG = type.includes(file.type);
// //
if (!isJPG) { if (!isJPG) {
@ -131,41 +156,9 @@ const handleBeforeUpload = (file: any) => {
return true; return true;
}; };
// base64 //
const handleUploadRequest: UploadRequestHandler = (options: UploadRequestOptions) => { const handleUploadError = (err: any) => {
return new Promise<void>((resolve, reject) => { proxy?.$modal.msgError('上传文件失败');
const file = options.file as File;
const quill = toRaw(quillEditorRef.value)?.getQuill();
if (!quill) {
proxy?.$modal.msgError('编辑器未就绪');
proxy?.$modal.closeLoading();
reject(new Error('editor not ready'));
return;
}
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result as string;
const range = quill.selection?.savedRange;
const length = range ? range.index : quill.getLength();
quill.insertEmbed(length, 'image', base64);
quill.setSelection(length + 1);
proxy?.$modal.closeLoading();
options.onSuccess?.({ url: base64 });
resolve();
};
reader.onerror = () => {
proxy?.$modal.msgError('图片插入失败');
proxy?.$modal.closeLoading();
const err = Object.assign(new Error('read image failed'), {
status: 0,
method: 'POST',
url: options.action || ''
});
options.onError?.(err as any);
reject(err);
};
reader.readAsDataURL(file);
});
}; };
</script> </script>

View File

@ -34,7 +34,7 @@
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul"> <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li v-for="(file, index) in fileList" :key="file.uid" class="el-upload-list__item ele-upload-list__item-content"> <li v-for="(file, index) in fileList" :key="file.uid" class="el-upload-list__item ele-upload-list__item-content">
<el-link :href="`${file.url}`" :underline="false" target="_blank"> <el-link :href="`${file.url}`" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span> <el-icon><document /></el-icon> {{ getFileName(file.name) }}
</el-link> </el-link>
<div class="ele-upload-list__item-content-action"> <div class="ele-upload-list__item-content-action">
<el-button type="danger" v-if="!disabled" link @click="handleDelete(index)">删除</el-button> <el-button type="danger" v-if="!disabled" link @click="handleDelete(index)">删除</el-button>

View File

@ -14,7 +14,7 @@
<el-select <el-select
v-if="userId === 1 && tenantEnabled" v-if="userId === 1 && tenantEnabled"
v-model="companyName" v-model="companyName"
class="min-w-244px" class="min-w-244px mr-2"
clearable clearable
filterable filterable
reserve-keyword reserve-keyword
@ -34,11 +34,11 @@
</el-tooltip> </el-tooltip>
<!-- 消息 --> <!-- 消息 -->
<el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom"> <el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom">
<div> <div style="display:flex;align-items:center">
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false"> <el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
<template #reference> <template #reference>
<el-badge :value="newNotice > 0 ? newNotice : ''" :max="99"> <el-badge :value="newNotice > 0 ? newNotice : ''" :max="99">
<div class="right-menu-item hover-effect" style="display: block"><svg-icon icon-class="message" /></div> <div class="right-menu-item hover-effect"><svg-icon icon-class="message" /></div>
</el-badge> </el-badge>
</template> </template>
<template #default> <template #default>
@ -289,9 +289,7 @@ watch(
} }
.right-menu { .right-menu {
//float: right;
height: 100%; height: 100%;
line-height: 50px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: auto; margin-left: auto;
@ -301,19 +299,22 @@ watch(
} }
.right-menu-item { .right-menu-item {
display: inline-block; display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 8px; padding: 0 8px;
height: 100%; height: 32px;
font-size: 18px; font-size: 18px;
color: var(--el-text-color-regular); color: var(--el-text-color-regular);
vertical-align: text-bottom; border-radius: var(--app-radius-md);
&.hover-effect { &.hover-effect {
cursor: pointer; cursor: pointer;
transition: background 0.3s; transition: background 0.2s ease, color 0.2s ease;
&:hover { &:hover {
background: var(--el-fill-color-lighter); background: var(--el-fill-color-light);
color: var(--el-color-primary);
} }
} }
} }
@ -322,7 +323,7 @@ watch(
margin-right: 40px; margin-right: 40px;
.avatar-wrapper { .avatar-wrapper {
margin-top: 5px; margin-top: 0;
position: relative; position: relative;
.user-avatar { .user-avatar {
@ -330,7 +331,8 @@ watch(
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: var(--app-radius-md); border-radius: var(--app-radius-md);
margin-top: 10px; margin-top: 0;
display: block;
} }
i { i {

View File

@ -75,6 +75,12 @@
<el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" /> <el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" />
</span> </span>
</div> </div>
<div class="drawer-item">
<span>页面圆角</span>
<span class="comp-style">
<el-slider v-model="radiusBase" :min="0" :max="32" :step="2" style="width: 120px" @change="radiusBaseChange" />
</span>
</div>
<el-divider /> <el-divider />
@ -143,6 +149,7 @@ const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore); const storeSettings = computed(() => settingsStore);
const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']); const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
const navType = ref(settingsStore.navType); const navType = ref(settingsStore.navType);
const radiusBase = ref(settingsStore.radiusBase);
// //
const isDark = useDark({ const isDark = useDark({
storageKey: 'useDarkKey', storageKey: 'useDarkKey',
@ -190,6 +197,16 @@ const themeChange = (val: string) => {
settingsStore.theme = val; settingsStore.theme = val;
handleThemeStyle(val); handleThemeStyle(val);
}; };
const radiusBaseChange = (val: number) => {
settingsStore.radiusBase = val;
const el = document.documentElement;
el.style.setProperty('--app-radius-base', `${val}px`);
el.style.setProperty('--app-radius-sm', `${Math.round(val * 0.6)}px`);
el.style.setProperty('--app-radius-md', `${val}px`);
el.style.setProperty('--app-radius-lg', `${Math.round(val * 1.4)}px`);
el.style.setProperty('--el-border-radius-base', `${val}px`);
el.style.setProperty('--el-border-radius-small', `${Math.round(val * 0.6)}px`);
};
const handleTheme = (val: string) => { const handleTheme = (val: string) => {
sideTheme.value = val; sideTheme.value = val;
if (isDark.value && val === SideThemeEnum.LIGHT) { if (isDark.value && val === SideThemeEnum.LIGHT) {
@ -210,6 +227,7 @@ const saveSetting = () => {
settings.value.sideTheme = storeSettings.value.sideTheme; settings.value.sideTheme = storeSettings.value.sideTheme;
settings.value.theme = storeSettings.value.theme; settings.value.theme = storeSettings.value.theme;
settings.value.navType = storeSettings.value.navType; settings.value.navType = storeSettings.value.navType;
settings.value.radiusBase = storeSettings.value.radiusBase;
setTimeout(() => { setTimeout(() => {
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
}, 1000); }, 1000);
@ -223,6 +241,10 @@ const openSetting = () => {
showSettings.value = true; showSettings.value = true;
}; };
onMounted(() => {
radiusBaseChange(storeSettings.value.radiusBase);
});
defineExpose({ defineExpose({
openSetting openSetting
}); });

View File

@ -11,18 +11,20 @@ import { useTagsViewStore } from '@/store/modules/tagsView';
const tagAndTagSpacing = ref(4); const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref<ElScrollbarInstance>(); const scrollContainerRef = ref<ElScrollbarInstance>();
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef); const scrollWrapper = computed<HTMLDivElement | undefined>(() => scrollContainerRef.value?.wrapRef);
onMounted(() => { onMounted(() => {
scrollWrapper.value?.addEventListener('scroll', emitScroll, true); scrollWrapper.value?.addEventListener('scroll', emitScroll, true);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
scrollWrapper.value?.removeEventListener('scroll', emitScroll); scrollWrapper.value?.removeEventListener('scroll', emitScroll, true);
}); });
const handleScroll = (e: WheelEvent) => { const handleScroll = (e: WheelEvent) => {
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40; const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
const $scrollWrapper = scrollWrapper.value; const $scrollWrapper = scrollWrapper.value;
if (!$scrollWrapper) return;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4; $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
}; };
const emits = defineEmits(['scroll']); const emits = defineEmits(['scroll']);
@ -37,6 +39,7 @@ const moveToTarget = (currentTag: RouteLocationNormalized) => {
const $container = scrollContainerRef.value?.$el; const $container = scrollContainerRef.value?.$el;
const $containerWidth = $container.offsetWidth; const $containerWidth = $container.offsetWidth;
const $scrollWrapper = scrollWrapper.value; const $scrollWrapper = scrollWrapper.value;
if (!$scrollWrapper) return;
let firstTag = null; let firstTag = null;
let lastTag = null; let lastTag = null;

View File

@ -243,9 +243,9 @@ onMounted(() => {
height: 34px; height: 34px;
width: 100%; width: 100%;
background-color: var(--el-bg-color); background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color-light); border-top: none;
border-radius: var(--app-radius-md); border-bottom: 1px solid var(--el-border-color-lighter);
box-shadow: var(--app-shadow-sm); box-shadow: none;
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
display: inline-block; display: inline-block;
@ -275,12 +275,12 @@ onMounted(() => {
margin-right: 15px; margin-right: 15px;
} }
&.active { &.active {
background-color: #42b983; background-color: var(--tags-view-active-bg);
color: #fff; color: #fff;
border-color: #42b983; border-color: var(--tags-view-active-border-color);
&::before { &::before {
content: ''; content: '';
background: #fff; background: rgba(255, 255, 255, 0.7);
display: inline-block; display: inline-block;
width: 8px; width: 8px;
height: 8px; height: 8px;

View File

@ -1,10 +1,11 @@
<template> <template>
<div class="layout-search-dialog"> <div class="layout-search-dialog">
<el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false"> <el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false" width="600px" top="12vh">
<template #footer> <div class="layout-search-dialog__content">
<el-autocomplete <el-autocomplete
ref="layoutMenuAutocompleteRef" ref="layoutMenuAutocompleteRef"
v-model="state.menuQuery" v-model="state.menuQuery"
class="layout-search-dialog__autocomplete"
:fetch-suggestions="menuSearch" :fetch-suggestions="menuSearch"
placeholder="搜索" placeholder="搜索"
:fit-input-width="true" :fit-input-width="true"
@ -20,7 +21,7 @@
</div> </div>
</template> </template>
</el-autocomplete> </el-autocomplete>
</template> </div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@ -132,27 +133,29 @@ defineExpose({
<style lang="scss" scoped> <style lang="scss" scoped>
.layout-search-dialog { .layout-search-dialog {
position: relative;
:deep(.el-dialog) { :deep(.el-dialog) {
padding: 0; border-radius: var(--app-radius-base);
overflow: visible;
.el-dialog__header, .el-dialog__header,
.el-dialog__body { .el-dialog__footer {
display: none; display: none;
} }
.el-dialog__footer { .el-dialog__body {
width: 100%; padding: 20px;
position: absolute;
left: 50%;
transform: translateX(-50%);
top: -53vh;
} }
} }
:deep(.el-autocomplete) {
width: 560px; &__content {
position: absolute; width: 100%;
top: 150px; }
left: 50%;
transform: translateX(-50%); &__autocomplete {
width: 100%;
}
:deep(.el-input__wrapper) {
min-height: 44px;
border-radius: var(--app-radius-md);
} }
} }
</style> </style>

View File

@ -127,6 +127,7 @@ const setLayout = () => {
width: calc(100% - #{$base-sidebar-width}); width: calc(100% - #{$base-sidebar-width});
transition: width 0.28s; transition: width 0.28s;
background: $fixed-header-bg; background: $fixed-header-bg;
box-shadow: 0 2px 8px rgba(0, 21, 41, 0.10);
} }
.hideSidebar .fixed-header { .hideSidebar .fixed-header {

View File

@ -71,6 +71,11 @@ const setting: DefaultSettings = {
/** /**
* *
*/ */
layout: '' layout: '',
/**
*
*/
radiusBase: 8
}; };
export default setting; export default setting;

View File

@ -7,7 +7,6 @@ import { NavTypeEnum } from '@/enums/NavTypeEnum';
export const useSettingsStore = defineStore('setting', () => { export const useSettingsStore = defineStore('setting', () => {
const storageSetting = useStorage<LayoutSetting>('layout-setting', { const storageSetting = useStorage<LayoutSetting>('layout-setting', {
topNav: defaultSettings.topNav,
tagsView: defaultSettings.tagsView, tagsView: defaultSettings.tagsView,
tagsIcon: defaultSettings.tagsIcon, tagsIcon: defaultSettings.tagsIcon,
fixedHeader: defaultSettings.fixedHeader, fixedHeader: defaultSettings.fixedHeader,
@ -15,7 +14,8 @@ export const useSettingsStore = defineStore('setting', () => {
dynamicTitle: defaultSettings.dynamicTitle, dynamicTitle: defaultSettings.dynamicTitle,
sideTheme: defaultSettings.sideTheme, sideTheme: defaultSettings.sideTheme,
theme: defaultSettings.theme, theme: defaultSettings.theme,
navType: defaultSettings.navType navType: defaultSettings.navType,
radiusBase: defaultSettings.radiusBase
}); });
const title = ref<string>(defaultSettings.title); const title = ref<string>(defaultSettings.title);
const theme = ref<string>(storageSetting.value.theme); const theme = ref<string>(storageSetting.value.theme);
@ -29,6 +29,7 @@ export const useSettingsStore = defineStore('setting', () => {
const animationEnable = ref<boolean>(defaultSettings.animationEnable); const animationEnable = ref<boolean>(defaultSettings.animationEnable);
const dark = ref<boolean>(defaultSettings.dark); const dark = ref<boolean>(defaultSettings.dark);
const navType = ref<NavTypeEnum>(storageSetting.value.navType || NavTypeEnum.LEFT); const navType = ref<NavTypeEnum>(storageSetting.value.navType || NavTypeEnum.LEFT);
const radiusBase = ref<number>(storageSetting.value.radiusBase ?? defaultSettings.radiusBase);
const setTitle = (value: string) => { const setTitle = (value: string) => {
title.value = value; title.value = value;
@ -47,6 +48,7 @@ export const useSettingsStore = defineStore('setting', () => {
animationEnable, animationEnable,
dark, dark,
navType, navType,
radiusBase,
setTitle setTitle
}; };
}); });

View File

@ -123,6 +123,10 @@ declare global {
* *
*/ */
theme: string; theme: string;
/**
*
*/
radiusBase: number;
} }
declare interface DefaultSettings extends LayoutSetting { declare interface DefaultSettings extends LayoutSetting {

View File

@ -33,7 +33,7 @@
* 部署方式 Docker 容器编排 一键部署业务集群<br /> * 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br /> * 国际化 SpringMessage Spring标准国际化方案<br />
</p> </p>
<p><b>当前版本:</b> <span>v5.5.3</span></p> <p><b>当前版本:</b> <span>v5.6.2</span></p>
<p> <p>
<el-tag type="danger">&yen;免费开源</el-tag> <el-tag type="danger">&yen;免费开源</el-tag>
</p> </p>
@ -77,7 +77,7 @@
* 分布式监控 PrometheusGrafana 全方位性能监控<br /> * 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br /> * 其余与 Vue 版本一致<br />
</p> </p>
<p><b>当前版本:</b> <span>v2.5.3</span></p> <p><b>当前版本:</b> <span>v2.6.2</span></p>
<p> <p>
<el-tag type="danger">&yen;免费开源</el-tag> <el-tag type="danger">&yen;免费开源</el-tag>
</p> </p>

View File

@ -67,7 +67,7 @@
<span class="link-type" @click.stop="handleTypeRowClick(scope.row)">{{ scope.row.dictType }}</span> <span class="link-type" @click.stop="handleTypeRowClick(scope.row)">{{ scope.row.dictType }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" width="160"/> <el-table-column label="备注" align="center" prop="remark" width="160" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180"> <el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ proxy.parseTime(scope.row.createTime) }}</span> <span>{{ proxy.parseTime(scope.row.createTime) }}</span>
@ -213,13 +213,15 @@
<el-input v-model="typeForm.dictName" placeholder="请输入字典名称" /> <el-input v-model="typeForm.dictName" placeholder="请输入字典名称" />
</el-form-item> </el-form-item>
<el-form-item prop="dictType"> <el-form-item prop="dictType">
<template #label>
<span>
<el-tooltip content="数据存储中的Key值sys_user_sex" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
字典类型
</span>
</template>
<el-input v-model="typeForm.dictType" placeholder="请输入字典类型" maxlength="100" /> <el-input v-model="typeForm.dictType" placeholder="请输入字典类型" maxlength="100" />
<span slot="label">
<el-tooltip content="数据存储中的Key值sys_user_sex" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
字典类型
</span>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="typeForm.remark" type="textarea" placeholder="请输入内容"></el-input> <el-input v-model="typeForm.remark" type="textarea" placeholder="请输入内容"></el-input>
@ -671,5 +673,4 @@ onMounted(() => {
.dict-table-wrap { .dict-table-wrap {
overflow-x: auto; overflow-x: auto;
} }
</style> </style>

View File

@ -82,44 +82,6 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item prop="genType">
<template #label>
生成代码方式
<el-tooltip content="默认为zip压缩包下载也可以自定义生成路径" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
</template>
<el-radio v-model="infoForm.genType" value="0">zip压缩包</el-radio>
<el-radio v-model="infoForm.genType" value="1">自定义路径</el-radio>
</el-form-item>
</el-col>
<el-col v-if="infoForm.genType == '1'" :span="24">
<el-form-item prop="genPath">
<template #label>
自定义路径
<el-tooltip content="填写磁盘绝对路径若不填写则生成到当前Web项目下" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
</template>
<el-input v-model="infoForm.genPath">
<template #append>
<el-dropdown>
<el-button type="primary">
最近路径快速选择
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="infoForm.genPath = '/'">恢复默认的生成基础路径</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row> </el-row>
<template v-if="info.tplCategory == 'tree'"> <template v-if="info.tplCategory == 'tree'">

View File

@ -113,7 +113,7 @@
</template> </template>
<script setup name="Gen" lang="ts"> <script setup name="Gen" lang="ts">
import { delTable, genCode, getDataNames, listTable, previewTable, synchDb } from '@/api/tool/gen'; import { delTable, getDataNames, listTable, previewTable, synchDb } from '@/api/tool/gen';
import { TableQuery, TableVO } from '@/api/tool/gen/types'; import { TableQuery, TableVO } from '@/api/tool/gen/types';
import router from '@/router'; import router from '@/router';
import ImportTable from './importTable.vue'; import ImportTable from './importTable.vue';
@ -181,12 +181,7 @@ const handleGenTable = async (row?: TableVO) => {
proxy?.$modal.msgError('请选择要生成的数据'); proxy?.$modal.msgError('请选择要生成的数据');
return; return;
} }
if (row?.genType === '1') { proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip');
await genCode(row.tableId);
proxy?.$modal.msgSuccess('成功生成到自定义路径:' + row.genPath);
} else {
proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip');
}
}; };
/** 同步数据库操作 */ /** 同步数据库操作 */
const handleSynchDb = async (row: TableVO) => { const handleSynchDb = async (row: TableVO) => {

View File

@ -42,19 +42,19 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" icon="Plus" @click="handleAdd()">添加</el-button> <el-button v-hasPermi="['workflow:definition:add']" type="primary" icon="Plus" @click="handleAdd()">添加</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button> <el-button v-hasPermi="['workflow:definition:edit']" type="success" icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button> <el-button v-hasPermi="['workflow:definition:remove']" type="danger" icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" icon="UploadFilled" @click="uploadDialog.visible = true">部署流程文件</el-button> <el-button v-hasPermi="['workflow:definition:import']" type="primary" icon="UploadFilled" @click="uploadDialog.visible = true">部署流程文件</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" icon="Download" :disabled="single" @click="handleExportDef">导出</el-button> <el-button v-hasPermi="['workflow:definition:export']" type="warning" icon="Download" :disabled="single" @click="handleExportDef">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar> <right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row> </el-row>
@ -74,6 +74,7 @@
<el-table-column align="center" prop="activityStatus" label="激活状态" width="130"> <el-table-column align="center" prop="activityStatus" label="激活状态" width="130">
<template #default="scope"> <template #default="scope">
<el-switch <el-switch
v-hasPermi="['workflow:definition:active']"
v-model="scope.row.activityStatus" v-model="scope.row.activityStatus"
:active-value="1" :active-value="1"
:inactive-value="0" :inactive-value="0"
@ -92,21 +93,21 @@
<template #default="scope"> <template #default="scope">
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除流程</el-button> <el-button v-hasPermi="['workflow:definition:remove']" link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除流程</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button link type="primary" size="small" icon="CopyDocument" @click="handleCopyDef(scope.row)">复制流程</el-button> <el-button v-hasPermi="['workflow:definition:copy']" link type="primary" size="small" icon="CopyDocument" @click="handleCopyDef(scope.row)">复制流程</el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button link type="primary" v-if="scope.row.isPublish === 0" icon="Pointer" size="small" @click="design(scope.row)" <el-button v-hasPermi="['workflow:definition:query']" link type="primary" v-if="scope.row.isPublish === 0" icon="Pointer" size="small" @click="design(scope.row)"
>流程设计</el-button >流程设计</el-button
> >
<el-button link type="primary" v-else icon="View" size="small" @click="designView(scope.row)">查看流程</el-button> <el-button v-hasPermi="['workflow:definition:query']" link type="primary" v-else icon="View" size="small" @click="designView(scope.row)">查看流程</el-button>
</el-col> </el-col>
<el-col v-if="scope.row.isPublish !== 1" :span="1.5"> <el-col v-if="scope.row.isPublish !== 1" :span="1.5">
<el-button link type="primary" size="small" icon="CircleCheck" @click="handlePublish(scope.row)">发布流程</el-button> <el-button v-hasPermi="['workflow:definition:publish']" link type="primary" size="small" icon="CircleCheck" @click="handlePublish(scope.row)">发布流程</el-button>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>

View File

@ -50,7 +50,7 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button> <el-button v-hasPermi="['workflow:instance:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-col> </el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar> <right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row> </el-row>
@ -75,7 +75,7 @@
<el-table-column align="center" prop="version" label="版本号" width="90"> <el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template> <template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column> </el-table-column>
<el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="状态" min-width="70"> <el-table-column v-if="tab === 'running'" v-hasPermi="['workflow:instance:active']" align="center" prop="isSuspended" label="状态" min-width="70">
<template #default="scope"> <template #default="scope">
<el-tag v-if="!scope.row.isSuspended" type="success">激活</el-tag> <el-tag v-if="!scope.row.isSuspended" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag> <el-tag v-else type="danger">挂起</el-tag>
@ -104,15 +104,15 @@
</el-popover> </el-popover>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" size="small" icon="Delete" @click="handleDelete(scope.row)">删除 </el-button> <el-button v-hasPermi="['workflow:instance:remove']" type="danger" size="small" icon="Delete" @click="handleDelete(scope.row)">删除 </el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button> <el-button v-hasPermi="['workflow:instance:query']" type="primary" size="small" icon="View" @click="handleView(scope.row)">查看</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" size="small" icon="Document" @click="handleInstanceVariable(scope.row)"> 变量 </el-button> <el-button v-hasPermi="['workflow:instance:variableQuery']" type="primary" size="small" icon="Document" @click="handleInstanceVariable(scope.row)"> 变量 </el-button>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
@ -138,7 +138,7 @@
<el-table-column align="center" prop="version" label="版本号" width="90"> <el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template> <template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column> </el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" min-width="70"> <el-table-column v-hasPermi="['workflow:instance:active']" align="center" prop="suspensionState" label="状态" min-width="70">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag> <el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag> <el-tag v-else type="danger">挂起</el-tag>
@ -170,7 +170,7 @@
<el-input v-model="form.value" placeholder="请输入变量值" /> <el-input v-model="form.value" placeholder="请输入变量值" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleVariable(ruleFormRef)">确认</el-button> <el-button v-hasPermi="['workflow:instance:variable']" type="primary" @click="handleVariable(ruleFormRef)">确认</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>

View File

@ -12,7 +12,9 @@ export default (path: any) => {
}, },
resolvers: [ resolvers: [
// 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式) // 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式)
ElementPlusResolver() ElementPlusResolver({
importStyle: false
})
], ],
vueTemplate: true, // 是否在 vue 模板中自动导入 vueTemplate: true, // 是否在 vue 模板中自动导入
dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts') dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts')

View File

@ -6,7 +6,9 @@ export default (path: any) => {
return Components({ return Components({
resolvers: [ resolvers: [
// 自动导入 Element Plus 组件 // 自动导入 Element Plus 组件
ElementPlusResolver(), ElementPlusResolver({
importStyle: false
}),
// 自动注册图标组件 // 自动注册图标组件
IconsResolver({ IconsResolver({
enabledCollections: ['ep'] enabledCollections: ['ep']

View File

@ -1,28 +1,110 @@
import compression from 'vite-plugin-compression'; import { promises as fs } from 'fs';
import path from 'path';
import zlib from 'zlib';
import { promisify } from 'util';
import type { Plugin, ResolvedConfig } from 'vite';
export default (env: any) => { const gzip = promisify(zlib.gzip);
const { VITE_BUILD_COMPRESS } = env; const brotliCompress = promisify(zlib.brotliCompress);
const plugin: any[] = []; const compressibleFileRE = /\.(js|mjs|json|css|html)$/i;
if (VITE_BUILD_COMPRESS) { const defaultThreshold = 1025;
const compressList = VITE_BUILD_COMPRESS.split(',');
if (compressList.includes('gzip')) { type CompressionKind = 'gzip' | 'brotli';
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push( const compressionHandlers: Record<CompressionKind, { ext: string; compress: (content: Buffer) => Promise<Buffer> }> = {
compression({ gzip: {
ext: '.gz', ext: '.gz',
deleteOriginFile: false compress: (content) => gzip(content, { level: zlib.constants.Z_BEST_COMPRESSION })
}) },
); brotli: {
} ext: '.br',
if (compressList.includes('brotli')) { compress: (content) =>
plugin.push( brotliCompress(content, {
compression({ params: {
ext: '.br', [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY,
algorithm: 'brotliCompress', [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT
deleteOriginFile: false }
}) })
);
}
} }
return plugin; };
async function collectFiles(rootDir: string): Promise<string[]> {
const entries = await fs.readdir(rootDir, { withFileTypes: true });
const files = await Promise.all(
entries.map(async (entry) => {
const fullPath = path.join(rootDir, entry.name);
if (entry.isDirectory()) {
return collectFiles(fullPath);
}
return compressibleFileRE.test(entry.name) ? [fullPath] : [];
})
);
return files.flat();
}
function createCompressionPlugin(kind: CompressionKind): Plugin {
const handler = compressionHandlers[kind];
let config: ResolvedConfig | undefined;
return {
name: `local:compression:${kind}`,
apply: 'build',
enforce: 'post',
configResolved(resolvedConfig) {
config = resolvedConfig;
},
async closeBundle() {
const outputDir = path.resolve(process.cwd(), config?.build.outDir ?? 'dist');
const files = await collectFiles(outputDir);
const compressedEntries: Array<{ file: string; originalKb: string; compressedKb: string }> = [];
await Promise.all(
files.map(async (filePath) => {
const stat = await fs.stat(filePath);
if (stat.size < defaultThreshold) {
return;
}
const content = await fs.readFile(filePath);
const compressed = await handler.compress(content);
const outputFile = `${filePath}${handler.ext}`;
await fs.writeFile(outputFile, compressed);
compressedEntries.push({
file: path.relative(outputDir, outputFile).replaceAll('\\', '/'),
originalKb: (stat.size / 1024).toFixed(2),
compressedKb: (compressed.byteLength / 1024).toFixed(2)
});
})
);
if (!compressedEntries.length) {
return;
}
compressedEntries.sort((a, b) => a.file.localeCompare(b.file));
config?.logger.info(`\n[compression:${kind}] generated ${compressedEntries.length} files`);
for (const entry of compressedEntries) {
config?.logger.info(`${path.basename(outputDir)}/${entry.file} ${entry.originalKb}kb -> ${entry.compressedKb}kb`);
}
config?.logger.info('');
}
};
}
export default (env: Record<string, string>) => {
const { VITE_BUILD_COMPRESS } = env;
const plugins: Plugin[] = [];
if (!VITE_BUILD_COMPRESS) {
return plugins;
}
const compressionList = VITE_BUILD_COMPRESS.split(',').map((item) => item.trim()) as CompressionKind[];
if (compressionList.includes('gzip')) {
plugins.push(createCompressionPlugin('gzip'));
}
if (compressionList.includes('brotli')) {
plugins.push(createCompressionPlugin('brotli'));
}
return plugins;
}; };