68 Commits

Author SHA1 Message Date
RuoYi
7d09f25218 若依 3.8.7 2023-12-08 09:02:39 +08:00
RuoYi
353f76740a 修复定时任务年表达式出现NaN问题 2023-12-01 21:54:17 +08:00
RuoYi
5186e2be77 TopNav开关事件控制 2023-12-01 21:53:23 +08:00
RuoYi
64036d520f 升级组件依赖到最新版本 2023-12-01 14:20:45 +08:00
RuoYi
15b2acbcd5 显隐列组件支持复选框弹出类型 2023-12-01 12:02:01 +08:00
RuoYi
51db1ec865 代码生成支持选择前端模板类型 2023-11-30 09:41:14 +08:00
RuoYi
0b124ef12b 优化菜单图标选择后点击其他区域闪烁问题 2023-11-30 09:41:05 +08:00
RuoYi
9e1616b075 优化控制台提示路由重复问题 2023-11-30 09:40:51 +08:00
RuoYi
ad0c7f5c94 优化头像上传参数新增文件名称 2023-11-29 12:45:00 +08:00
RuoYi
cb80de3742 优化字典标签支持自定义分隔符 2023-11-29 12:44:42 +08:00
RuoYi
438cb0d1dd 优化下载zip方法新增遮罩层 2023-11-29 12:44:01 +08:00
RuoYi
6ec98f2e64 优化缓存监控图表支持跟随屏幕大小自适应调整 2023-11-29 12:43:44 +08:00
RuoYi
dfb8096387 优化代码 2023-11-29 12:43:28 +08:00
RuoYi
e6f582bd1a 优化白名单页面放行逻辑 2023-11-28 13:00:15 +08:00
RuoYi
afd8de5094 优化个人中心/基本资料修改时数据显示问题 2023-11-28 11:26:51 +08:00
RuoYi
87be9b7acf 修复五级路由缓存无效问题 2023-11-10 15:34:08 +08:00
RuoYi
d3672b2714 优化样式 2023-11-10 14:48:44 +08:00
RuoYi
46da1d5ed1 修复内链iframe没有传递参数问题(I8DUOJ) 2023-11-10 11:13:38 +08:00
RuoYi
29355eb630 优化pagination样式 2023-11-01 10:13:19 +08:00
RuoYi
a4004968ec 修复字典数据重置选项置空问题 2023-11-01 09:55:42 +08:00
RuoYi
aea6ee2900 更换表格内预览图片被覆盖问题 2023-10-27 12:56:16 +08:00
RuoYi
98cba51541 登录不做数据重复提交验证 2023-10-21 14:31:24 +08:00
RuoYi
40648703fc 添加新群号:174951577 2023-10-09 21:28:31 +08:00
RuoYi
c12a0976ef 去除多余的参数 2023-10-09 21:28:17 +08:00
RuoYi
e8fe5f1455 修复HeaderSearch组件跳转query参数丢失问题 2023-09-28 22:12:58 +08:00
RuoYi
8e7754c5d7 富文本编辑器支持fileSize和type 2023-09-28 11:30:32 +08:00
RuoYi
895d9a37d5 升级vue-quill到最新版本1.2.0 2023-09-28 11:29:40 +08:00
RuoYi
27a1153b5a 操作日志列表新增IP地址查询 2023-09-27 15:22:27 +08:00
RuoYi
205a67504b 全局数据存储用户编号 2023-09-27 15:22:15 +08:00
RuoYi
8491d97001 优化菜单管理类型为按钮状态可选 2023-09-18 15:05:09 +08:00
RuoYi
1b45f0a1cd 修复自定义字典样式不生效的问题(I81F03) 2023-09-14 16:54:43 +08:00
RuoYi
accdc1be7a LICENSE 2023-09-01 08:44:50 +08:00
RuoYi
df9c850302 优化代码 2023-09-01 08:44:35 +08:00
RuoYi
54ad578ec0 TopNav更多菜单arrow样式优化 2023-08-31 18:00:47 +08:00
RuoYi
c5fe5bf3cb 修改未登录访问需要登录的资源,在登录后重定向丢失请求参数问题 2023-08-31 11:47:54 +08:00
RuoYi
a9196bbdf7 优化菜单样式 2023-08-31 10:02:21 +08:00
RuoYi
64014ba81a 优化控制台debuger位置错误问题 2023-08-28 13:02:33 +08:00
RuoYi
2e1bd34d58 防重复提交数据大小限制(I7KZDA) 2023-08-21 11:57:30 +08:00
RuoYi
38c52a9265 登录页面redirect重定向功能(I7DBUP) 2023-08-15 16:31:59 +08:00
RuoYi
d6aa40cf3b 优化定时任务状态页面显示 2023-08-14 19:21:29 +08:00
RuoYi
cce8921b52 添加新群号:143961921 2023-07-28 11:09:34 +08:00
RuoYi
7b8d2db756 若依 3.8.6 2023-06-30 08:24:18 +08:00
RuoYi
fab5c92d26 修改侧边栏的平台标题内容与process.env.VITE_APP_TITLE保持同步 2023-06-29 10:25:10 +08:00
RuoYi
9e7d46217a 添加新群号:136919097 2023-04-23 16:43:50 +08:00
RuoYi
78526a4bab 恢复翻页/切换路由滚动功能 2023-04-23 16:43:38 +08:00
RuoYi
8552850e2c DictTag组件,当value没有匹配的值时,展示value 2023-04-23 16:43:15 +08:00
RuoYi
4250792a2a 修复没有重叠的判断 2023-04-23 16:42:33 +08:00
RuoYi
ab98f29c3f 缓存列表:多次清除操作,提示不变的问题 2023-04-23 16:42:03 +08:00
RuoYi
3535b4d67f 修复开启TopNav后一级菜单路由参数设置无效问题(I6T1DK) 2023-04-11 16:52:09 +08:00
RuoYi
4610953b11 优化已选择下拉图标高亮回显 2023-04-10 18:33:56 +08:00
RuoYi
1d14ade6b5 优化避免鼠标移出时无法隐藏滚动条的问题 2023-04-10 18:33:40 +08:00
RuoYi
a4289fa8c3 添加新群号:101046199 2023-04-05 18:30:39 +08:00
RuoYi
c46abc1c79 优化固定头部页签滚动条被隐藏的问题 2023-04-05 18:30:25 +08:00
RuoYi
f61979f419 移除vue-multiselect样式 2023-04-05 18:23:43 +08:00
RuoYi
0fca81b350 关闭页签后存在其他页签时不应该跳转首页 2023-03-17 14:31:27 +08:00
RuoYi
3286323c8c 优化弹窗后导航栏偏移的问题(I3YMWW) 2023-03-17 14:24:54 +08:00
RuoYi
16c38acb10 修复页面切换时布局错乱的问题(I6LR1D) 2023-03-17 14:24:02 +08:00
RuoYi
ee2205d4f1 新增富文本组件支持 2023-03-06 14:19:40 +08:00
RuoYi
612164abde 优化文件下载出现的异常(I6DLNU) 2023-02-28 14:42:03 +08:00
RuoYi
e7ad4f5048 日志管理使用索引提升查询性能 2023-02-23 10:55:28 +08:00
RuoYi
0fd409dd58 支持登录IP黑名单限制 2023-02-21 09:02:36 +08:00
RuoYi
bce42bf13c 新增监控页面图标显示 2023-02-17 08:54:46 +08:00
RuoYi
10b387ff94 操作日志新增消耗时间属性 2023-02-16 11:05:31 +08:00
RuoYi
d70239952c 操作日志新增消耗时间属性 2023-02-16 10:23:43 +08:00
RuoYi
6a5e441155 首页页签右键选择时不显示关闭左侧 2023-02-04 22:40:20 +08:00
RuoYi
1f25c95c05 update copyright 2023 2023-02-04 22:33:47 +08:00
RuoYi
d32db45e36 重置时取消部门选中 2023-01-19 11:08:57 +08:00
RuoYi
1aba7f61d9 添加新群号:108482800 2023-01-11 12:55:12 +08:00
60 changed files with 848 additions and 318 deletions

20
LICENSE Normal file
View File

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

View File

@@ -1,11 +1,11 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.5</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.7</h1>
<h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.5-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.7-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
@@ -106,4 +106,4 @@ yarn dev
## 若依前后端分离交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) 点击按钮入群。
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) 点击按钮入群。

View File

@@ -1,9 +1,10 @@
{
"name": "ruoyi",
"version": "3.8.5",
"version": "3.8.7",
"description": "若依管理系统",
"author": "若依",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "vite",
"build:prod": "vite build",
@@ -15,29 +16,30 @@
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
},
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
"@vueuse/core": "9.5.0",
"@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1",
"axios": "0.27.2",
"echarts": "5.4.0",
"element-plus": "2.2.27",
"echarts": "5.4.3",
"element-plus": "2.4.3",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"js-cookie": "3.0.1",
"jsencrypt": "3.3.1",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"nprogress": "0.2.0",
"pinia": "2.0.22",
"vue": "3.2.45",
"vue-cropper": "1.0.3",
"vue-router": "4.1.4"
"pinia": "2.1.7",
"vue": "3.3.9",
"vue-cropper": "1.1.1",
"vue-router": "4.2.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "3.1.0",
"@vue/compiler-sfc": "3.2.45",
"sass": "1.56.1",
"unplugin-auto-import": "0.11.4",
"vite": "3.2.3",
"@vitejs/plugin-vue": "4.5.0",
"@vue/compiler-sfc": "3.3.9",
"sass": "1.69.5",
"unplugin-auto-import": "0.17.1",
"vite": "5.0.4",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-setup-extend": "0.4.0"
"unplugin-vue-setup-extend-plus": "1.0.0"
}
}

View File

@@ -11,7 +11,8 @@ export function login(username, password, code, uuid) {
return request({
url: '/login',
headers: {
isToken: false
isToken: false,
repeatSubmit: false
},
method: 'post',
data: data

BIN
src/assets/images/pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -182,12 +182,3 @@ aside {
margin-bottom: 10px;
}
}
//refine vue-multiselect plugin
.multiselect {
line-height: 16px;
}
.multiselect--active {
z-index: 1000 !important;
}

View File

@@ -102,11 +102,15 @@
/** 表格布局 **/
.pagination-container {
// position: relative;
height: 25px;
margin-bottom: 10px;
margin-top: 15px;
padding: 10px 20px !important;
position: relative;
height: 25px;
margin-bottom: 10px;
margin-top: 15px;
padding: 10px 20px !important;
}
.el-dialog .pagination-container {
position: static !important;
}
/* tree border */

View File

@@ -1,7 +1,7 @@
#app {
.main-container {
min-height: 100%;
height: 100%;
transition: margin-left .28s;
margin-left: $base-sidebar-width;
position: relative;

View File

@@ -18,10 +18,6 @@
transition: all .5s;
}
.fade-transform-leave-active {
position: absolute;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);

View File

@@ -1,5 +1,5 @@
<template>
<el-form size="small">
<el-form>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
允许的通配符[, - * ? / L W]

View File

@@ -1,5 +1,5 @@
<template>
<el-form size="small">
<el-form>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
小时允许的通配符[, - * /]

View File

@@ -1,5 +1,5 @@
<template>
<el-form size="small">
<el-form>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
分钟允许的通配符[, - * /]

View File

@@ -1,5 +1,5 @@
<template>
<el-form size='small'>
<el-form>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
允许的通配符[, - * /]

View File

@@ -1,5 +1,5 @@
<template>
<el-form size="small">
<el-form>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
允许的通配符[, - * /]

View File

@@ -1,5 +1,5 @@
<template>
<el-form size='small'>
<el-form>
<el-form-item>
<el-radio v-model='radioValue' :label="1">
允许的通配符[, - * ? / L #]

View File

@@ -1,5 +1,5 @@
<template>
<el-form size="small">
<el-form>
<el-form-item>
<el-radio :label="1" v-model='radioValue'>
不填允许的通配符[, - * /]
@@ -15,16 +15,16 @@
<el-form-item>
<el-radio :label="3" v-model='radioValue'>
周期从
<el-input-number v-model='cycle01' :min='fullYear' :max="maxFullYear - 1" /> -
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="maxFullYear" />
<el-input-number v-model='cycle01' :min='fullYear' :max="2098"/> -
<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : fullYear + 1" :max="2099"/>
</el-radio>
</el-form-item>
<el-form-item>
<el-radio :label="4" v-model='radioValue'>
<el-input-number v-model='average01' :min='fullYear' :max="maxFullYear - 1"/> 年开始
<el-input-number v-model='average02' :min="1" :max="10" /> 年执行一次
<el-input-number v-model='average01' :min='fullYear' :max="2098"/> 年开始
<el-input-number v-model='average02' :min="1" :max="2099 - average01 || fullYear"/> 年执行一次
</el-radio>
</el-form-item>
@@ -96,7 +96,7 @@ function changeRadioValue(value) {
cycle02.value = Number(indexArr[1])
radioValue.value = 3
} else if (value.indexOf("/") > -1) {
const indexArr = value.split('#')
const indexArr = value.split('/')
average01.value = Number(indexArr[1])
average02.value = Number(indexArr[0])
radioValue.value = 4

View File

@@ -3,11 +3,11 @@
<template v-for="(item, index) in options">
<template v-if="values.includes(item.value)">
<span
v-if="item.elTagType == 'default' || item.elTagType == ''"
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
:key="item.value"
:index="index"
:class="item.elTagClass"
>{{ item.label }}</span>
>{{ item.label + " " }}</span>
<el-tag
v-else
:disable-transitions="true"
@@ -15,13 +15,19 @@
:index="index"
:type="item.elTagType === 'primary' ? '' : item.elTagType"
:class="item.elTagClass"
>{{ item.label }}</el-tag>
>{{ item.label + " " }}</el-tag>
</template>
</template>
<template v-if="unmatch && showValue">
{{ unmatchArray | handleArray }}
</template>
</div>
</template>
<script setup>
// 记录未匹配的项
const unmatchArray = ref([]);
const props = defineProps({
// 数据
options: {
@@ -30,20 +36,47 @@ const props = defineProps({
},
// 当前的值
value: [Number, String, Array],
})
// 当未找到匹配的数据时显示value
showValue: {
type: Boolean,
default: true,
},
separator: {
type: String,
default: ",",
}
});
const values = computed(() => {
if (props.value !== null && typeof props.value !== 'undefined') {
return Array.isArray(props.value) ? props.value : [String(props.value)];
} else {
return [];
}
})
if (props.value === null || typeof props.value === 'undefined' || props.value === '') return [];
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
});
const unmatch = computed(() => {
unmatchArray.value = [];
// 没有value不显示
if (props.value === null || typeof props.value === 'undefined' || props.value === '' || props.options.length === 0) return false
// 传入值为数组
let unmatch = false // 添加一个标志来判断是否有未匹配项
values.value.forEach(item => {
if (!props.options.some(v => v.value === item)) {
unmatchArray.value.push(item)
unmatch = true // 如果有未匹配项将标志设置为true
}
})
return unmatch // 返回标志的值
});
function handleArray(array) {
if (array.length === 0) return "";
return array.reduce((pre, cur) => {
return pre + " " + cur;
});
}
</script>
<style scoped>
.el-tag + .el-tag {
margin-left: 10px;
}
</style>
</style>

View File

@@ -0,0 +1,251 @@
<template>
<div>
<el-upload
:action="uploadUrl"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
name="file"
:show-file-list="false"
:headers="headers"
class="editor-img-uploader"
v-if="type == 'url'"
>
<i ref="uploadRef" class="editor-img-uploader"></i>
</el-upload>
</div>
<div class="editor">
<quill-editor
ref="quillEditorRef"
v-model:content="content"
contentType="html"
@textChange="(e) => $emit('update:modelValue', content)"
:options="options"
:style="styles"
/>
</div>
</template>
<script setup>
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { getToken } from "@/utils/auth";
const { proxy } = getCurrentInstance();
const quillEditorRef = ref();
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
const headers = ref({
Authorization: "Bearer " + getToken()
});
const props = defineProps({
/* 编辑器的内容 */
modelValue: {
type: String,
},
/* 高度 */
height: {
type: Number,
default: null,
},
/* 最小高度 */
minHeight: {
type: Number,
default: null,
},
/* 只读 */
readOnly: {
type: Boolean,
default: false,
},
/* 上传文件大小限制(MB) */
fileSize: {
type: Number,
default: 5,
},
/* 类型base64格式、url格式 */
type: {
type: String,
default: "url",
}
});
const options = ref({
theme: "snow",
bounds: document.body,
debug: "warn",
modules: {
// 工具栏配置
toolbar: [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["link", "image", "video"] // 链接、图片、视频
],
},
placeholder: "请输入内容",
readOnly: props.readOnly
});
const styles = computed(() => {
let style = {};
if (props.minHeight) {
style.minHeight = `${props.minHeight}px`;
}
if (props.height) {
style.height = `${props.height}px`;
}
return style;
});
const content = ref("");
watch(() => props.modelValue, (v) => {
if (v !== content.value) {
content.value = v === undefined ? "<p></p>" : v;
}
}, { immediate: true });
// 如果设置了上传地址则自定义图片上传事件
onMounted(() => {
if (props.type == 'url') {
let quill = quillEditorRef.value.getQuill();
let toolbar = quill.getModule("toolbar");
toolbar.addHandler("image", (value) => {
if (value) {
proxy.$refs.uploadRef.click();
} else {
quill.format("image", false);
}
});
}
});
// 上传前校检格式和大小
function handleBeforeUpload(file) {
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
const isJPG = type.includes(file.type);
//检验文件格式
if (!isJPG) {
proxy.$modal.msgError(`图片格式错误!`);
return false;
}
// 校检文件大小
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
return false;
}
}
return true;
}
// 上传成功处理
function handleUploadSuccess(res, file) {
// 如果上传成功
if (res.code == 200) {
// 获取富文本实例
let quill = toRaw(quillEditorRef.value).getQuill();
// 获取光标位置
let length = quill.selection.savedRange.index;
// 插入图片res.url为服务器返回的图片链接地址
quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName);
// 调整光标到最后
quill.setSelection(length + 1);
} else {
proxy.$modal.msgError("图片插入失败");
}
}
// 上传失败处理
function handleUploadError() {
proxy.$modal.msgError("图片插入失败");
}
</script>
<style>
.editor-img-uploader {
display: none;
}
.editor, .ql-toolbar {
white-space: pre-wrap !important;
line-height: normal !important;
}
.quill-img {
display: none;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: "保存";
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode="video"]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
content: "10px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
content: "32px";
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: "标题1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: "标题2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: "标题3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: "标题4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: "标题5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: "标题6";
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
content: "衬线字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
content: "等宽字体";
}
</style>

View File

@@ -164,10 +164,11 @@ function uploadedSuccessfully() {
// 获取文件名称
function getFileName(name) {
// 如果是url那么取最后的名字 如果不是直接返回
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1);
} else {
return "";
return name;
}
}

View File

@@ -45,12 +45,17 @@ function close() {
}
function change(val) {
const path = val.path;
const query = val.query;
if (isHttp(path)) {
// http(s):// 路径新窗口打开
const pindex = path.indexOf("http");
window.open(path.substr(pindex, path.length), "_blank");
} else {
router.push(path)
if (query) {
router.push({ path: path, query: JSON.parse(query) });
} else {
router.push(path)
}
}
search.value = ''
@@ -98,6 +103,9 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
res.push(data)
}
}
if (r.query) {
data.query = r.query
}
// recursive child routes
if (r.children) {

View File

@@ -2,7 +2,7 @@
<div class="icon-body">
<el-input
v-model="iconName"
style="position: relative;"
class="icon-search"
clearable
placeholder="请输入图标名称"
@clear="filterIcons"
@@ -11,9 +11,13 @@
<template #suffix><i class="el-icon-search el-input__icon" /></template>
</el-input>
<div class="icon-list">
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
<svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
<span>{{ item }}</span>
<div class="list-container">
<div v-for="(item, index) in iconList" class="icon-item-wrapper" :key="index" @click="selectedIcon(item)">
<div :class="['icon-item', { active: activeIcon === item }]">
<svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/>
<span>{{ item }}</span>
</div>
</div>
</div>
</div>
</div>
@@ -22,6 +26,12 @@
<script setup>
import icons from './requireIcons'
const props = defineProps({
activeIcon: {
type: String
}
});
const iconName = ref('');
const iconList = ref(icons);
const emit = defineEmits(['selected']);
@@ -49,26 +59,53 @@ defineExpose({
</script>
<style lang='scss' scoped>
.icon-body {
width: 100%;
padding: 10px;
.icon-list {
height: 200px;
overflow-y: scroll;
div {
height: 30px;
line-height: 30px;
margin-bottom: -5px;
cursor: pointer;
width: 33%;
float: left;
.icon-body {
width: 100%;
padding: 10px;
.icon-search {
position: relative;
margin-bottom: 5px;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
.icon-list {
height: 200px;
overflow: auto;
.list-container {
display: flex;
flex-wrap: wrap;
.icon-item-wrapper {
width: calc(100% / 3);
height: 25px;
line-height: 25px;
cursor: pointer;
display: flex;
.icon-item {
display: flex;
max-width: 100%;
height: 100%;
padding: 0 5px;
&:hover {
background: #ececec;
border-radius: 5px;
}
.icon {
flex-shrink: 0;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
padding-left: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.icon-item.active {
background: #ececec;
border-radius: 5px;
}
}
}
}
}
}
</style>

View File

@@ -4,7 +4,7 @@
fit="cover"
:style="`width:${realWidth};height:${realHeight};`"
:preview-src-list="realSrcList"
append-to-body="true"
preview-teleported
>
<template #error>
<div class="image-slot">

View File

@@ -8,7 +8,19 @@
<el-button circle icon="Refresh" @click="refresh()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
<el-button circle icon="Menu" @click="showColumn()" />
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
<el-button circle icon="Menu" />
<template #dropdown>
<el-dropdown-menu>
<template v-for="item in columns" :key="item.key">
<el-dropdown-item>
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-tooltip>
</el-row>
<el-dialog :title="title" v-model="open" append-to-body>
@@ -24,17 +36,26 @@
<script setup>
const props = defineProps({
/* 是否显示检索条件 */
showSearch: {
type: Boolean,
default: true,
},
/* 显隐列信息 */
columns: {
type: Array,
},
/* 是否显示检索图标 */
search: {
type: Boolean,
default: true,
},
/* 显隐列类型transfer穿梭框、checkbox复选框 */
showColumnsType: {
type: String,
default: "checkbox",
},
/* 右外边距 */
gutter: {
type: Number,
default: 10,
@@ -81,12 +102,20 @@ function showColumn() {
open.value = true;
}
// 显隐列初始默认隐藏列
for (let item in props.columns) {
if (props.columns[item].visible === false) {
value.value.push(parseInt(item));
if (props.showColumnsType == 'transfer') {
// 显隐列初始默认隐藏列
for (let item in props.columns) {
if (props.columns[item].visible === false) {
value.value.push(parseInt(item));
}
}
}
// 勾选
function checkboxChange(event, label) {
props.columns.filter(item => item.label == label)[0].visible = event;
}
</script>
<style lang='scss' scoped>
@@ -98,8 +127,8 @@ for (let item in props.columns) {
:deep(.el-transfer__button:first-child) {
margin-bottom: 10px;
}
.my-el-transfer {
text-align: center;
:deep(.el-dropdown-menu__item) {
line-height: 30px;
padding: 0 17px;
}
</style>
</style>

View File

@@ -6,10 +6,12 @@
:ellipsis="false"
>
<template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
><svg-icon :icon-class="item.meta.icon" />
{{ item.meta.title }}</el-menu-item
>
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template>
<!-- 顶部菜单超出数量折叠 -->
@@ -19,10 +21,12 @@
<el-menu-item
:index="item.path"
:key="index"
v-if="index >= visibleNumber"
><svg-icon :icon-class="item.meta.icon" />
{{ item.meta.title }}</el-menu-item
>
v-if="index >= visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template>
</el-sub-menu>
</el-menu>
@@ -121,7 +125,13 @@ function handleSelect(key, keyPath) {
window.open(key, "_blank");
} else if (!route || !route.children) {
// 没有子路由路径内部打开
router.push({ path: key });
const routeMenu = childrenMenus.value.find(item => item.path === key);
if (routeMenu && routeMenu.query) {
let query = JSON.parse(routeMenu.query);
router.push({ path: key, query: query });
} else {
router.push({ path: key });
}
appStore.toggleSideBarHide(true);
} else {
// 显示左侧联动菜单
@@ -183,4 +193,22 @@ onMounted(() => {
padding: 0 5px !important;
margin: 0 10px !important;
}
/* 背景色隐藏 */
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
background-color: #ffffff !important;
}
/* 图标右间距 */
.topmenu-container .svg-icon {
margin-right: 4px;
}
/* topmenu more arrow */
.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
position: static;
vertical-align: middle;
margin-left: 8px;
margin-top: 0px;
}
</style>

View File

@@ -47,7 +47,22 @@ const tagsViewStore = useTagsViewStore()
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 17px;
padding-right: 6px;
}
}
</style>
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background-color: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background-color: #c0c0c0;
border-radius: 3px;
}
</style>

View File

@@ -1,19 +1,25 @@
<template>
<transition-group name="fade-transform" mode="out-in">
<inner-link
v-for="(item, index) in tagsViewStore.iframeViews"
:key="item.path"
:iframeId="'iframe' + index"
v-show="route.path === item.path"
:src="item.meta.link"
></inner-link>
</transition-group>
<inner-link
v-for="(item, index) in tagsViewStore.iframeViews"
:key="item.path"
:iframeId="'iframe' + index"
v-show="route.path === item.path"
:src="iframeUrl(item.meta.link, item.query)"
></inner-link>
</template>
<script setup>
import InnerLink from "../InnerLink/index"
import useTagsViewStore from '@/store/modules/tagsView'
import InnerLink from "../InnerLink/index";
import useTagsViewStore from "@/store/modules/tagsView";
const route = useRoute();
const tagsViewStore = useTagsViewStore()
const tagsViewStore = useTagsViewStore();
function iframeUrl(url, query) {
if (Object.keys(query).length > 0) {
let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&");
return url + "?" + params;
}
return url;
}
</script>

View File

@@ -33,7 +33,7 @@
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item command="setLayout">
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item divided command="logout">

View File

@@ -38,35 +38,35 @@
<div class="drawer-item">
<span>开启 TopNav</span>
<span class="comp-style">
<el-switch v-model="topNav" class="drawer-switch" />
<el-switch v-model="settingsStore.topNav" @change="topNavChange" class="drawer-switch" />
</span>
</div>
<div class="drawer-item">
<span>开启 Tags-Views</span>
<span class="comp-style">
<el-switch v-model="tagsView" class="drawer-switch" />
<el-switch v-model="settingsStore.tagsView" class="drawer-switch" />
</span>
</div>
<div class="drawer-item">
<span>固定 Header</span>
<span class="comp-style">
<el-switch v-model="fixedHeader" class="drawer-switch" />
<el-switch v-model="settingsStore.fixedHeader" class="drawer-switch" />
</span>
</div>
<div class="drawer-item">
<span>显示 Logo</span>
<span class="comp-style">
<el-switch v-model="sidebarLogo" class="drawer-switch" />
<el-switch v-model="settingsStore.sidebarLogo" class="drawer-switch" />
</span>
</div>
<div class="drawer-item">
<span>动态标题</span>
<span class="comp-style">
<el-switch v-model="dynamicTitle" class="drawer-switch" />
<el-switch v-model="settingsStore.dynamicTitle" class="drawer-switch" />
</span>
</div>
@@ -80,7 +80,6 @@
<script setup>
import variables from '@/assets/styles/variables.module.scss'
import originElementPlus from 'element-plus/theme-chalk/index.css'
import axios from 'axios'
import { ElLoading, ElMessage } from 'element-plus'
import { useDynamicTitle } from '@/utils/dynamicTitle'
@@ -100,54 +99,19 @@ const storeSettings = computed(() => settingsStore);
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
/** 是否需要topnav */
const topNav = computed({
get: () => storeSettings.value.topNav,
set: (val) => {
settingsStore.changeSetting({ key: 'topNav', value: val })
if (!val) {
appStore.toggleSideBarHide(false);
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
}
function topNavChange(val) {
if (!val) {
appStore.toggleSideBarHide(false);
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
}
})
/** 是否需要tagview */
const tagsView = computed({
get: () => storeSettings.value.tagsView,
set: (val) => {
settingsStore.changeSetting({ key: 'tagsView', value: val })
}
})
/**是否需要固定头部 */
const fixedHeader = computed({
get: () => storeSettings.value.fixedHeader,
set: (val) => {
settingsStore.changeSetting({ key: 'fixedHeader', value: val })
}
})
/**是否需要侧边栏的logo */
const sidebarLogo = computed({
get: () => storeSettings.value.sidebarLogo,
set: (val) => {
settingsStore.changeSetting({ key: 'sidebarLogo', value: val })
}
})
/**是否需要侧边栏的动态网页的title */
const dynamicTitle = computed({
get: () => storeSettings.value.dynamicTitle,
set: (val) => {
settingsStore.changeSetting({ key: 'dynamicTitle', value: val })
// 动态设置网页标题
useDynamicTitle()
}
})
}
function themeChange(val) {
settingsStore.changeSetting({ key: 'theme', value: val })
theme.value = val;
settingsStore.theme = val;
handleThemeStyle(val);
}
function handleTheme(val) {
settingsStore.changeSetting({ key: 'sideTheme', value: val })
settingsStore.sideTheme = val;
sideTheme.value = val;
}
function saveSetting() {

View File

@@ -25,7 +25,7 @@ defineProps({
}
})
const title = ref('若依管理系统');
const title = import.meta.env.VITE_APP_TITLE;
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
</script>

View File

@@ -9,15 +9,15 @@
</app-link>
</template>
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
<template v-if="item.meta" #title>
<svg-icon :icon-class="item.meta && item.meta.icon" />
<span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
v-for="(child, index) in item.children"
:key="child.path + index"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"

View File

@@ -99,7 +99,7 @@ defineExpose({
bottom: 0px;
}
:deep(.el-scrollbar__wrap) {
height: 49px;
height: 39px;
}
}
</style>

View File

@@ -94,7 +94,7 @@ function isAffix(tag) {
}
function isFirstView() {
try {
return selectedTag.value.fullPath === visitedViews.value[1].fullPath || selectedTag.value.fullPath === '/index'
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath
} catch (err) {
return false
}
@@ -280,7 +280,7 @@ function handleScroll() {
height: 8px;
border-radius: 50%;
position: relative;
margin-right: 2px;
margin-right: 5px;
}
}
}

View File

@@ -3,7 +3,8 @@ import { createApp } from 'vue'
import Cookies from 'js-cookie'
import ElementPlus from 'element-plus'
import locale from 'element-plus/lib/locale/lang/zh-cn' // 中文语言
import 'element-plus/dist/index.css'
import locale from 'element-plus/es/locale/lang/zh-cn'
import '@/assets/styles/index.scss' // global css
@@ -12,7 +13,6 @@ import store from './store'
import router from './router'
import directive from './directive' // directive
// 注册指令
import plugins from './plugins' // plugins
import { download } from '@/utils/request'
@@ -31,6 +31,8 @@ import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, select
import Pagination from '@/components/Pagination'
// 自定义表格工具组件
import RightToolbar from '@/components/RightToolbar'
// 富文本组件
import Editor from "@/components/Editor"
// 文件上传组件
import FileUpload from "@/components/FileUpload"
// 图片上传组件
@@ -62,6 +64,7 @@ app.component('FileUpload', FileUpload)
app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)
app.use(router)
app.use(store)

View File

@@ -11,7 +11,7 @@ import usePermissionStore from '@/store/modules/permission'
NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'];
const whiteList = ['/login', '/register'];
router.beforeEach((to, from, next) => {
NProgress.start()
@@ -21,6 +21,8 @@ router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
if (useUserStore().roles.length === 0) {
isRelogin.show = true

View File

@@ -1,11 +1,12 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
import axios from 'axios'
import { ElLoading, ElMessage } from 'element-plus'
import { saveAs } from 'file-saver'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { blobValidate } from '@/utils/ruoyi'
const baseURL = import.meta.env.VITE_APP_BASE_API
let downloadLoadingInstance;
export default {
name(name, isDelete = true) {
@@ -15,9 +16,9 @@ export default {
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then(async (res) => {
const isLogin = await blobValidate(res.data);
if (isLogin) {
}).then((res) => {
const isBlob = blobValidate(res.data);
if (isBlob) {
const blob = new Blob([res.data])
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
} else {
@@ -32,9 +33,9 @@ export default {
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then(async (res) => {
const isLogin = await blobValidate(res.data);
if (isLogin) {
}).then((res) => {
const isBlob = blobValidate(res.data);
if (isBlob) {
const blob = new Blob([res.data])
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
} else {
@@ -44,19 +45,25 @@ export default {
},
zip(url, name) {
var url = baseURL + url
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then(async (res) => {
const isLogin = await blobValidate(res.data);
if (isLogin) {
}).then((res) => {
const isBlob = blobValidate(res.data);
if (isBlob) {
const blob = new Blob([res.data], { type: 'application/zip' })
this.saveAs(blob, name)
} else {
this.printErrMsg(res.data);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close();
})
},
saveAs(text, name, opts) {

View File

@@ -32,8 +32,12 @@ export default {
// 关闭指定tab页签
closePage(obj) {
if (obj === undefined) {
return useTagsViewStore().delView(router.currentRoute.value).then(({ lastPath }) => {
return router.push(lastPath || '/index');
return useTagsViewStore().delView(router.currentRoute.value).then(({ visitedViews }) => {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
return router.push(latestView.fullPath)
}
return router.push('/');
});
}
return useTagsViewStore().delView(obj);

View File

@@ -10,7 +10,7 @@ export default {
/**
* 是否系统布局配置
*/
showSettings: false,
showSettings: true,
/**
* 是否显示顶部导航

View File

@@ -101,6 +101,10 @@ function filterChildren(childrenMap, lastRouter = false) {
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path
if (el.children && el.children.length) {
children = children.concat(filterChildren(el.children, el))
return
}
}
children = children.concat(el)
})

View File

@@ -7,6 +7,7 @@ const useUserStore = defineStore(
{
state: () => ({
token: getToken(),
id: '',
name: '',
avatar: '',
roles: [],
@@ -42,8 +43,9 @@ const useUserStore = defineStore(
} else {
this.roles = ['ROLE_DEFAULT']
}
this.id = user.userId
this.name = user.userName
this.avatar = avatar;
this.avatar = avatar
resolve(res)
}).catch(error => {
reject(error)

View File

@@ -42,6 +42,12 @@ service.interceptors.request.use(config => {
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小
const limitSize = 5 * 1024 * 1024; // 限制存放数据5M
if (requestSize >= limitSize) {
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制无法进行防重复提交验证。')
return config;
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
@@ -72,7 +78,7 @@ service.interceptors.response.use(res => {
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
@@ -125,8 +131,8 @@ export function download(url, params, filename, config) {
responseType: 'blob',
...config
}).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const isBlob = blobValidate(data);
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {

View File

@@ -241,12 +241,6 @@ export function getNormalPath(p) {
}
// 验证是否为blob格式
export async function blobValidate(data) {
try {
const text = await data.text();
JSON.parse(text);
return false;
} catch (error) {
return true;
}
}
export function blobValidate(data) {
return data.type !== 'application/json'
}

View File

@@ -118,12 +118,11 @@
>
</p>
<p>
<i class="el-icon-user-solid"></i> QQ群<s>满937441</s> <s>满887144332</s>
<s>满180251782</s> <s>满104180207</s> <s>满186866453</s> <s>满201396349</s>
<s>满101456076</s> <s>满101539465</s> <s>满264312783</s> <s>满167385320</s>
<s>满104748341</s> <s>满160110482</s> <a href="https://jq.qq.com/?_wv=1027&k=Xnbinl0U" target="_blank">
170801498</a
>
<i class="el-icon-user-solid"></i> QQ群<s> 满937441 </s> <s> 满887144332 </s>
<s> 满180251782 </s> <s> 满104180207 </s> <s> 满186866453 </s> <s> 满201396349 </s>
<s> 满101456076 </s> <s> 满101539465 </s> <s> 满264312783 </s> <s> 满167385320 </s>
<s> 满104748341 </s> <s> 满160110482 </s> <s> 满170801498 </s> <s> 满108482800 </s>
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=1HmEGh7zKA_CKI2E-pGInPTlC5jS9mc_&authKey=XaiUf1wfbSTEecm4lDMtIMsc6g%2BoETxjBm1BbZPr6IfuMGRj7oG4GEeu7jtzNaw%2F&noverify=0&group_code=174951577" target="_blank">174951577</a>
</p>
<p>
<i class="el-icon-chat-dot-round"></i> 微信<a
@@ -149,6 +148,87 @@
</div>
</template>
<el-collapse accordion>
<el-collapse-item title="v3.8.7 - 2023-12-08">
<ol>
<li>操作日志记录部门名称</li>
<li>全局数据存储用户编号</li>
<li>新增编程式判断资源访问权限</li>
<li>操作日志列表新增IP地址查询</li>
<li>定时任务新增页去除状态选项</li>
<li>代码生成支持选择前端模板类型</li>
<li>显隐列组件支持复选框弹出类型</li>
<li>通用排序属性orderBy参数限制长度</li>
<li>Excel自定义数据处理器增加单元格/工作簿对象</li>
<li>升级oshi到最新版本6.4.8</li>
<li>升级druid到最新版本1.2.20</li>
<li>升级fastjson到最新版2.0.43</li>
<li>升级pagehelper到最新版1.4.7</li>
<li>升级commons.io到最新版本2.13.0</li>
<li>升级element-ui到最新版本2.15.14</li>
<li>修复五级路由缓存无效问题</li>
<li>修复外链带端口出现的异常</li>
<li>修复树模板父级编码变量错误</li>
<li>修复字典表详情页面搜索问题</li>
<li>修复内链iframe没有传递参数问题</li>
<li>修复自定义字典样式不生效的问题</li>
<li>修复字典缓存删除方法参数错误问题</li>
<li>修复Excel导入数据临时文件无法删除问题</li>
<li>修复未登录带参数访问成功后参数丢失问题</li>
<li>修复HeaderSearch组件跳转query参数丢失问题</li>
<li>修复代码生成导入后必填项与数据库不匹配问题</li>
<li>修复Excels导入时无法获取到dictType字典值问题</li>
<li>优化下载zip方法新增遮罩层</li>
<li>优化头像上传参数新增文件名称</li>
<li>优化字典标签支持自定义分隔符</li>
<li>优化菜单管理类型为按钮状态可选</li>
<li>优化前端防重复提交数据大小限制</li>
<li>优化TopNav菜单没有图标svg不显示</li>
<li>优化数字金额大写转换精度丢失问题</li>
<li>优化富文本Editor组件检验图片格式</li>
<li>优化页签在Firefox浏览器被遮挡的问题</li>
<li>优化个人中心/基本资料修改时数据显示问题</li>
<li>优化缓存监控图表支持跟随屏幕大小自适应调整</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.6 - 2023-06-30">
<ol>
<li>支持登录IP黑名单限制</li>
<li>新增监控页面图标显示</li>
<li>操作日志新增消耗时间属性</li>
<li>屏蔽定时任务bean违规的字符</li>
<li>日志管理使用索引提升查询性能</li>
<li>日志注解支持排除指定的请求参数</li>
<li>支持自定义隐藏属性列过滤子对象</li>
<li>升级oshi到最新版本6.4.3</li>
<li>升级druid到最新版本1.2.16</li>
<li>升级fastjson到最新版2.0.34</li>
<li>升级spring-boot到最新版本2.5.15</li>
<li>升级element-ui到最新版本2.15.13</li>
<li>移除apache/commons-fileupload依赖</li>
<li>修复页面切换时布局错乱的问题</li>
<li>修复匿名注解Anonymous空指针问题</li>
<li>修复路由跳转被阻止时内部产生报错信息问题</li>
<li>修复isMatchedIp的参数判断产生空指针的问题</li>
<li>修复用户多角色数据权限可能出现权限抬升的情况</li>
<li>修复开启TopNav后一级菜单路由参数设置无效问题</li>
<li>修复DictTag组件value没有匹配的值时则展示value</li>
<li>优化文件下载出现的异常</li>
<li>优化选择图标组件高亮回显</li>
<li>优化弹窗后导航栏偏移的问题</li>
<li>优化修改密码日志存储明文问题</li>
<li>优化页签栏关闭其他出现的异常问题</li>
<li>优化页签关闭左侧选项排除首页选项</li>
<li>优化关闭当前tab页跳转最右侧tab页</li>
<li>优化缓存列表清除操作提示不变的问题</li>
<li>优化字符未使用下划线不进行驼峰式处理</li>
<li>优化用户导入更新时需获取用户编号问题</li>
<li>优化侧边栏的平台标题与VUE_APP_TITLE保持同步</li>
<li>优化导出Excel时设置dictType属性重复查缓存问题</li>
<li>连接池Druid支持新的配置connectTimeout和socketTimeout</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.5 - 2023-01-01">
<ol>
<li>定时任务违规的字符</li>
@@ -893,7 +973,7 @@
</template>
<div class="body">
<img
src="https://oscimg.oschina.net/oscnet/up-d6695f82666e5018f715c41cb7ee60d3b73.png"
src="@/assets/images/pay.png"
alt="donate"
style="width:100%"
/>
@@ -908,7 +988,7 @@
</template>
<script setup name="Index">
const version = ref('3.8.5')
const version = ref('3.8.7')
function goTarget(url) {
window.open(url, '__blank')

View File

@@ -59,7 +59,7 @@
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span>
<span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
</div>
</div>
</template>
@@ -71,6 +71,7 @@ import { encrypt, decrypt } from "@/utils/jsencrypt";
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const route = useRoute();
const router = useRouter();
const { proxy } = getCurrentInstance();
@@ -96,6 +97,10 @@ const captchaEnabled = ref(true);
const register = ref(false);
const redirect = ref(undefined);
watch(route, (newRoute) => {
redirect.value = newRoute.query && newRoute.query.redirect;
}, { immediate: true });
function handleLogin() {
proxy.$refs.loginRef.validate(valid => {
if (valid) {
@@ -113,7 +118,14 @@ function handleLogin() {
}
// 调用action的登录方法
userStore.login(loginForm.value).then(() => {
router.push({ path: redirect.value || "/" });
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
}
return acc;
}, {});
router.push({ path: redirect.value || "/", query: otherQueryParams });
}).catch(() => {
loading.value = false;
// 重新获取验证码

View File

@@ -3,7 +3,7 @@
<el-row>
<el-col :span="24" class="card-box">
<el-card>
<template #header><span>基本信息</span></template>
<template #header><Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">基本信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%">
<tbody>
@@ -45,7 +45,7 @@
<el-col :span="12" class="card-box">
<el-card>
<template #header><span>命令统计</span></template>
<template #header><PieChart style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">命令统计</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="commandstats" style="height: 420px" />
</div>
@@ -54,9 +54,7 @@
<el-col :span="12" class="card-box">
<el-card>
<template #header>
<span>内存信息</span>
</template>
<template #header><Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="usedmemory" style="height: 420px" />
</div>
@@ -100,7 +98,6 @@ function getList() {
}
]
});
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
usedmemoryInstance.setOption({
tooltip: {
@@ -124,6 +121,10 @@ function getList() {
}
]
})
window.addEventListener("resize", () => {
commandstatsIntance.resize();
usedmemoryInstance.resize();
});
})
}

View File

@@ -4,7 +4,7 @@
<el-col :span="8">
<el-card style="height: calc(100vh - 125px)">
<template #header>
<span>缓存列表</span>
<Collection style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">缓存列表</span>
<el-button
style="float: right; padding: 3px 0"
link
@@ -63,7 +63,7 @@
<el-col :span="8">
<el-card style="height: calc(100vh - 125px)">
<template #header>
<span>键名列表</span>
<Key style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">键名列表</span>
<el-button
style="float: right; padding: 3px 0"
link
@@ -114,7 +114,7 @@
<el-col :span="8">
<el-card :bordered="false" style="height: calc(100vh - 125px)">
<template #header>
<span>缓存内容</span>
<Document style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">缓存内容</span>
<el-button
style="float: right; padding: 3px 0"
link
@@ -185,7 +185,7 @@ function refreshCacheNames() {
/** 清理指定名称缓存 */
function handleClearCacheName(row) {
clearCacheName(row.cacheName).then(response => {
proxy.$modal.msgSuccess("清理缓存名称[" + nowCacheName.value + "]成功");
proxy.$modal.msgSuccess("清理缓存名称[" + row.cacheName + "]成功");
getCacheKeys();
});
}

View File

@@ -138,7 +138,7 @@
/>
<!-- 添加或修改定时任务对话框 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body>
<el-dialog :title="title" v-model="open" width="820px" append-to-body>
<el-form ref="jobRef" :model="form" :rules="rules" label-width="120px">
<el-row>
<el-col :span="12">
@@ -190,7 +190,18 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-col :span="24" v-if="form.jobId !== undefined">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_job_status"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行策略" prop="misfirePolicy">
<el-radio-group v-model="form.misfirePolicy">
<el-radio-button label="1">立即执行</el-radio-button>
@@ -207,17 +218,6 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_job_status"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
@@ -256,7 +256,7 @@
<el-col :span="12">
<el-form-item label="任务状态:">
<div v-if="form.status == 0">正常</div>
<div v-else-if="form.status == 1">失败</div>
<div v-else-if="form.status == 1">暂停</div>
</el-form-item>
</el-col>
<el-col :span="12">

View File

@@ -37,11 +37,12 @@
<el-form-item label="登录时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item>
@@ -105,7 +106,7 @@
<dict-tag :options="sys_common_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="描述" align="center" prop="msg" />
<el-table-column label="描述" align="center" prop="msg" :show-overflow-tooltip="true" />
<el-table-column label="访问时间" align="center" prop="loginTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.loginTime) }}</span>

View File

@@ -1,6 +1,15 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="操作地址" prop="operIp">
<el-input
v-model="queryParams.operIp"
placeholder="请输入操作地址"
clearable
style="width: 240px;"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="系统模块" prop="title">
<el-input
v-model="queryParams.title"
@@ -52,11 +61,12 @@
<el-form-item label="操作时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item>
@@ -98,27 +108,31 @@
</el-row>
<el-table ref="operlogRef" v-loading="loading" :data="operlogList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="日志编号" align="center" prop="operId" />
<el-table-column label="系统模块" align="center" prop="title" />
<el-table-column label="系统模块" align="center" prop="title" :show-overflow-tooltip="true" />
<el-table-column label="操作类型" align="center" prop="businessType">
<template #default="scope">
<dict-tag :options="sys_oper_type" :value="scope.row.businessType" />
</template>
</el-table-column>
<el-table-column label="请求方式" align="center" prop="requestMethod" />
<el-table-column label="操作人员" align="center" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" width="100" />
<el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
<el-table-column label="操作人员" align="center" width="110" prop="operName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
<el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
<el-table-column label="操作状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_common_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作日期" align="center" prop="operTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
<el-table-column label="操作日期" align="center" prop="operTime" width="180" sortable="custom" :sort-orders="['descending', 'ascending']">
<template #default="scope">
<span>{{ parseTime(scope.row.operTime) }}</span>
</template>
</el-table-column>
<el-table-column label="消耗时间" align="center" prop="costTime" width="110" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']">
<template #default="scope">
<span>{{ scope.row.costTime }}毫秒</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="View" @click="handleView(scope.row, scope.index)" v-hasPermi="['monitor:operlog:query']">详细</el-button>
@@ -135,7 +149,7 @@
/>
<!-- 操作日志详细 -->
<el-dialog title="操作日志详细" v-model="open" width="700px" append-to-body>
<el-dialog title="操作日志详细" v-model="open" width="800px" append-to-body>
<el-form :model="form" label-width="100px">
<el-row>
<el-col :span="12">
@@ -157,13 +171,16 @@
<el-col :span="24">
<el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="8">
<el-form-item label="操作状态:">
<div v-if="form.status === 0">正常</div>
<div v-else-if="form.status === 1">失败</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="8">
<el-form-item label="消耗时间:">{{ form.costTime }}毫秒</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作时间:">{{ parseTime(form.operTime) }}</el-form-item>
</el-col>
<el-col :span="24">
@@ -203,6 +220,7 @@ const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
operIp: undefined,
title: undefined,
operName: undefined,
businessType: undefined,

View File

@@ -3,7 +3,7 @@
<el-row>
<el-col :span="12" class="card-box">
<el-card>
<template #header><span>CPU</span></template>
<template #header><Cpu style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">CPU</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
@@ -37,7 +37,7 @@
<el-col :span="12" class="card-box">
<el-card>
<template #header><span>内存</span></template>
<template #header><Tickets style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>
@@ -76,7 +76,7 @@
<el-col :span="24" class="card-box">
<el-card>
<template #header><span>服务器信息</span></template>
<template #header><Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">服务器信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<tbody>
@@ -100,7 +100,7 @@
<el-col :span="24" class="card-box">
<el-card>
<template #header><span>Java虚拟机信息</span></template>
<template #header><CoffeeCup style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">Java虚拟机信息</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;table-layout:fixed;">
<tbody>
@@ -136,7 +136,7 @@
<el-col :span="24" class="card-box">
<el-card>
<template #header><span>磁盘状态</span></template>
<template #header><MessageBox style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">磁盘状态</span></template>
<div class="el-table el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<thead>

View File

@@ -70,7 +70,7 @@
</el-form>
<!-- 底部 -->
<div class="el-register-footer">
<span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span>
<span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
</div>
</div>
</template>

View File

@@ -101,7 +101,7 @@
<el-table-column label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column label="参数键值" align="center" prop="configValue" />
<el-table-column label="参数键值" align="center" prop="configValue" :show-overflow-tooltip="true" />
<el-table-column label="系统内置" align="center" prop="configType">
<template #default="scope">
<dict-tag :options="sys_yes_no" :value="scope.row.configType" />

View File

@@ -91,8 +91,8 @@
<el-table-column label="字典编码" align="center" prop="dictCode" />
<el-table-column label="字典标签" align="center" prop="dictLabel">
<template #default="scope">
<span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{ scope.row.dictLabel }}</span>
<el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{ scope.row.dictLabel }}</el-tag>
<span v-if="(scope.row.listClass == '' || scope.row.listClass == 'default') && (scope.row.cssClass == '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
<el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag>
</template>
</el-table-column>
<el-table-column label="字典键值" align="center" prop="dictValue" />
@@ -210,8 +210,8 @@ const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
dictName: undefined,
dictType: undefined,
dictLabel: undefined,
status: undefined
},
rules: {
@@ -279,7 +279,7 @@ function handleClose() {
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
queryParams.value.dictType = defaultDictType;
queryParams.value.dictType = defaultDictType.value;
handleQuery();
}
/** 新增按钮操作 */

View File

@@ -113,12 +113,10 @@
<el-popover
placement="bottom-start"
:width="540"
v-model:visible="showChooseIcon"
trigger="click"
@show="showSelectIcon"
>
<template #reference>
<el-input v-model="form.icon" placeholder="点击选择图标" @blur="showSelectIcon" v-click-outside="hideSelectIcon" readonly>
<el-input v-model="form.icon" placeholder="点击选择图标" @blur="showSelectIcon" readonly>
<template #prefix>
<svg-icon
v-if="form.icon"
@@ -130,7 +128,7 @@
</template>
</el-input>
</template>
<icon-select ref="iconSelectRef" @selected="selected" />
<icon-select ref="iconSelectRef" @selected="selected" :active-icon="form.icon" />
</el-popover>
</el-form-item>
</el-col>
@@ -246,7 +244,7 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.menuType != 'F'">
<el-col :span="12">
<el-form-item>
<template #label>
<span>
@@ -281,7 +279,6 @@
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from "@/api/system/menu";
import SvgIcon from "@/components/SvgIcon";
import IconSelect from "@/components/IconSelect";
import { ClickOutside as vClickOutside } from 'element-plus'
const { proxy } = getCurrentInstance();
const { sys_show_hide, sys_normal_disable } = proxy.useDict("sys_show_hide", "sys_normal_disable");
@@ -294,7 +291,6 @@ const title = ref("");
const menuOptions = ref([]);
const isExpandAll = ref(false);
const refreshTable = ref(true);
const showChooseIcon = ref(false);
const iconSelectRef = ref(null);
const data = reactive({
@@ -353,20 +349,10 @@ function reset() {
/** 展示下拉图标 */
function showSelectIcon() {
iconSelectRef.value.reset();
showChooseIcon.value = true;
}
/** 选择图标 */
function selected(name) {
form.value.icon = name;
showChooseIcon.value = false;
}
/** 图标外层点击隐藏下拉列表 */
function hideSelectIcon(event) {
var elem = event.relatedTarget || event.srcElement || event.target || event.currentTarget;
var className = elem.className;
if (className !== "el-input__inner") {
showChooseIcon.value = false;
}
}
/** 搜索按钮操作 */
function handleQuery() {

View File

@@ -143,12 +143,7 @@
</el-col>
<el-col :span="24">
<el-form-item label="内容">
<el-input
:rows="6"
type="textarea"
placeholder="请输入内容"
v-model="form.noticeContent"
/>
<editor v-model="form.noticeContent" :min-height="192"/>
</el-form-item>
</el-col>
</el-row>

View File

@@ -438,7 +438,7 @@ function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
queryParams.value.deptId = undefined;
proxy.$refs.tree.setCurrentKey(null);
proxy.$refs.deptTreeRef.setCurrentKey(null);
handleQuery();
};
/** 删除按钮操作 */

View File

@@ -10,7 +10,7 @@
</template>
<div>
<div class="text-center">
<userAvatar :user="state.user" />
<userAvatar />
</div>
<ul class="list-group list-group-striped">
<li class="list-group-item">

View File

@@ -73,13 +73,14 @@ const title = ref("修改头像");
//图片裁剪数据
const options = reactive({
img: userStore.avatar, // 裁剪图片的地址
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 200, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
outputType: "png", // 默认生成截图为PNG格式
previews: {} //预览数据
img: userStore.avatar, // 裁剪图片的地址
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 200, // 默认生成截图框宽度
autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
outputType: "png", // 默认生成截图为PNG格式
filename: 'avatar', // 文件名称
previews: {} //预览数据
});
/** 编辑头像 */
@@ -114,6 +115,7 @@ function beforeUpload(file) {
reader.readAsDataURL(file);
reader.onload = () => {
options.img = reader.result;
options.filename = file.name;
};
}
}
@@ -121,7 +123,7 @@ function beforeUpload(file) {
function uploadImg() {
proxy.$refs.cropper.getCropBlob(data => {
let formData = new FormData();
formData.append("avatarfile", data);
formData.append("avatarfile", data, options.filename);
uploadAvatar(formData).then(response => {
open.value = false;
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;

View File

@@ -1,16 +1,16 @@
<template>
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
<el-form ref="userRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="user.nickName" maxlength="30" />
<el-input v-model="form.nickName" maxlength="30" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="user.phonenumber" maxlength="11" />
<el-input v-model="form.phonenumber" maxlength="11" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="user.email" maxlength="50" />
<el-input v-model="form.email" maxlength="50" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="user.sex">
<el-radio-group v-model="form.sex">
<el-radio label="0"></el-radio>
<el-radio label="1"></el-radio>
</el-radio-group>
@@ -33,6 +33,7 @@ const props = defineProps({
const { proxy } = getCurrentInstance();
const form = ref({});
const rules = ref({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
@@ -43,14 +44,24 @@ const rules = ref({
function submit() {
proxy.$refs.userRef.validate(valid => {
if (valid) {
updateUserProfile(props.user).then(response => {
updateUserProfile(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
props.user.phonenumber = form.value.phonenumber;
props.user.email = form.value.email;
});
}
});
};
/** 关闭按钮 */
function close() {
proxy.$tab.closePage();
};
// 回显当前登录用户信息
watch(() => props.user, user => {
if (user) {
form.value = { nickName: user.nickName, phonenumber: user.phonenumber, email: user.email, sex: user.sex };
}
},{ immediate: true });
</script>

View File

@@ -12,6 +12,16 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="tplWebType">
<template #label>前端类型</template>
<el-select v-model="info.tplWebType">
<el-option label="Vue2 Element UI 模版" value="element-ui" />
<el-option label="Vue3 Element Plus 模版" value="element-plus" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="packageName">
<template #label>
@@ -60,6 +70,19 @@
</el-form-item>
</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="info.genType" label="0">zip压缩包</el-radio>
<el-radio v-model="info.genType" label="1">自定义路径</el-radio>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<template #label>
@@ -77,19 +100,6 @@
</el-form-item>
</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="info.genType" label="0">zip压缩包</el-radio>
<el-radio v-model="info.genType" label="1">自定义路径</el-radio>
</el-form-item>
</el-col>
<el-col :span="24" v-if="info.genType == '1'">
<el-form-item prop="genPath">
<template #label>
@@ -277,5 +287,11 @@ watch(() => props.info.subTableName, val => {
setSubTableColumns(val);
});
watch(() => props.info.tplWebType, val => {
if (val === '') {
props.info.tplWebType = "element-plus";
}
});
getMenuTreeselect();
</script>

View File

@@ -1,5 +1,5 @@
import setupExtend from 'vite-plugin-vue-setup-extend'
import setupExtend from 'unplugin-vue-setup-extend-plus/vite'
export default function createSetupExtend() {
return setupExtend()
return setupExtend({})
}