Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168ad6a3b6 | ||
|
|
d2b4332589 | ||
|
|
b086e172ae | ||
|
|
e7d8010390 | ||
|
|
b51b7cfa04 | ||
|
|
199c9951f5 | ||
|
|
b8019c9f8e | ||
|
|
ccc452eb6b | ||
|
|
abe8095320 | ||
|
|
215fab2ea6 | ||
|
|
0547a82b71 | ||
|
|
91527eb654 | ||
|
|
5b2b495da5 | ||
|
|
f1be984efb | ||
|
|
03dd370d1b | ||
|
|
d231e28847 | ||
|
|
4ac33ed630 | ||
|
|
65b97a9018 | ||
|
|
4da85f6491 | ||
|
|
f237f27861 | ||
|
|
5723e17eef | ||
|
|
b75fd760e8 | ||
|
|
40037b8c3b | ||
|
|
f5b8eba57a | ||
|
|
37c94666fb | ||
|
|
dbe3df5edd | ||
|
|
1428299f55 | ||
|
|
ebe0df0d37 | ||
|
|
19348408d3 | ||
|
|
9ee89f7085 | ||
|
|
060adcbdc1 | ||
|
|
8eee596292 | ||
|
|
dae8c5016c | ||
|
|
c88c3d6c69 | ||
|
|
2d0fc59fc7 | ||
|
|
2f8a257efd | ||
|
|
7de94e2ea3 | ||
|
|
2aa0b4e521 | ||
|
|
a4c4653802 | ||
|
|
06d52deb60 | ||
|
|
f2a0f69465 | ||
|
|
589151d1ed | ||
|
|
2dbc91653a | ||
|
|
ff14fa5e4e | ||
|
|
c2005614bc | ||
|
|
42c1ea3c89 | ||
|
|
4b038efabf | ||
|
|
ea552797b5 | ||
|
|
d9ecdc9a53 | ||
|
|
11889edf70 | ||
|
|
93fe80dc68 | ||
|
|
537fc97695 | ||
|
|
ca5ae82f9e | ||
|
|
094e300939 | ||
|
|
3393757b8c | ||
|
|
33e2eb7642 | ||
|
|
ec7183d094 | ||
|
|
865100e487 | ||
|
|
ef417f3e90 | ||
|
|
31cffcb9d4 | ||
|
|
7701ec465c | ||
|
|
8b97a0c09b | ||
|
|
5382a52d22 | ||
|
|
c89ba258bc | ||
|
|
7644a87718 | ||
|
|
4db1eedc6a | ||
|
|
e212a0ab75 | ||
|
|
535c5bd814 | ||
|
|
497f98a2a8 | ||
|
|
6061c3548f | ||
|
|
f8c4968b33 | ||
|
|
e986a4c537 | ||
|
|
3e029957af | ||
|
|
d981cac3f3 | ||
|
|
341728774c | ||
|
|
e82ee73704 | ||
|
|
2a59dbfbb3 | ||
|
|
341e3be3a9 | ||
|
|
7507a187aa | ||
|
|
d02ba57ffb | ||
|
|
f476e35522 | ||
|
|
1c2da6dfb1 | ||
|
|
bbb45eb72e | ||
|
|
5d8172a117 | ||
|
|
c01ac5ccac | ||
|
|
0780d9659b | ||
|
|
c227751acb | ||
|
|
30f52f9946 | ||
|
|
4805cc9ea5 | ||
|
|
1abf0641b0 | ||
|
|
e3d4018c14 | ||
|
|
cae65bb74b | ||
|
|
6666ce5526 | ||
|
|
49d0fc0bdc | ||
|
|
84b3565732 | ||
|
|
ea4a870302 | ||
|
|
6a10846b6d | ||
|
|
69c189a0c9 | ||
|
|
1270e6e3c9 |
@@ -1,21 +1,20 @@
|
||||
<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.7</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.0</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.7-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.0-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>
|
||||
|
||||
## 平台简介
|
||||
|
||||
* 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
|
||||
* 配套后端代码仓库地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 或 [RuoYi-Vue-fast](https://github.com/yangzongzhuan/RuoYi-Vue-fast) 版本。
|
||||
* 配套后端代码仓库地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 或 [RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast) 版本。
|
||||
* 前端技术栈([Vue2](https://cn.vuejs.org) + [Element](https://github.com/ElemeFE/element) + [Vue CLI](https://cli.vuejs.org/zh)),请移步[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue/tree/master/ruoyi-ui)。
|
||||
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)
|
||||
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)
|
||||
|
||||
## 前端运行
|
||||
|
||||
@@ -106,4 +105,4 @@ yarn dev
|
||||
|
||||
## 若依前后端分离交流群
|
||||
|
||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) 点击按钮入群。
|
||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) 点击按钮入群。
|
||||
36
package.json
36
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ruoyi",
|
||||
"version": "3.8.7",
|
||||
"version": "3.9.0",
|
||||
"description": "若依管理系统",
|
||||
"author": "若依",
|
||||
"license": "MIT",
|
||||
@@ -18,28 +18,34 @@
|
||||
"dependencies": {
|
||||
"@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.3",
|
||||
"element-plus": "2.4.3",
|
||||
"@vueuse/core": "13.3.0",
|
||||
"axios": "1.9.0",
|
||||
"clipboard": "2.0.11",
|
||||
"echarts": "5.6.0",
|
||||
"element-plus": "2.10.7",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.6.2",
|
||||
"js-beautify": "1.14.11",
|
||||
"js-cookie": "3.0.5",
|
||||
"jsencrypt": "3.3.2",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "2.1.7",
|
||||
"vue": "3.3.9",
|
||||
"pinia": "3.0.2",
|
||||
"splitpanes": "4.0.4",
|
||||
"vue": "3.5.16",
|
||||
"vue-cropper": "1.1.1",
|
||||
"vue-router": "4.2.5"
|
||||
"vue-router": "4.5.1",
|
||||
"vuedraggable": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"@vitejs/plugin-vue": "5.2.4",
|
||||
"sass-embedded": "1.89.1",
|
||||
"unplugin-auto-import": "0.18.6",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.1",
|
||||
"vite": "6.3.5",
|
||||
"vite-plugin-compression": "0.5.1",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.0"
|
||||
"vite-plugin-svg-icons": "2.0.1"
|
||||
},
|
||||
"overrides": {
|
||||
"quill": "2.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ export function updateUserPwd(oldPassword, newPassword) {
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
params: data
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,6 +43,15 @@ export function importTable(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 创建表
|
||||
export function createTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/createTable',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 预览生成代码
|
||||
export function previewTable(tableId) {
|
||||
return request({
|
||||
|
||||
1
src/assets/icons/svg/enter.svg
Normal file
1
src/assets/icons/svg/enter.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746590936918" class="icon" viewBox="0 0 1194 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5378" xmlns:xlink="http://www.w3.org/1999/xlink" width="233.203125" height="200"><path d="M1151.9144 325.11999969V89.12a57.04000031 57.04000031 0 0 0-28.8-49.44 58.15999969 58.15999969 0 0 0-57.76000031 0 57.04000031 57.04000031 0 0 0-28.8 49.44v235.99999969c0.24 84.31999969-33.6 152.56000031-94.08 212.00000062-60.07999969 59.83999969-141.84 80.64-227.04 80.4H225.91440031L388.07439969 457.11999969a56.80000031 56.80000031 0 0 0 12.40000031-62.16 57.76000031 57.76000031 0 0 0-94.00000031-18.63999938L48.8744 631.20000031a56.88 56.88 0 0 0 0 80.79999938l264.96 262.56a58.08 58.08 0 0 0 96.55999969-25.59999938 56.80000031 56.80000031 0 0 0-14.95999969-55.2L232.07439969 731.67999969h483.44000062c116.56000031 0 226.15999969-32.08000031 308.64-113.76 82.15999969-80.80000031 128.23999969-178.15999969 127.83999938-292.87999969" p-id="5379"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/icons/svg/moon.svg
Normal file
1
src/assets/icons/svg/moon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733303018722" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1447" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M368.832 67.2c51.328-16.384 89.216 34.112 75.712 76.416a346.816 346.816 0 0 0 435.84 435.84c42.304-13.44 92.8 24.384 76.48 75.712A467.968 467.968 0 1 1 368.832 67.2z m-35.776 122.688a368.832 368.832 0 1 0 501.056 501.056 445.952 445.952 0 0 1-501.056-501.056z" p-id="1448"></path></svg>
|
||||
|
After Width: | Height: | Size: 619 B |
1
src/assets/icons/svg/more-up.svg
Normal file
1
src/assets/icons/svg/more-up.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746760911144" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12537" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M395.21211 182.914448c0 62.669318 49.541323 113.472378 110.642936 113.472378 61.093427 0 110.652146-50.80306 110.65214601-113.472378 0-62.685691-49.559742-113.487727-110.65214601-113.487727C444.75241 69.426721 395.21211 120.22978 395.21211 182.914448zM395.21211 523.34693101c0 62.668295 49.541323 113.487727 110.642936 113.48772699 61.093427 0 110.652146-50.820456 110.652146-113.487727 0-62.669318-49.559742-113.472378-110.652146-113.472378C444.75241 409.874553 395.21211 460.67761301 395.21211 523.34693101zM395.21211 841.084529c0 62.686714 49.541323 113.488751 110.642936 113.488751 61.093427 0 110.652146-50.80203599 110.65214601-113.488751 0-62.669318-49.559742-113.471354-110.65214601-113.471354C444.75241 727.614198 395.21211 778.416234 395.21211 841.084529z" p-id="12538"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/icons/svg/sunny.svg
Normal file
1
src/assets/icons/svg/sunny.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733303115132" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12397" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 890.432c18.432 0 33.408 14.976 33.408 33.408v66.752a33.408 33.408 0 0 1-66.816 0v-66.752c0-18.432 14.976-33.408 33.408-33.408z m-267.52-110.848a33.408 33.408 0 0 1 0 47.232l-47.296 47.232a33.408 33.408 0 0 1-47.232-47.232l47.232-47.232a33.408 33.408 0 0 1 47.232 0z m582.336 0l47.232 47.232a33.408 33.408 0 0 1-47.232 47.232l-47.232-47.232a33.408 33.408 0 1 1 47.232-47.232zM512 200.32a311.68 311.68 0 1 1 0 623.296 311.68 311.68 0 0 1 0-623.36z m0 66.752a244.864 244.864 0 1 0 0 489.728 244.864 244.864 0 0 0 0-489.728zM100.16 478.592a33.408 33.408 0 1 1 0 66.816H33.408a33.408 33.408 0 0 1 0-66.816h66.752z m890.432 0a33.408 33.408 0 0 1 0 66.816h-66.752a33.408 33.408 0 1 1 0-66.816h66.752zM197.184 149.952l47.232 47.232a33.408 33.408 0 1 1-47.232 47.232l-47.232-47.232a33.408 33.408 0 0 1 47.232-47.232z m676.864 0a33.408 33.408 0 0 1 0 47.232l-47.232 47.232a33.408 33.408 0 1 1-47.232-47.232l47.232-47.232a33.408 33.408 0 0 1 47.232 0zM512 0c18.432 0 33.408 14.976 33.408 33.408v66.752a33.408 33.408 0 1 1-66.816 0V33.408C478.592 14.976 493.568 0 512 0z" p-id="12398"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,4 +1,4 @@
|
||||
@import './variables.module.scss';
|
||||
@use './variables.module.scss' as *;
|
||||
|
||||
@mixin colorBtn($color) {
|
||||
background: $color;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
@import './variables.module.scss';
|
||||
@import './mixin.scss';
|
||||
@import './transition.scss';
|
||||
@import './element-ui.scss';
|
||||
@import './sidebar.scss';
|
||||
@import './btn.scss';
|
||||
@import './ruoyi.scss';
|
||||
@use './mixin.scss';
|
||||
@use './transition.scss';
|
||||
@use './element-ui.scss';
|
||||
@use './sidebar.scss';
|
||||
@use './btn.scss';
|
||||
@use './ruoyi.scss';
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
@@ -131,10 +130,6 @@ aside {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
@@ -1,200 +1,212 @@
|
||||
/**
|
||||
/**
|
||||
* 通用css样式布局处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
/** 基础通用 **/
|
||||
/** 基础通用 **/
|
||||
.pt5 {
|
||||
padding-top: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.pr5 {
|
||||
padding-right: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.pb5 {
|
||||
padding-bottom: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.mr5 {
|
||||
margin-right: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.mb5 {
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.mb10 {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.mt20 {
|
||||
margin-top: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mr20 {
|
||||
margin-right: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.mb20 {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.el-form--inline {
|
||||
.el-form-item {
|
||||
.el-input, .el-cascader, .el-select, .el-autocomplete {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-form .el-form-item__label {
|
||||
font-weight: 700;
|
||||
font-weight: 700;
|
||||
}
|
||||
.el-dialog:not(.is-fullscreen) {
|
||||
margin-top: 6vh !important;
|
||||
margin-top: 6vh !important;
|
||||
}
|
||||
|
||||
.el-dialog.scrollbar .el-dialog__body {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 70vh;
|
||||
padding: 10px 20px 0;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 70vh;
|
||||
padding: 10px 20px 0;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
|
||||
th {
|
||||
word-break: break-word;
|
||||
background-color: #f8f8f9 !important;
|
||||
color: #515a6e;
|
||||
height: 40px !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.el-table__body-wrapper {
|
||||
.el-button [class*="el-icon-"] + span {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
|
||||
th {
|
||||
word-break: break-word;
|
||||
background-color: #f8f8f9 !important;
|
||||
color: #515a6e;
|
||||
height: 40px !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.el-table__body-wrapper {
|
||||
.el-button [class*="el-icon-"] + span {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单布局 **/
|
||||
.form-header {
|
||||
font-size:15px;
|
||||
color:#6379bb;
|
||||
border-bottom:1px solid #ddd;
|
||||
margin:8px 10px 25px 10px;
|
||||
padding-bottom:5px
|
||||
font-size:15px;
|
||||
color:#6379bb;
|
||||
border-bottom:1px solid #ddd;
|
||||
margin:8px 10px 25px 10px;
|
||||
padding-bottom:5px
|
||||
}
|
||||
|
||||
/** 表格布局 **/
|
||||
.pagination-container {
|
||||
position: relative;
|
||||
height: 25px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 15px;
|
||||
padding: 10px 20px !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* 弹窗中的分页器 */
|
||||
.el-dialog .pagination-container {
|
||||
position: static !important;
|
||||
position: static !important;
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0 !important;
|
||||
|
||||
.el-pagination {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.pagination-container {
|
||||
.el-pagination {
|
||||
> .el-pagination__jump {
|
||||
display: none !important;
|
||||
}
|
||||
> .el-pagination__sizes {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* tree border */
|
||||
.tree-border {
|
||||
margin-top: 5px;
|
||||
border: 1px solid #e5e6e7;
|
||||
background: #FFFFFF none;
|
||||
border-radius:4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination {
|
||||
right: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@media ( max-width : 768px) {
|
||||
.pagination-container .el-pagination > .el-pagination__jump {
|
||||
display: none !important;
|
||||
}
|
||||
.pagination-container .el-pagination > .el-pagination__sizes {
|
||||
display: none !important;
|
||||
}
|
||||
margin-top: 5px;
|
||||
border: 1px solid var(--el-border-color-light, #e5e6e7);
|
||||
background: var(--el-bg-color, #FFFFFF) none;
|
||||
border-radius:4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-table .fixed-width .el-button--small {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
width: inherit;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
/** 表格更多操作下拉样式 */
|
||||
.el-table .el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.el-table .el-dropdown, .el-icon-arrow-down {
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-tree-node__content > .el-checkbox {
|
||||
margin-right: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.list-group-striped > .list-group-item {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-radius: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-radius: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
padding-left: 0px;
|
||||
list-style: none;
|
||||
padding-left: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
border-top: 1px solid #e7eaec;
|
||||
margin-bottom: -1px;
|
||||
padding: 11px 0px;
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
border-top: 1px solid #e7eaec;
|
||||
margin-bottom: -1px;
|
||||
padding: 11px 0px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right !important;
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.el-card__header {
|
||||
padding: 14px 15px 7px !important;
|
||||
min-height: 40px;
|
||||
padding: 14px 15px 7px !important;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
padding: 15px 20px 20px 20px !important;
|
||||
padding: 15px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
.card-box {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* button color */
|
||||
@@ -220,62 +232,67 @@
|
||||
|
||||
/* text color */
|
||||
.text-navy {
|
||||
color: #1ab394;
|
||||
color: #1ab394;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #1c84c6;
|
||||
color: #1c84c6;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: #23c6c8;
|
||||
color: #23c6c8;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f8ac59;
|
||||
color: #f8ac59;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #ed5565;
|
||||
color: #ed5565;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #888888;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
/* image */
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.img-lg {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar-upload-preview {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(50%, -50%);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(50%, -50%);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 拖拽列样式 */
|
||||
.sortable-ghost{
|
||||
opacity: .8;
|
||||
color: #fff!important;
|
||||
background: #42b983!important;
|
||||
opacity: .8;
|
||||
color: #fff!important;
|
||||
background: #42b983!important;
|
||||
}
|
||||
|
||||
/* 表格右侧工具栏样式 */
|
||||
.top-right-btn {
|
||||
margin-left: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 分割面板样式 */
|
||||
.splitpanes.default-theme .splitpanes__pane {
|
||||
background-color: var(--splitpanes-default-bg) !important;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
@use './variables.module.scss' as vars;
|
||||
|
||||
#app {
|
||||
|
||||
.main-container {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
transition: margin-left .28s;
|
||||
margin-left: $base-sidebar-width;
|
||||
margin-left: vars.$base-sidebar-width;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -12,10 +14,8 @@
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
-webkit-transition: width .28s;
|
||||
transition: width 0.28s;
|
||||
width: $base-sidebar-width !important;
|
||||
background-color: $base-menu-background;
|
||||
width: vars.$base-sidebar-width !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
@@ -25,7 +25,7 @@
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
-webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
|
||||
box-shadow: 2px 0 6px rgba(0,21,41,.35);
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
@@ -89,12 +89,12 @@
|
||||
}
|
||||
|
||||
& .theme-dark .is-active > .el-sub-menu__title {
|
||||
color: $base-menu-color-active !important;
|
||||
color: vars.$base-menu-color-active !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: $base-sidebar-width !important;
|
||||
min-width: vars.$base-sidebar-width !important;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06) !important;
|
||||
@@ -103,10 +103,10 @@
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .theme-dark .el-sub-menu .el-menu-item {
|
||||
background-color: $base-sub-menu-background !important;
|
||||
background-color: vars.$base-sub-menu-background;
|
||||
|
||||
&:hover {
|
||||
background-color: $base-sub-menu-hover !important;
|
||||
background-color: vars.$base-sub-menu-hover !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@
|
||||
}
|
||||
|
||||
.el-menu--collapse .el-menu .el-sub-menu {
|
||||
min-width: $base-sidebar-width !important;
|
||||
min-width: vars.$base-sidebar-width !important;
|
||||
}
|
||||
|
||||
// mobile responsive
|
||||
@@ -180,14 +180,14 @@
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform .28s;
|
||||
width: $base-sidebar-width !important;
|
||||
width: vars.$base-sidebar-width !important;
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-$base-sidebar-width, 0, 0);
|
||||
transform: translate3d(-(vars.$base-sidebar-width), 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-enter-from,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-enter-from,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// base color
|
||||
$blue: #324157;
|
||||
$light-blue: #3A71A8;
|
||||
$light-blue: #333c46;
|
||||
$red: #C03639;
|
||||
$pink: #E65D6E;
|
||||
$green: #30B08F;
|
||||
@@ -8,58 +8,214 @@ $tiffany: #4AB7BD;
|
||||
$yellow: #FEC171;
|
||||
$panGreen: #30B08F;
|
||||
|
||||
// 默认菜单主题风格
|
||||
// 默认主题变量
|
||||
$menuText: #bfcbd9;
|
||||
$menuActiveText: #409eff;
|
||||
$menuBg: #304156;
|
||||
$menuHover: #263445;
|
||||
|
||||
// 浅色主题theme-light
|
||||
$menuLightBg: #ffffff;
|
||||
$menuLightHover: #f0f1f5;
|
||||
$menuLightText: #303133;
|
||||
$menuLightActiveText: #409EFF;
|
||||
|
||||
// 基础变量
|
||||
$base-sidebar-width: 200px;
|
||||
$sideBarWidth: 200px;
|
||||
|
||||
// 菜单暗色变量
|
||||
$base-menu-color: #bfcbd9;
|
||||
$base-menu-color-active: #f4f4f5;
|
||||
$base-menu-background: #304156;
|
||||
$base-logo-title-color: #ffffff;
|
||||
|
||||
$base-menu-light-color: rgba(0, 0, 0, 0.7);
|
||||
$base-menu-light-background: #ffffff;
|
||||
$base-logo-light-title-color: #001529;
|
||||
|
||||
$base-sub-menu-background: #1f2d3d;
|
||||
$base-sub-menu-hover: #001528;
|
||||
|
||||
// 自定义暗色菜单风格
|
||||
/**
|
||||
$base-menu-color:hsla(0,0%,100%,.65);
|
||||
$base-menu-color-active:#fff;
|
||||
$base-menu-background:#001529;
|
||||
$base-logo-title-color: #ffffff;
|
||||
|
||||
$base-menu-light-color:rgba(0,0,0,.70);
|
||||
$base-menu-light-background:#ffffff;
|
||||
$base-logo-light-title-color: #001529;
|
||||
|
||||
$base-sub-menu-background:#000c17;
|
||||
$base-sub-menu-hover:#001528;
|
||||
*/
|
||||
|
||||
// 组件变量
|
||||
$--color-primary: #409EFF;
|
||||
$--color-success: #67C23A;
|
||||
$--color-warning: #E6A23C;
|
||||
$--color-danger: #F56C6C;
|
||||
$--color-info: #909399;
|
||||
|
||||
$base-sidebar-width: 200px;
|
||||
|
||||
// the :export directive is the magic sauce for webpack
|
||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||
:export {
|
||||
menuColor: $base-menu-color;
|
||||
menuLightColor: $base-menu-light-color;
|
||||
menuColorActive: $base-menu-color-active;
|
||||
menuBackground: $base-menu-background;
|
||||
menuLightBackground: $base-menu-light-background;
|
||||
subMenuBackground: $base-sub-menu-background;
|
||||
subMenuHover: $base-sub-menu-hover;
|
||||
sideBarWidth: $base-sidebar-width;
|
||||
logoTitleColor: $base-logo-title-color;
|
||||
logoLightTitleColor: $base-logo-light-title-color;
|
||||
primaryColor: $--color-primary;
|
||||
successColor: $--color-success;
|
||||
dangerColor: $--color-danger;
|
||||
infoColor: $--color-info;
|
||||
warningColor: $--color-warning;
|
||||
menuText: $menuText;
|
||||
menuActiveText: $menuActiveText;
|
||||
menuBg: $menuBg;
|
||||
menuHover: $menuHover;
|
||||
menuLightBg: $menuLightBg;
|
||||
menuLightHover: $menuLightHover;
|
||||
menuLightText: $menuLightText;
|
||||
menuLightActiveText: $menuLightActiveText;
|
||||
sideBarWidth: $sideBarWidth;
|
||||
// 导出基础颜色
|
||||
blue: $blue;
|
||||
lightBlue: $light-blue;
|
||||
red: $red;
|
||||
pink: $pink;
|
||||
green: $green;
|
||||
tiffany: $tiffany;
|
||||
yellow: $yellow;
|
||||
panGreen: $panGreen;
|
||||
// 导出组件颜色
|
||||
colorPrimary: $--color-primary;
|
||||
colorSuccess: $--color-success;
|
||||
colorWarning: $--color-warning;
|
||||
colorDanger: $--color-danger;
|
||||
colorInfo: $--color-info;
|
||||
}
|
||||
|
||||
// CSS变量定义
|
||||
:root {
|
||||
/* 亮色模式变量 */
|
||||
--sidebar-bg: #{$menuBg};
|
||||
--sidebar-text: #{$menuText};
|
||||
--menu-hover: #{$menuHover};
|
||||
|
||||
--navbar-bg: #ffffff;
|
||||
--navbar-text: #303133;
|
||||
|
||||
/* splitpanes default-theme 变量 */
|
||||
--splitpanes-default-bg: #ffffff;
|
||||
|
||||
}
|
||||
|
||||
// 暗黑模式变量
|
||||
html.dark {
|
||||
/* 默认通用 */
|
||||
--el-bg-color: #141414;
|
||||
--el-bg-color-overlay: #1d1e1f;
|
||||
--el-text-color-primary: #ffffff;
|
||||
--el-text-color-regular: #d0d0d0;
|
||||
--el-border-color: #434343;
|
||||
--el-border-color-light: #434343;
|
||||
|
||||
/* 侧边栏 */
|
||||
--sidebar-bg: #141414;
|
||||
--sidebar-text: #ffffff;
|
||||
--menu-hover: #2d2d2d;
|
||||
--menu-active-text: #{$menuActiveText};
|
||||
|
||||
/* 顶部导航栏 */
|
||||
--navbar-bg: #141414;
|
||||
--navbar-text: #ffffff;
|
||||
--navbar-hover: #141414;
|
||||
|
||||
/* 标签栏 */
|
||||
--tags-bg: #141414;
|
||||
--tags-item-bg: #1d1e1f;
|
||||
--tags-item-border: #303030;
|
||||
--tags-item-text: #d0d0d0;
|
||||
--tags-item-hover: #2d2d2d;
|
||||
--tags-close-hover: #64666a;
|
||||
|
||||
/* splitpanes 组件暗黑模式变量 */
|
||||
--splitpanes-bg: #141414;
|
||||
--splitpanes-border: #303030;
|
||||
--splitpanes-splitter-bg: #1d1e1f;
|
||||
--splitpanes-splitter-hover-bg: #2d2d2d;
|
||||
|
||||
/* blockquote 暗黑模式变量 */
|
||||
--blockquote-bg: #1d1e1f;
|
||||
--blockquote-border: #303030;
|
||||
--blockquote-text: #d0d0d0;
|
||||
|
||||
/* Cron 时间表达式 模式变量 */
|
||||
--cron-border: #303030;
|
||||
|
||||
/* splitpanes default-theme 暗黑模式变量 */
|
||||
--splitpanes-default-bg: #141414;
|
||||
|
||||
/* 侧边栏菜单覆盖 */
|
||||
.sidebar-container {
|
||||
.el-menu-item, .menu-title {
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .theme-dark .el-sub-menu .el-menu-item {
|
||||
background-color: var(--el-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 顶部栏栏菜单覆盖 */
|
||||
.el-menu--horizontal {
|
||||
.el-menu-item {
|
||||
&:not(.is-disabled) {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--navbar-hover) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 分割窗格覆盖 */
|
||||
.splitpanes {
|
||||
background-color: var(--splitpanes-bg);
|
||||
|
||||
.splitpanes__pane {
|
||||
background-color: var(--splitpanes-bg);
|
||||
border-color: var(--splitpanes-border);
|
||||
}
|
||||
|
||||
.splitpanes__splitter {
|
||||
background-color: var(--splitpanes-splitter-bg);
|
||||
border-color: var(--splitpanes-border);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--splitpanes-splitter-hover-bg);
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: var(--splitpanes-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格样式覆盖 */
|
||||
.el-table {
|
||||
--el-table-header-bg-color: var(--el-bg-color-overlay) !important;
|
||||
--el-table-header-text-color: var(--el-text-color-regular) !important;
|
||||
--el-table-border-color: var(--el-border-color-light) !important;
|
||||
--el-table-row-hover-bg-color: var(--el-bg-color-overlay) !important;
|
||||
|
||||
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
|
||||
th {
|
||||
background-color: var(--el-bg-color-overlay, #f8f8f9) !important;
|
||||
color: var(--el-text-color-regular, #515a6e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 树组件高亮样式覆盖 */
|
||||
.el-tree {
|
||||
.el-tree-node.is-current > .el-tree-node__content {
|
||||
background-color: var(--el-bg-color-overlay) !important;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-tree-node__content:hover {
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
}
|
||||
}
|
||||
|
||||
/* 下拉菜单样式覆盖 */
|
||||
.el-dropdown-menu__item:not(.is-disabled):focus, .el-dropdown-menu__item:not(.is-disabled):hover{
|
||||
background-color: var(--navbar-hover) !important;
|
||||
}
|
||||
|
||||
/* blockquote样式覆盖 */
|
||||
blockquote {
|
||||
background-color: var(--blockquote-bg) !important;
|
||||
border-left-color: var(--blockquote-border) !important;
|
||||
color: var(--blockquote-text) !important;
|
||||
}
|
||||
|
||||
/* 时间表达式标题样式覆盖 */
|
||||
.popup-result .title {
|
||||
background: var(--cron-border);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
|
||||
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
|
||||
</el-breadcrumb-item>
|
||||
@@ -10,21 +10,53 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const permissionStore = usePermissionStore()
|
||||
const levelList = ref([])
|
||||
|
||||
function getBreadcrumb() {
|
||||
// only show routes with meta.title
|
||||
let matched = route.matched.filter(item => item.meta && item.meta.title);
|
||||
const first = matched[0]
|
||||
// 判断是否为首页
|
||||
if (!isDashboard(first)) {
|
||||
matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)
|
||||
let matched = []
|
||||
const pathNum = findPathNum(route.path)
|
||||
// multi-level menu
|
||||
if (pathNum > 2) {
|
||||
const reg = /\/\w+/gi
|
||||
const pathList = route.path.match(reg).map((item, index) => {
|
||||
if (index !== 0) item = item.slice(1)
|
||||
return item
|
||||
})
|
||||
getMatched(pathList, permissionStore.defaultRoutes, matched)
|
||||
} else {
|
||||
matched = route.matched.filter((item) => item.meta && item.meta.title)
|
||||
}
|
||||
// 判断是否为首页
|
||||
if (!isDashboard(matched[0])) {
|
||||
matched = [{ path: "/index", meta: { title: "首页" } }].concat(matched)
|
||||
}
|
||||
|
||||
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
}
|
||||
function findPathNum(str, char = "/") {
|
||||
let index = str.indexOf(char)
|
||||
let num = 0
|
||||
while (index !== -1) {
|
||||
num++
|
||||
index = str.indexOf(char, index + 1)
|
||||
}
|
||||
return num
|
||||
}
|
||||
function getMatched(pathList, routeList, matched) {
|
||||
let data = routeList.find(item => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0])
|
||||
if (data) {
|
||||
matched.push(data)
|
||||
if (data.children && pathList.length) {
|
||||
pathList.shift()
|
||||
getMatched(pathList, data.children, matched)
|
||||
}
|
||||
}
|
||||
}
|
||||
function isDashboard(route) {
|
||||
const name = route && route.name
|
||||
if (!name) {
|
||||
@@ -48,7 +80,7 @@ watchEffect(() => {
|
||||
}
|
||||
getBreadcrumb()
|
||||
})
|
||||
getBreadcrumb();
|
||||
getBreadcrumb()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
<el-radio v-model='radioValue' :value="1">
|
||||
日,允许的通配符[, - * ? / L W]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
<el-radio v-model='radioValue' :value="2">
|
||||
不指定
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
<el-radio v-model='radioValue' :value="3">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="1" :max="30" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="31" /> 日
|
||||
@@ -21,7 +21,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
<el-radio v-model='radioValue' :value="4">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="1" :max="30" /> 号开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="31 - average01" /> 日执行一次
|
||||
@@ -29,20 +29,20 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="5">
|
||||
<el-radio v-model='radioValue' :value="5">
|
||||
每月
|
||||
<el-input-number v-model='workday' :min="1" :max="31" /> 号最近的那个工作日
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="6">
|
||||
<el-radio v-model='radioValue' :value="6">
|
||||
本月最后一天
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="7">
|
||||
<el-radio v-model='radioValue' :value="7">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 31" :key="item" :label="item" :value="item" />
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
<el-radio v-model='radioValue' :value="1">
|
||||
小时,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
<el-radio v-model='radioValue' :value="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="0" :max="22" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="23" /> 时
|
||||
@@ -15,7 +15,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
<el-radio v-model='radioValue' :value="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="22" /> 时开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="23 - average01" /> 小时执行一次
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
<el-radio v-model='radioValue' :value="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 24" :key="item" :label="item - 1" :value="item - 1" />
|
||||
@@ -77,6 +77,12 @@ const checkboxString = computed(() => {
|
||||
watch(() => props.cron.hour, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (props.cron.min === '*') {
|
||||
emit('update', 'min', '0', 'hour')
|
||||
}
|
||||
if (props.cron.second === '*') {
|
||||
emit('update', 'second', '0', 'hour')
|
||||
}
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
|
||||
@@ -70,42 +70,46 @@
|
||||
<p class="title">时间表达式</p>
|
||||
<table>
|
||||
<thead>
|
||||
<th v-for="item of tabTitles" :key="item">{{item}}</th>
|
||||
<th>Cron 表达式</th>
|
||||
<tr>
|
||||
<th v-for="item of tabTitles" :key="item">{{item}}</th>
|
||||
<th>Cron 表达式</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.second.length < 10">{{crontabValueObj.second}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.second" placement="top"><span>{{crontabValueObj.second}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.min.length < 10">{{crontabValueObj.min}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.min" placement="top"><span>{{crontabValueObj.min}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.hour.length < 10">{{crontabValueObj.hour}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.hour" placement="top"><span>{{crontabValueObj.hour}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.day.length < 10">{{crontabValueObj.day}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.day" placement="top"><span>{{crontabValueObj.day}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.month.length < 10">{{crontabValueObj.month}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.month" placement="top"><span>{{crontabValueObj.month}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.week.length < 10">{{crontabValueObj.week}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.week" placement="top"><span>{{crontabValueObj.week}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.year.length < 10">{{crontabValueObj.year}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.year" placement="top"><span>{{crontabValueObj.year}}</span></el-tooltip>
|
||||
</td>
|
||||
<td class="result">
|
||||
<span v-if="crontabValueString.length < 90">{{crontabValueString}}</span>
|
||||
<el-tooltip v-else :content="crontabValueString" placement="top"><span>{{crontabValueString}}</span></el-tooltip>
|
||||
</td>
|
||||
<tr>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.second.length < 10">{{crontabValueObj.second}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.second" placement="top"><span>{{crontabValueObj.second}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.min.length < 10">{{crontabValueObj.min}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.min" placement="top"><span>{{crontabValueObj.min}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.hour.length < 10">{{crontabValueObj.hour}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.hour" placement="top"><span>{{crontabValueObj.hour}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.day.length < 10">{{crontabValueObj.day}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.day" placement="top"><span>{{crontabValueObj.day}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.month.length < 10">{{crontabValueObj.month}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.month" placement="top"><span>{{crontabValueObj.month}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.week.length < 10">{{crontabValueObj.week}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.week" placement="top"><span>{{crontabValueObj.week}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.year.length < 10">{{crontabValueObj.year}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.year" placement="top"><span>{{crontabValueObj.year}}</span></el-tooltip>
|
||||
</td>
|
||||
<td class="result">
|
||||
<span v-if="crontabValueString.length < 90">{{crontabValueString}}</span>
|
||||
<el-tooltip v-else :content="crontabValueString" placement="top"><span>{{crontabValueString}}</span></el-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -251,7 +255,6 @@ onMounted(() => {
|
||||
.popup-main {
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
<el-radio v-model='radioValue' :value="1">
|
||||
分钟,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
<el-radio v-model='radioValue' :value="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 分钟
|
||||
@@ -15,7 +15,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
<el-radio v-model='radioValue' :value="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="58" /> 分钟开始, 每
|
||||
<el-input-number v-model='average02' :min="1" :max="59 - average01" /> 分钟执行一次
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
<el-radio v-model='radioValue' :value="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
<el-radio v-model='radioValue' :value="1">
|
||||
月,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
<el-radio v-model='radioValue' :value="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="1" :max="11" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="12" /> 月
|
||||
@@ -15,7 +15,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
<el-radio v-model='radioValue' :value="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="1" :max="11" /> 月开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="12 - average01" /> 月月执行一次
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
<el-radio v-model='radioValue' :value="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
||||
<el-option v-for="item in monthList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
|
||||
@@ -26,289 +26,289 @@ watch(() => props.ex, () => expressionChange())
|
||||
// 表达式值变化时,开始去计算结果
|
||||
function expressionChange() {
|
||||
// 计算开始-隐藏结果
|
||||
isShow.value = false;
|
||||
isShow.value = false
|
||||
// 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]
|
||||
let ruleArr = props.ex.split(' ');
|
||||
let ruleArr = props.ex.split(' ')
|
||||
// 用于记录进入循环的次数
|
||||
let nums = 0;
|
||||
let nums = 0
|
||||
// 用于暂时存符号时间规则结果的数组
|
||||
let resultArr = [];
|
||||
let resultArr = []
|
||||
// 获取当前时间精确至[年、月、日、时、分、秒]
|
||||
let nTime = new Date();
|
||||
let nYear = nTime.getFullYear();
|
||||
let nMonth = nTime.getMonth() + 1;
|
||||
let nDay = nTime.getDate();
|
||||
let nHour = nTime.getHours();
|
||||
let nMin = nTime.getMinutes();
|
||||
let nSecond = nTime.getSeconds();
|
||||
let nTime = new Date()
|
||||
let nYear = nTime.getFullYear()
|
||||
let nMonth = nTime.getMonth() + 1
|
||||
let nDay = nTime.getDate()
|
||||
let nHour = nTime.getHours()
|
||||
let nMin = nTime.getMinutes()
|
||||
let nSecond = nTime.getSeconds()
|
||||
// 根据规则获取到近100年可能年数组、月数组等等
|
||||
getSecondArr(ruleArr[0]);
|
||||
getMinArr(ruleArr[1]);
|
||||
getHourArr(ruleArr[2]);
|
||||
getDayArr(ruleArr[3]);
|
||||
getMonthArr(ruleArr[4]);
|
||||
getWeekArr(ruleArr[5]);
|
||||
getYearArr(ruleArr[6], nYear);
|
||||
getSecondArr(ruleArr[0])
|
||||
getMinArr(ruleArr[1])
|
||||
getHourArr(ruleArr[2])
|
||||
getDayArr(ruleArr[3])
|
||||
getMonthArr(ruleArr[4])
|
||||
getWeekArr(ruleArr[5])
|
||||
getYearArr(ruleArr[6], nYear)
|
||||
// 将获取到的数组赋值-方便使用
|
||||
let sDate = dateArr.value[0];
|
||||
let mDate = dateArr.value[1];
|
||||
let hDate = dateArr.value[2];
|
||||
let DDate = dateArr.value[3];
|
||||
let MDate = dateArr.value[4];
|
||||
let YDate = dateArr.value[5];
|
||||
let sDate = dateArr.value[0]
|
||||
let mDate = dateArr.value[1]
|
||||
let hDate = dateArr.value[2]
|
||||
let DDate = dateArr.value[3]
|
||||
let MDate = dateArr.value[4]
|
||||
let YDate = dateArr.value[5]
|
||||
// 获取当前时间在数组中的索引
|
||||
let sIdx = getIndex(sDate, nSecond);
|
||||
let mIdx = getIndex(mDate, nMin);
|
||||
let hIdx = getIndex(hDate, nHour);
|
||||
let DIdx = getIndex(DDate, nDay);
|
||||
let MIdx = getIndex(MDate, nMonth);
|
||||
let YIdx = getIndex(YDate, nYear);
|
||||
let sIdx = getIndex(sDate, nSecond)
|
||||
let mIdx = getIndex(mDate, nMin)
|
||||
let hIdx = getIndex(hDate, nHour)
|
||||
let DIdx = getIndex(DDate, nDay)
|
||||
let MIdx = getIndex(MDate, nMonth)
|
||||
let YIdx = getIndex(YDate, nYear)
|
||||
// 重置月日时分秒的函数(后面用的比较多)
|
||||
const resetSecond = function () {
|
||||
sIdx = 0;
|
||||
sIdx = 0
|
||||
nSecond = sDate[sIdx]
|
||||
}
|
||||
const resetMin = function () {
|
||||
mIdx = 0;
|
||||
mIdx = 0
|
||||
nMin = mDate[mIdx]
|
||||
resetSecond();
|
||||
resetSecond()
|
||||
}
|
||||
const resetHour = function () {
|
||||
hIdx = 0;
|
||||
hIdx = 0
|
||||
nHour = hDate[hIdx]
|
||||
resetMin();
|
||||
resetMin()
|
||||
}
|
||||
const resetDay = function () {
|
||||
DIdx = 0;
|
||||
DIdx = 0
|
||||
nDay = DDate[DIdx]
|
||||
resetHour();
|
||||
resetHour()
|
||||
}
|
||||
const resetMonth = function () {
|
||||
MIdx = 0;
|
||||
MIdx = 0
|
||||
nMonth = MDate[MIdx]
|
||||
resetDay();
|
||||
resetDay()
|
||||
}
|
||||
// 如果当前年份不为数组中当前值
|
||||
if (nYear !== YDate[YIdx]) {
|
||||
resetMonth();
|
||||
resetMonth()
|
||||
}
|
||||
// 如果当前月份不为数组中当前值
|
||||
if (nMonth !== MDate[MIdx]) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
}
|
||||
// 如果当前“日”不为数组中当前值
|
||||
if (nDay !== DDate[DIdx]) {
|
||||
resetHour();
|
||||
resetHour()
|
||||
}
|
||||
// 如果当前“时”不为数组中当前值
|
||||
if (nHour !== hDate[hIdx]) {
|
||||
resetMin();
|
||||
resetMin()
|
||||
}
|
||||
// 如果当前“分”不为数组中当前值
|
||||
if (nMin !== mDate[mIdx]) {
|
||||
resetSecond();
|
||||
resetSecond()
|
||||
}
|
||||
// 循环年份数组
|
||||
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
|
||||
let YY = YDate[Yi];
|
||||
let YY = YDate[Yi]
|
||||
// 如果到达最大值时
|
||||
if (nMonth > MDate[MDate.length - 1]) {
|
||||
resetMonth();
|
||||
continue;
|
||||
resetMonth()
|
||||
continue
|
||||
}
|
||||
// 循环月份数组
|
||||
goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
|
||||
// 赋值、方便后面运算
|
||||
let MM = MDate[Mi];
|
||||
MM = MM < 10 ? '0' + MM : MM;
|
||||
MM = MM < 10 ? '0' + MM : MM
|
||||
// 如果到达最大值时
|
||||
if (nDay > DDate[DDate.length - 1]) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
// 循环日期数组
|
||||
goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
|
||||
// 赋值、方便后面运算
|
||||
let DD = DDate[Di];
|
||||
let thisDD = DD < 10 ? '0' + DD : DD;
|
||||
let DD = DDate[Di]
|
||||
let thisDD = DD < 10 ? '0' + DD : DD
|
||||
// 如果到达最大值时
|
||||
if (nHour > hDate[hDate.length - 1]) {
|
||||
resetHour();
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth;
|
||||
continue goMonth
|
||||
}
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
// 判断日期的合法性,不合法的话也是跳出当前循环
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && dayRule.value !== 'workDay' && dayRule.value !== 'lastWeek' && dayRule.value !== 'lastDay') {
|
||||
resetDay();
|
||||
continue goMonth;
|
||||
resetDay()
|
||||
continue goMonth
|
||||
}
|
||||
// 如果日期规则中有值时
|
||||
if (dayRule.value === 'lastDay') {
|
||||
// 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
DD--
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
}
|
||||
}
|
||||
} else if (dayRule.value === 'workDay') {
|
||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
DD--
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
}
|
||||
}
|
||||
// 获取达到条件的日期是星期X
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
|
||||
// 当星期日时
|
||||
if (thisWeek === 1) {
|
||||
// 先找下一个日,并判断是否为月底
|
||||
DD++;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
DD++
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
// 判断下一日已经不是合法日期
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD -= 3;
|
||||
DD -= 3
|
||||
}
|
||||
} else if (thisWeek === 7) {
|
||||
// 当星期6时只需判断不是1号就可进行操作
|
||||
if (dayRuleSup.value !== 1) {
|
||||
DD--;
|
||||
DD--
|
||||
} else {
|
||||
DD += 2;
|
||||
DD += 2
|
||||
}
|
||||
}
|
||||
} else if (dayRule.value === 'weekDay') {
|
||||
// 如果指定了是星期几
|
||||
// 获取当前日期是属于星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
|
||||
// 校验当前星期是否在星期池(dayRuleSup)中
|
||||
if (dayRuleSup.value.indexOf(thisWeek) < 0) {
|
||||
// 如果到达最大值时
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth;
|
||||
continue goMonth
|
||||
}
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
} else if (dayRule.value === 'assWeek') {
|
||||
// 如果指定了是第几周的星期几
|
||||
// 获取每月1号是属于星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
|
||||
if (dayRuleSup.value[1] >= thisWeek) {
|
||||
DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1;
|
||||
DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1
|
||||
} else {
|
||||
DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1;
|
||||
DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1
|
||||
}
|
||||
} else if (dayRule.value === 'lastWeek') {
|
||||
// 如果指定了每月最后一个星期几
|
||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
DD--
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
}
|
||||
}
|
||||
// 获取月末最后一天是星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
|
||||
// 找到要求中最近的那个星期几
|
||||
if (dayRuleSup.value < thisWeek) {
|
||||
DD -= thisWeek - dayRuleSup.value;
|
||||
DD -= thisWeek - dayRuleSup.value
|
||||
} else if (dayRuleSup.value > thisWeek) {
|
||||
DD -= 7 - (dayRuleSup.value - thisWeek)
|
||||
}
|
||||
}
|
||||
// 判断时间值是否小于10置换成“05”这种格式
|
||||
DD = DD < 10 ? '0' + DD : DD;
|
||||
DD = DD < 10 ? '0' + DD : DD
|
||||
// 循环“时”数组
|
||||
goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
|
||||
let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
|
||||
// 如果到达最大值时
|
||||
if (nMin > mDate[mDate.length - 1]) {
|
||||
resetMin();
|
||||
resetMin()
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour();
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth;
|
||||
continue goMonth
|
||||
}
|
||||
continue goDay;
|
||||
continue goDay
|
||||
}
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
// 循环"分"数组
|
||||
goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
|
||||
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];
|
||||
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi]
|
||||
// 如果到达最大值时
|
||||
if (nSecond > sDate[sDate.length - 1]) {
|
||||
resetSecond();
|
||||
resetSecond()
|
||||
if (mi === mDate.length - 1) {
|
||||
resetMin();
|
||||
resetMin()
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour();
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth;
|
||||
continue goMonth
|
||||
}
|
||||
continue goDay;
|
||||
continue goDay
|
||||
}
|
||||
continue goHour;
|
||||
continue goHour
|
||||
}
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
// 循环"秒"数组
|
||||
goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
|
||||
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];
|
||||
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si]
|
||||
// 添加当前时间(时间合法性在日期循环时已经判断)
|
||||
if (MM !== '00' && DD !== '00') {
|
||||
resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
|
||||
nums++;
|
||||
nums++
|
||||
}
|
||||
// 如果条数满了就退出循环
|
||||
if (nums === 5) break goYear;
|
||||
if (nums === 5) break goYear
|
||||
// 如果到达最大值时
|
||||
if (si === sDate.length - 1) {
|
||||
resetSecond();
|
||||
resetSecond()
|
||||
if (mi === mDate.length - 1) {
|
||||
resetMin();
|
||||
resetMin()
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour();
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth;
|
||||
continue goMonth
|
||||
}
|
||||
continue goDay;
|
||||
continue goDay
|
||||
}
|
||||
continue goHour;
|
||||
continue goHour
|
||||
}
|
||||
continue goMin;
|
||||
continue goMin
|
||||
}
|
||||
} //goSecond
|
||||
} //goMin
|
||||
@@ -318,31 +318,31 @@ function expressionChange() {
|
||||
}
|
||||
// 判断100年内的结果条数
|
||||
if (resultArr.length === 0) {
|
||||
resultList.value = ['没有达到条件的结果!'];
|
||||
resultList.value = ['没有达到条件的结果!']
|
||||
} else {
|
||||
resultList.value = resultArr;
|
||||
resultList.value = resultArr
|
||||
if (resultArr.length !== 5) {
|
||||
resultList.value.push('最近100年内只有上面' + resultArr.length + '条结果!')
|
||||
}
|
||||
}
|
||||
// 计算完成-显示结果
|
||||
isShow.value = true;
|
||||
isShow.value = true
|
||||
}
|
||||
// 用于计算某位数字在数组中的索引
|
||||
function getIndex(arr, value) {
|
||||
if (value <= arr[0] || value > arr[arr.length - 1]) {
|
||||
return 0;
|
||||
return 0
|
||||
} else {
|
||||
for (let i = 0; i < arr.length - 1; i++) {
|
||||
if (value > arr[i] && value <= arr[i + 1]) {
|
||||
return i + 1;
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"年"数组
|
||||
function getYearArr(rule, year) {
|
||||
dateArr.value[5] = getOrderArr(year, year + 100);
|
||||
dateArr.value[5] = getOrderArr(year, year + 100)
|
||||
if (rule !== undefined) {
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[5] = getCycleArr(rule, year + 100, false)
|
||||
@@ -355,7 +355,7 @@ function getYearArr(rule, year) {
|
||||
}
|
||||
// 获取"月"数组
|
||||
function getMonthArr(rule) {
|
||||
dateArr.value[4] = getOrderArr(1, 12);
|
||||
dateArr.value[4] = getOrderArr(1, 12)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[4] = getCycleArr(rule, 12, false)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
@@ -369,58 +369,58 @@ function getWeekArr(rule) {
|
||||
// 只有当日期规则的两个值均为“”时则表达日期是有选项的
|
||||
if (dayRule.value === '' && dayRuleSup.value === '') {
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dayRule.value = 'weekDay';
|
||||
dayRule.value = 'weekDay'
|
||||
dayRuleSup.value = getCycleArr(rule, 7, false)
|
||||
} else if (rule.indexOf('#') >= 0) {
|
||||
dayRule.value = 'assWeek';
|
||||
let matchRule = rule.match(/[0-9]{1}/g);
|
||||
dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])];
|
||||
dateArr.value[3] = [1];
|
||||
dayRule.value = 'assWeek'
|
||||
let matchRule = rule.match(/[0-9]{1}/g)
|
||||
dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])]
|
||||
dateArr.value[3] = [1]
|
||||
if (dayRuleSup.value[1] === 7) {
|
||||
dayRuleSup.value[1] = 0;
|
||||
dayRuleSup.value[1] = 0
|
||||
}
|
||||
} else if (rule.indexOf('L') >= 0) {
|
||||
dayRule.value = 'lastWeek';
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]);
|
||||
dateArr.value[3] = [31];
|
||||
dayRule.value = 'lastWeek'
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0])
|
||||
dateArr.value[3] = [31]
|
||||
if (dayRuleSup.value === 7) {
|
||||
dayRuleSup.value = 0;
|
||||
dayRuleSup.value = 0
|
||||
}
|
||||
} else if (rule !== '*' && rule !== '?') {
|
||||
dayRule.value = 'weekDay';
|
||||
dayRule.value = 'weekDay'
|
||||
dayRuleSup.value = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"日"数组-少量为日期规则
|
||||
function getDayArr(rule) {
|
||||
dateArr.value[3] = getOrderArr(1, 31);
|
||||
dayRule.value = '';
|
||||
dayRuleSup.value = '';
|
||||
dateArr.value[3] = getOrderArr(1, 31)
|
||||
dayRule.value = ''
|
||||
dayRuleSup.value = ''
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[3] = getCycleArr(rule, 31, false)
|
||||
dayRuleSup.value = 'null';
|
||||
dayRuleSup.value = 'null'
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[3] = getAverageArr(rule, 31)
|
||||
dayRuleSup.value = 'null';
|
||||
dayRuleSup.value = 'null'
|
||||
} else if (rule.indexOf('W') >= 0) {
|
||||
dayRule.value = 'workDay';
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]);
|
||||
dateArr.value[3] = [dayRuleSup.value];
|
||||
dayRule.value = 'workDay'
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0])
|
||||
dateArr.value[3] = [dayRuleSup.value]
|
||||
} else if (rule.indexOf('L') >= 0) {
|
||||
dayRule.value = 'lastDay';
|
||||
dayRuleSup.value = 'null';
|
||||
dateArr.value[3] = [31];
|
||||
dayRule.value = 'lastDay'
|
||||
dayRuleSup.value = 'null'
|
||||
dateArr.value[3] = [31]
|
||||
} else if (rule !== '*' && rule !== '?') {
|
||||
dateArr.value[3] = getAssignArr(rule)
|
||||
dayRuleSup.value = 'null';
|
||||
dayRuleSup.value = 'null'
|
||||
} else if (rule === '*') {
|
||||
dayRuleSup.value = 'null';
|
||||
dayRuleSup.value = 'null'
|
||||
}
|
||||
}
|
||||
// 获取"时"数组
|
||||
function getHourArr(rule) {
|
||||
dateArr.value[2] = getOrderArr(0, 23);
|
||||
dateArr.value[2] = getOrderArr(0, 23)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[2] = getCycleArr(rule, 24, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
@@ -431,7 +431,7 @@ function getHourArr(rule) {
|
||||
}
|
||||
// 获取"分"数组
|
||||
function getMinArr(rule) {
|
||||
dateArr.value[1] = getOrderArr(0, 59);
|
||||
dateArr.value[1] = getOrderArr(0, 59)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[1] = getCycleArr(rule, 60, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
@@ -442,7 +442,7 @@ function getMinArr(rule) {
|
||||
}
|
||||
// 获取"秒"数组
|
||||
function getSecondArr(rule) {
|
||||
dateArr.value[0] = getOrderArr(0, 59);
|
||||
dateArr.value[0] = getOrderArr(0, 59)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[0] = getCycleArr(rule, 60, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
@@ -453,86 +453,86 @@ function getSecondArr(rule) {
|
||||
}
|
||||
// 根据传进来的min-max返回一个顺序的数组
|
||||
function getOrderArr(min, max) {
|
||||
let arr = [];
|
||||
let arr = []
|
||||
for (let i = min; i <= max; i++) {
|
||||
arr.push(i);
|
||||
arr.push(i)
|
||||
}
|
||||
return arr;
|
||||
return arr
|
||||
}
|
||||
// 根据规则中指定的零散值返回一个数组
|
||||
function getAssignArr(rule) {
|
||||
let arr = [];
|
||||
let assiginArr = rule.split(',');
|
||||
let arr = []
|
||||
let assiginArr = rule.split(',')
|
||||
for (let i = 0; i < assiginArr.length; i++) {
|
||||
arr[i] = Number(assiginArr[i])
|
||||
}
|
||||
arr.sort(compare)
|
||||
return arr;
|
||||
return arr
|
||||
}
|
||||
// 根据一定算术规则计算返回一个数组
|
||||
function getAverageArr(rule, limit) {
|
||||
let arr = [];
|
||||
let agArr = rule.split('/');
|
||||
let min = Number(agArr[0]);
|
||||
let step = Number(agArr[1]);
|
||||
let arr = []
|
||||
let agArr = rule.split('/')
|
||||
let min = Number(agArr[0])
|
||||
let step = Number(agArr[1])
|
||||
while (min <= limit) {
|
||||
arr.push(min);
|
||||
min += step;
|
||||
arr.push(min)
|
||||
min += step
|
||||
}
|
||||
return arr;
|
||||
return arr
|
||||
}
|
||||
// 根据规则返回一个具有周期性的数组
|
||||
function getCycleArr(rule, limit, status) {
|
||||
// status--表示是否从0开始(则从1开始)
|
||||
let arr = [];
|
||||
let cycleArr = rule.split('-');
|
||||
let min = Number(cycleArr[0]);
|
||||
let max = Number(cycleArr[1]);
|
||||
let arr = []
|
||||
let cycleArr = rule.split('-')
|
||||
let min = Number(cycleArr[0])
|
||||
let max = Number(cycleArr[1])
|
||||
if (min > max) {
|
||||
max += limit;
|
||||
max += limit
|
||||
}
|
||||
for (let i = min; i <= max; i++) {
|
||||
let add = 0;
|
||||
let add = 0
|
||||
if (status === false && i % limit === 0) {
|
||||
add = limit;
|
||||
add = limit
|
||||
}
|
||||
arr.push(Math.round(i % limit + add))
|
||||
}
|
||||
arr.sort(compare)
|
||||
return arr;
|
||||
return arr
|
||||
}
|
||||
// 比较数字大小(用于Array.sort)
|
||||
function compare(value1, value2) {
|
||||
if (value2 - value1 > 0) {
|
||||
return -1;
|
||||
return -1
|
||||
} else {
|
||||
return 1;
|
||||
return 1
|
||||
}
|
||||
}
|
||||
// 格式化日期格式如:2017-9-19 18:04:33
|
||||
function formatDate(value, type) {
|
||||
// 计算日期相关值
|
||||
let time = typeof value == 'number' ? new Date(value) : value;
|
||||
let Y = time.getFullYear();
|
||||
let M = time.getMonth() + 1;
|
||||
let D = time.getDate();
|
||||
let h = time.getHours();
|
||||
let m = time.getMinutes();
|
||||
let s = time.getSeconds();
|
||||
let week = time.getDay();
|
||||
let time = typeof value == 'number' ? new Date(value) : value
|
||||
let Y = time.getFullYear()
|
||||
let M = time.getMonth() + 1
|
||||
let D = time.getDate()
|
||||
let h = time.getHours()
|
||||
let m = time.getMinutes()
|
||||
let s = time.getSeconds()
|
||||
let week = time.getDay()
|
||||
// 如果传递了type的话
|
||||
if (type === undefined) {
|
||||
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
|
||||
} else if (type === 'week') {
|
||||
// 在quartz中 1为星期日
|
||||
return week + 1;
|
||||
return week + 1
|
||||
}
|
||||
}
|
||||
// 检查日期是否存在
|
||||
function checkDate(value) {
|
||||
let time = new Date(value);
|
||||
let time = new Date(value)
|
||||
let format = formatDate(time)
|
||||
return value === format;
|
||||
return value === format
|
||||
}
|
||||
onMounted(() => {
|
||||
expressionChange()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
<el-radio v-model='radioValue' :value="1">
|
||||
秒,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
<el-radio v-model='radioValue' :value="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 秒
|
||||
@@ -15,7 +15,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
<el-radio v-model='radioValue' :value="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="58" /> 秒开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="59 - average01" /> 秒执行一次
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
<el-radio v-model='radioValue' :value="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
<el-radio v-model='radioValue' :value="1">
|
||||
周,允许的通配符[, - * ? / L #]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
<el-radio v-model='radioValue' :value="2">
|
||||
不指定
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
<el-radio v-model='radioValue' :value="3">
|
||||
周期从
|
||||
<el-select clearable v-model="cycle01">
|
||||
<el-option
|
||||
@@ -38,7 +38,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
<el-radio v-model='radioValue' :value="4">
|
||||
第
|
||||
<el-input-number v-model='average01' :min="1" :max="4" /> 周的
|
||||
<el-select clearable v-model="average02">
|
||||
@@ -48,7 +48,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="5">
|
||||
<el-radio v-model='radioValue' :value="5">
|
||||
本月最后一个
|
||||
<el-select clearable v-model="weekday">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
@@ -57,7 +57,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="6">
|
||||
<el-radio v-model='radioValue' :value="6">
|
||||
指定
|
||||
<el-select class="multiselect" clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="6">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio :label="1" v-model='radioValue'>
|
||||
<el-radio :value="1" v-model='radioValue'>
|
||||
不填,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="2" v-model='radioValue'>
|
||||
<el-radio :value="2" v-model='radioValue'>
|
||||
每年
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="3" v-model='radioValue'>
|
||||
<el-radio :value="3" v-model='radioValue'>
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min='fullYear' :max="2098"/> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : fullYear + 1" :max="2099"/>
|
||||
@@ -21,7 +21,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="4" v-model='radioValue'>
|
||||
<el-radio :value="4" v-model='radioValue'>
|
||||
从
|
||||
<el-input-number v-model='average01' :min='fullYear' :max="2098"/> 年开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="2099 - average01 || fullYear"/> 年执行一次
|
||||
@@ -30,7 +30,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="5" v-model='radioValue'>
|
||||
<el-radio :value="5" v-model='radioValue'>
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
||||
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" />
|
||||
@@ -61,22 +61,24 @@ const props = defineProps({
|
||||
}
|
||||
}
|
||||
})
|
||||
const fullYear = ref(0)
|
||||
const maxFullYear = ref(0)
|
||||
|
||||
const fullYear = Number(new Date().getFullYear())
|
||||
const maxFullYear = fullYear + 10
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(0)
|
||||
const average01 = ref(0)
|
||||
const cycle01 = ref(fullYear)
|
||||
const cycle02 = ref(fullYear + 1)
|
||||
const average01 = ref(fullYear)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([])
|
||||
const checkCopy = ref([fullYear])
|
||||
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, fullYear.value, maxFullYear.value - 1)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, maxFullYear.value)
|
||||
cycle01.value = props.check(cycle01.value, fullYear, maxFullYear - 1)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, maxFullYear)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, fullYear.value, maxFullYear.value - 1)
|
||||
average01.value = props.check(average01.value, fullYear, maxFullYear - 1)
|
||||
average02.value = props.check(average02.value, 1, 10)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
@@ -97,8 +99,8 @@ function changeRadioValue(value) {
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf("/") > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[1])
|
||||
average02.value = Number(indexArr[0])
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 4
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
@@ -129,14 +131,6 @@ function onRadioChange() {
|
||||
break
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
fullYear.value = Number(new Date().getFullYear())
|
||||
maxFullYear.value = fullYear.value + 10
|
||||
cycle01.value = fullYear.value
|
||||
cycle02.value = cycle01.value + 1
|
||||
average01.value = fullYear.value
|
||||
checkCopy.value = [fullYear.value]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
:disable-transitions="true"
|
||||
:key="item.value + ''"
|
||||
:index="index"
|
||||
:type="item.elTagType === 'primary' ? '' : item.elTagType"
|
||||
:type="item.elTagType"
|
||||
:class="item.elTagClass"
|
||||
>{{ item.label + " " }}</el-tag>
|
||||
</template>
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<script setup>
|
||||
// 记录未匹配的项
|
||||
const unmatchArray = ref([]);
|
||||
const unmatchArray = ref([])
|
||||
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
@@ -45,17 +45,17 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: ",",
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const values = computed(() => {
|
||||
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);
|
||||
});
|
||||
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 = [];
|
||||
unmatchArray.value = []
|
||||
// 没有value不显示
|
||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '' || props.options.length === 0) return false
|
||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '' || !Array.isArray(props.options) || props.options.length === 0) return false
|
||||
// 传入值为数组
|
||||
let unmatch = false // 添加一个标志来判断是否有未匹配项
|
||||
values.value.forEach(item => {
|
||||
@@ -65,13 +65,13 @@ const unmatch = computed(() => {
|
||||
}
|
||||
})
|
||||
return unmatch // 返回标志的值
|
||||
});
|
||||
})
|
||||
|
||||
function handleArray(array) {
|
||||
if (array.length === 0) return "";
|
||||
if (array.length === 0) return ""
|
||||
return array.reduce((pre, cur) => {
|
||||
return pre + " " + cur;
|
||||
});
|
||||
return pre + " " + cur
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -27,17 +27,18 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { QuillEditor } from "@vueup/vue-quill";
|
||||
import "@vueup/vue-quill/dist/vue-quill.snow.css";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import axios from 'axios'
|
||||
import { QuillEditor } from "@vueup/vue-quill"
|
||||
import "@vueup/vue-quill/dist/vue-quill.snow.css"
|
||||
import { getToken } from "@/utils/auth"
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const quillEditorRef = ref();
|
||||
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||
const quillEditorRef = ref()
|
||||
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload") // 上传的图片服务器地址
|
||||
const headers = ref({
|
||||
Authorization: "Bearer " + getToken()
|
||||
});
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
/* 编辑器的内容 */
|
||||
@@ -69,7 +70,7 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: "url",
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const options = ref({
|
||||
theme: "snow",
|
||||
@@ -92,59 +93,60 @@ const options = ref({
|
||||
},
|
||||
placeholder: "请输入内容",
|
||||
readOnly: props.readOnly
|
||||
});
|
||||
})
|
||||
|
||||
const styles = computed(() => {
|
||||
let style = {};
|
||||
let style = {}
|
||||
if (props.minHeight) {
|
||||
style.minHeight = `${props.minHeight}px`;
|
||||
style.minHeight = `${props.minHeight}px`
|
||||
}
|
||||
if (props.height) {
|
||||
style.height = `${props.height}px`;
|
||||
style.height = `${props.height}px`
|
||||
}
|
||||
return style;
|
||||
});
|
||||
return style
|
||||
})
|
||||
|
||||
const content = ref("");
|
||||
const content = ref("")
|
||||
watch(() => props.modelValue, (v) => {
|
||||
if (v !== content.value) {
|
||||
content.value = v === undefined ? "<p></p>" : v;
|
||||
content.value = v == undefined ? "<p></p>" : v
|
||||
}
|
||||
}, { immediate: true });
|
||||
}, { immediate: true })
|
||||
|
||||
// 如果设置了上传地址则自定义图片上传事件
|
||||
onMounted(() => {
|
||||
if (props.type == 'url') {
|
||||
let quill = quillEditorRef.value.getQuill();
|
||||
let toolbar = quill.getModule("toolbar");
|
||||
let quill = quillEditorRef.value.getQuill()
|
||||
let toolbar = quill.getModule("toolbar")
|
||||
toolbar.addHandler("image", (value) => {
|
||||
if (value) {
|
||||
proxy.$refs.uploadRef.click();
|
||||
proxy.$refs.uploadRef.click()
|
||||
} else {
|
||||
quill.format("image", false);
|
||||
quill.format("image", false)
|
||||
}
|
||||
});
|
||||
})
|
||||
quill.root.addEventListener('paste', handlePasteCapture, true)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// 上传前校检格式和大小
|
||||
function handleBeforeUpload(file) {
|
||||
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
|
||||
const isJPG = type.includes(file.type);
|
||||
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"]
|
||||
const isJPG = type.includes(file.type)
|
||||
//检验文件格式
|
||||
if (!isJPG) {
|
||||
proxy.$modal.msgError(`图片格式错误!`);
|
||||
return false;
|
||||
proxy.$modal.msgError(`图片格式错误!`)
|
||||
return false
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
// 上传成功处理
|
||||
@@ -152,21 +154,44 @@ function handleUploadSuccess(res, file) {
|
||||
// 如果上传成功
|
||||
if (res.code == 200) {
|
||||
// 获取富文本实例
|
||||
let quill = toRaw(quillEditorRef.value).getQuill();
|
||||
let quill = toRaw(quillEditorRef.value).getQuill()
|
||||
// 获取光标位置
|
||||
let length = quill.selection.savedRange.index;
|
||||
let length = quill.selection.savedRange.index
|
||||
// 插入图片,res.url为服务器返回的图片链接地址
|
||||
quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName);
|
||||
quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName)
|
||||
// 调整光标到最后
|
||||
quill.setSelection(length + 1);
|
||||
quill.setSelection(length + 1)
|
||||
} else {
|
||||
proxy.$modal.msgError("图片插入失败");
|
||||
proxy.$modal.msgError("图片插入失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 上传失败处理
|
||||
function handleUploadError() {
|
||||
proxy.$modal.msgError("图片插入失败");
|
||||
proxy.$modal.msgError("图片插入失败")
|
||||
}
|
||||
|
||||
// 复制粘贴图片处理
|
||||
function handlePasteCapture(e) {
|
||||
const clipboard = e.clipboardData || window.clipboardData
|
||||
if (clipboard && clipboard.items) {
|
||||
for (let i = 0; i < clipboard.items.length; i++) {
|
||||
const item = clipboard.items[i]
|
||||
if (item.type.indexOf('image') !== -1) {
|
||||
e.preventDefault()
|
||||
const file = item.getAsFile()
|
||||
insertImage(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertImage(file) {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
axios.post(uploadUrl.value, formData, { headers: { "Content-Type": "multipart/form-data", Authorization: headers.value.Authorization } }).then(res => {
|
||||
handleUploadSuccess(res.data)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
:action="uploadFileUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:file-list="fileList"
|
||||
:data="data"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
@@ -13,25 +14,26 @@
|
||||
:headers="headers"
|
||||
class="upload-file-uploader"
|
||||
ref="fileUpload"
|
||||
v-if="!disabled"
|
||||
>
|
||||
<!-- 上传按钮 -->
|
||||
<el-button type="primary">选取文件</el-button>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
<div class="el-upload__tip" v-if="showTip && !disabled">
|
||||
请上传
|
||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
||||
的文件
|
||||
</div>
|
||||
<!-- 文件列表 -->
|
||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<transition-group ref="uploadFileList" class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||
</el-link>
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
|
||||
<el-link :underline="false" @click="handleDelete(index)" type="danger" v-if="!disabled"> 删除</el-link>
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
@@ -39,126 +41,152 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { getToken } from "@/utils/auth"
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 上传接口地址
|
||||
action: {
|
||||
type: String,
|
||||
default: "/common/upload"
|
||||
},
|
||||
// 上传携带的参数
|
||||
data: {
|
||||
type: Object
|
||||
},
|
||||
// 数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
default: 5
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
default: 5
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
|
||||
default: () => ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"]
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 禁用组件(仅查看文件)
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 拖动排序
|
||||
drag: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const emit = defineEmits();
|
||||
const number = ref(0);
|
||||
const uploadList = ref([]);
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||
const fileList = ref([]);
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits()
|
||||
const number = ref(0)
|
||||
const uploadList = ref([])
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) // 上传文件服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() })
|
||||
const fileList = ref([])
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
)
|
||||
|
||||
watch(() => props.modelValue, val => {
|
||||
if (val) {
|
||||
let temp = 1;
|
||||
let temp = 1
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(',');
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(',')
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
item = { name: item, url: item };
|
||||
item = { name: item, url: item }
|
||||
}
|
||||
item.uid = item.uid || new Date().getTime() + temp++;
|
||||
return item;
|
||||
});
|
||||
item.uid = item.uid || new Date().getTime() + temp++
|
||||
return item
|
||||
})
|
||||
} else {
|
||||
fileList.value = [];
|
||||
return [];
|
||||
fileList.value = []
|
||||
return []
|
||||
}
|
||||
},{ deep: true, immediate: true });
|
||||
},{ deep: true, immediate: true })
|
||||
|
||||
// 上传前校检格式和大小
|
||||
function handleBeforeUpload(file) {
|
||||
// 校检文件类型
|
||||
if (props.fileType.length) {
|
||||
const fileName = file.name.split('.');
|
||||
const fileExt = fileName[fileName.length - 1];
|
||||
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
|
||||
const fileName = file.name.split('.')
|
||||
const fileExt = fileName[fileName.length - 1]
|
||||
const isTypeOk = props.fileType.indexOf(fileExt) >= 0
|
||||
if (!isTypeOk) {
|
||||
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
|
||||
return false;
|
||||
proxy.$modal.msgError(`文件格式不正确,请上传${props.fileType.join("/")}格式文件!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 校检文件名是否包含特殊字符
|
||||
if (file.name.includes(',')) {
|
||||
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!')
|
||||
return false
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
proxy.$modal.loading("正在上传文件,请稍候...");
|
||||
number.value++;
|
||||
return true;
|
||||
proxy.$modal.loading("正在上传文件,请稍候...")
|
||||
number.value++
|
||||
return true
|
||||
}
|
||||
|
||||
// 文件个数超出
|
||||
function handleExceed() {
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
function handleUploadError(err) {
|
||||
proxy.$modal.msgError("上传文件失败");
|
||||
proxy.$modal.msgError("上传文件失败")
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
||||
uploadedSuccessfully();
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName })
|
||||
uploadedSuccessfully()
|
||||
} else {
|
||||
number.value--;
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgError(res.msg);
|
||||
proxy.$refs.fileUpload.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
number.value--
|
||||
proxy.$modal.closeLoading()
|
||||
proxy.$modal.msgError(res.msg)
|
||||
proxy.$refs.fileUpload.handleRemove(file)
|
||||
uploadedSuccessfully()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
function handleDelete(index) {
|
||||
fileList.value.splice(index, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
fileList.value.splice(index, 1)
|
||||
emit("update:modelValue", listToString(fileList.value))
|
||||
}
|
||||
|
||||
// 上传结束处理
|
||||
function uploadedSuccessfully() {
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
proxy.$modal.closeLoading();
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
|
||||
uploadList.value = []
|
||||
number.value = 0
|
||||
emit("update:modelValue", listToString(fileList.value))
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,26 +194,46 @@ function uploadedSuccessfully() {
|
||||
function getFileName(name) {
|
||||
// 如果是url那么取最后的名字 如果不是直接返回
|
||||
if (name.lastIndexOf("/") > -1) {
|
||||
return name.slice(name.lastIndexOf("/") + 1);
|
||||
return name.slice(name.lastIndexOf("/") + 1)
|
||||
} else {
|
||||
return name;
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
function listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
let strs = ""
|
||||
separator = separator || ","
|
||||
for (let i in list) {
|
||||
if (list[i].url) {
|
||||
strs += list[i].url + separator;
|
||||
strs += list[i].url + separator
|
||||
}
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : ''
|
||||
}
|
||||
</script>
|
||||
|
||||
// 初始化拖拽排序
|
||||
onMounted(() => {
|
||||
if (props.drag && !props.disabled) {
|
||||
nextTick(() => {
|
||||
const element = proxy.$refs.uploadFileList?.$el || proxy.$refs.uploadFileList
|
||||
Sortable.create(element, {
|
||||
ghostClass: 'file-upload-darg',
|
||||
onEnd: (evt) => {
|
||||
const movedItem = fileList.value.splice(evt.oldIndex, 1)[0]
|
||||
fileList.value.splice(evt.newIndex, 0, movedItem)
|
||||
emit('update:modelValue', listToString(fileList.value))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.file-upload-darg {
|
||||
opacity: 0.5;
|
||||
background: #c8ebfb;
|
||||
}
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@@ -194,6 +242,7 @@ function listToString(list, separator) {
|
||||
line-height: 2;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
transition: none !important;
|
||||
}
|
||||
.upload-file-list .ele-upload-list__item-content {
|
||||
display: flex;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
@@ -23,7 +24,7 @@ defineProps({
|
||||
|
||||
const emit = defineEmits()
|
||||
const toggleClick = () => {
|
||||
emit('toggleClick');
|
||||
emit('toggleClick')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,19 +1,46 @@
|
||||
<template>
|
||||
<div :class="{ 'show': show }" class="header-search">
|
||||
<div class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||
<el-select
|
||||
ref="headerSearchSelectRef"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="Search"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
<el-dialog
|
||||
v-model="show"
|
||||
width="600"
|
||||
@close="close"
|
||||
:show-close="false"
|
||||
append-to-body
|
||||
>
|
||||
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="search"
|
||||
ref="headerSearchSelectRef"
|
||||
size="large"
|
||||
@input="querySearch"
|
||||
prefix-icon="Search"
|
||||
placeholder="菜单搜索,支持标题、URL模糊查询"
|
||||
clearable
|
||||
@keyup.enter="selectActiveResult"
|
||||
@keydown.up.prevent="navigateResult('up')"
|
||||
@keydown.down.prevent="navigateResult('down')"
|
||||
>
|
||||
</el-input>
|
||||
|
||||
<div class="result-wrap">
|
||||
<el-scrollbar>
|
||||
<div class="search-item" tabindex="1" v-for="(item, index) in options" :key="item.path" :style="activeStyle(index)" @mouseenter="activeIndex = index" @mouseleave="activeIndex = -1">
|
||||
<div class="left">
|
||||
<svg-icon class="menu-icon" :icon-class="item.icon" />
|
||||
</div>
|
||||
<div class="search-info" @click="change(item)">
|
||||
<div class="menu-title">
|
||||
{{ item.title.join(" / ") }}
|
||||
</div>
|
||||
<div class="menu-path">
|
||||
{{ item.path }}
|
||||
</div>
|
||||
</div>
|
||||
<svg-icon icon-class="enter" v-show="index === activeIndex"/>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -21,38 +48,46 @@
|
||||
import Fuse from 'fuse.js'
|
||||
import { getNormalPath } from '@/utils/ruoyi'
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
const search = ref('');
|
||||
const options = ref([]);
|
||||
const searchPool = ref([]);
|
||||
const show = ref(false);
|
||||
const fuse = ref(undefined);
|
||||
const headerSearchSelectRef = ref(null);
|
||||
const router = useRouter();
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
const search = ref('')
|
||||
const options = ref([])
|
||||
const searchPool = ref([])
|
||||
const activeIndex = ref(-1)
|
||||
const show = ref(false)
|
||||
const fuse = ref(undefined)
|
||||
const headerSearchSelectRef = ref(null)
|
||||
const router = useRouter()
|
||||
const theme = computed(() => useSettingsStore().theme)
|
||||
const routes = computed(() => usePermissionStore().defaultRoutes)
|
||||
|
||||
function click() {
|
||||
show.value = !show.value
|
||||
if (show.value) {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||
options.value = searchPool.value
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function close() {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||
search.value = ''
|
||||
options.value = []
|
||||
show.value = false
|
||||
activeIndex.value = -1
|
||||
}
|
||||
|
||||
function change(val) {
|
||||
const path = val.path;
|
||||
const query = val.query;
|
||||
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");
|
||||
const pindex = path.indexOf("http")
|
||||
window.open(path.substr(pindex, path.length), "_blank")
|
||||
} else {
|
||||
if (query) {
|
||||
router.push({ path: path, query: JSON.parse(query) });
|
||||
router.push({ path: path, query: JSON.parse(query) })
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
@@ -64,6 +99,7 @@ function change(val) {
|
||||
show.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function initFuse(list) {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
@@ -80,6 +116,7 @@ function initFuse(list) {
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||
@@ -88,16 +125,17 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||
for (const r of routes) {
|
||||
// skip hidden router
|
||||
if (r.hidden) { continue }
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path
|
||||
const data = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
title: [...prefixTitle]
|
||||
title: [...prefixTitle],
|
||||
icon: ''
|
||||
}
|
||||
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title]
|
||||
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
data.icon = r.meta.icon
|
||||
if (r.redirect !== "noRedirect") {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data)
|
||||
@@ -117,30 +155,42 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function querySearch(query) {
|
||||
activeIndex.value = -1
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query)
|
||||
options.value = fuse.value.search(query).map((item) => item.item) ?? searchPool.value
|
||||
} else {
|
||||
options.value = []
|
||||
options.value = searchPool.value
|
||||
}
|
||||
}
|
||||
|
||||
function activeStyle(index) {
|
||||
if (index !== activeIndex.value) return {}
|
||||
return {
|
||||
"background-color": theme.value,
|
||||
"color": "#fff"
|
||||
}
|
||||
}
|
||||
|
||||
function navigateResult(direction) {
|
||||
if (direction === "up") {
|
||||
activeIndex.value = activeIndex.value <= 0 ? options.value.length - 1 : activeIndex.value - 1
|
||||
} else if (direction === "down") {
|
||||
activeIndex.value = activeIndex.value >= options.value.length - 1 ? 0 : activeIndex.value + 1
|
||||
}
|
||||
}
|
||||
|
||||
function selectActiveResult() {
|
||||
if (options.value.length > 0 && activeIndex.value >= 0) {
|
||||
change(options.value[activeIndex.value])
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
searchPool.value = generateRoutes(routes.value);
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
searchPool.value = generateRoutes(routes.value)
|
||||
})
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', close)
|
||||
}
|
||||
})
|
||||
|
||||
watch(searchPool, (list) => {
|
||||
initFuse(list)
|
||||
})
|
||||
@@ -148,40 +198,55 @@ watch(searchPool, (list) => {
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
.result-wrap {
|
||||
height: 280px;
|
||||
margin: 6px 0;
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
vertical-align: middle;
|
||||
.search-item {
|
||||
display: flex;
|
||||
height: 48px;
|
||||
align-items: center;
|
||||
padding-right: 10px;
|
||||
|
||||
.left {
|
||||
width: 60px;
|
||||
text-align: center;
|
||||
|
||||
.menu-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-info {
|
||||
padding-left: 5px;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
flex: 1;
|
||||
|
||||
.menu-title,
|
||||
.menu-path {
|
||||
height: 20px;
|
||||
}
|
||||
.menu-path {
|
||||
color: #ccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 210px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.search-item:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -30,11 +30,11 @@ const props = defineProps({
|
||||
activeIcon: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const iconName = ref('');
|
||||
const iconList = ref(icons);
|
||||
const emit = defineEmits(['selected']);
|
||||
const iconName = ref('')
|
||||
const iconList = ref(icons)
|
||||
const emit = defineEmits(['selected'])
|
||||
|
||||
function filterIcons() {
|
||||
iconList.value = icons
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
let icons = []
|
||||
const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
|
||||
const modules = import.meta.glob('./../../assets/icons/svg/*.svg')
|
||||
for (const path in modules) {
|
||||
const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
|
||||
icons.push(p);
|
||||
const p = path.split('assets/icons/svg/')[1].split('.svg')[0]
|
||||
icons.push(p)
|
||||
}
|
||||
|
||||
export default icons
|
||||
@@ -15,7 +15,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { isExternal } from "@/utils/validate";
|
||||
import { isExternal } from "@/utils/validate"
|
||||
|
||||
const props = defineProps({
|
||||
src: {
|
||||
@@ -30,41 +30,41 @@ const props = defineProps({
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const realSrc = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
let real_src = props.src.split(",")[0];
|
||||
let real_src = props.src.split(",")[0]
|
||||
if (isExternal(real_src)) {
|
||||
return real_src;
|
||||
return real_src
|
||||
}
|
||||
return import.meta.env.VITE_APP_BASE_API + real_src;
|
||||
});
|
||||
return import.meta.env.VITE_APP_BASE_API + real_src
|
||||
})
|
||||
|
||||
const realSrcList = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
let real_src_list = props.src.split(",");
|
||||
let srcList = [];
|
||||
let real_src_list = props.src.split(",")
|
||||
let srcList = []
|
||||
real_src_list.forEach(item => {
|
||||
if (isExternal(item)) {
|
||||
return srcList.push(item);
|
||||
return srcList.push(item)
|
||||
}
|
||||
return srcList.push(import.meta.env.VITE_APP_BASE_API + item);
|
||||
});
|
||||
return srcList;
|
||||
});
|
||||
return srcList.push(import.meta.env.VITE_APP_BASE_API + item)
|
||||
})
|
||||
return srcList
|
||||
})
|
||||
|
||||
const realWidth = computed(() =>
|
||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||
);
|
||||
)
|
||||
|
||||
const realHeight = computed(() =>
|
||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||
);
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
<div class="component-upload-image">
|
||||
<el-upload
|
||||
multiple
|
||||
:disabled="disabled"
|
||||
:action="uploadImgUrl"
|
||||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:data="data"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
@@ -20,7 +22,7 @@
|
||||
<el-icon class="avatar-uploader-icon"><plus /></el-icon>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
<div class="el-upload__tip" v-if="showTip && !disabled">
|
||||
请上传
|
||||
<template v-if="fileSize">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
@@ -46,163 +48,202 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { getToken } from "@/utils/auth"
|
||||
import { isExternal } from "@/utils/validate"
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 上传接口地址
|
||||
action: {
|
||||
type: String,
|
||||
default: "/common/upload"
|
||||
},
|
||||
// 上传携带的参数
|
||||
data: {
|
||||
type: Object
|
||||
},
|
||||
// 图片数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
default: 5
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
default: 5
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["png", "jpg", "jpeg"],
|
||||
default: () => ["png", "jpg", "jpeg"]
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
});
|
||||
// 禁用组件(仅查看图片)
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 拖动排序
|
||||
drag: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const emit = defineEmits();
|
||||
const number = ref(0);
|
||||
const uploadList = ref([]);
|
||||
const dialogImageUrl = ref("");
|
||||
const dialogVisible = ref(false);
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||
const fileList = ref([]);
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits()
|
||||
const number = ref(0)
|
||||
const uploadList = ref([])
|
||||
const dialogImageUrl = ref("")
|
||||
const dialogVisible = ref(false)
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
||||
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) // 上传的图片服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() })
|
||||
const fileList = ref([])
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
)
|
||||
|
||||
watch(() => props.modelValue, val => {
|
||||
if (val) {
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(",");
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(",")
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
if (item.indexOf(baseUrl) === -1) {
|
||||
item = { name: baseUrl + item, url: baseUrl + item };
|
||||
if (item.indexOf(baseUrl) === -1 && !isExternal(item)) {
|
||||
item = { name: baseUrl + item, url: baseUrl + item }
|
||||
} else {
|
||||
item = { name: item, url: item };
|
||||
item = { name: item, url: item }
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
return item
|
||||
})
|
||||
} else {
|
||||
fileList.value = [];
|
||||
return [];
|
||||
fileList.value = []
|
||||
return []
|
||||
}
|
||||
},{ deep: true, immediate: true });
|
||||
},{ deep: true, immediate: true })
|
||||
|
||||
// 上传前loading加载
|
||||
function handleBeforeUpload(file) {
|
||||
let isImg = false;
|
||||
let isImg = false
|
||||
if (props.fileType.length) {
|
||||
let fileExtension = "";
|
||||
let fileExtension = ""
|
||||
if (file.name.lastIndexOf(".") > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1)
|
||||
}
|
||||
isImg = props.fileType.some(type => {
|
||||
if (file.type.indexOf(type) > -1) return true;
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||
return false;
|
||||
});
|
||||
if (file.type.indexOf(type) > -1) return true
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
isImg = file.type.indexOf("image") > -1;
|
||||
isImg = file.type.indexOf("image") > -1
|
||||
}
|
||||
if (!isImg) {
|
||||
proxy.$modal.msgError(
|
||||
`文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
|
||||
);
|
||||
return false;
|
||||
proxy.$modal.msgError(`文件格式不正确,请上传${props.fileType.join("/")}图片格式文件!`)
|
||||
return false
|
||||
}
|
||||
if (file.name.includes(',')) {
|
||||
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!')
|
||||
return false
|
||||
}
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
proxy.$modal.loading("正在上传图片,请稍候...");
|
||||
number.value++;
|
||||
proxy.$modal.loading("正在上传图片,请稍候...")
|
||||
number.value++
|
||||
}
|
||||
|
||||
// 文件个数超出
|
||||
function handleExceed() {
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
|
||||
}
|
||||
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
||||
uploadedSuccessfully();
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName })
|
||||
uploadedSuccessfully()
|
||||
} else {
|
||||
number.value--;
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgError(res.msg);
|
||||
proxy.$refs.imageUpload.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
number.value--
|
||||
proxy.$modal.closeLoading()
|
||||
proxy.$modal.msgError(res.msg)
|
||||
proxy.$refs.imageUpload.handleRemove(file)
|
||||
uploadedSuccessfully()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除图片
|
||||
function handleDelete(file) {
|
||||
const findex = fileList.value.map(f => f.name).indexOf(file.name);
|
||||
const findex = fileList.value.map(f => f.name).indexOf(file.name)
|
||||
if (findex > -1 && uploadList.value.length === number.value) {
|
||||
fileList.value.splice(findex, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
return false;
|
||||
fileList.value.splice(findex, 1)
|
||||
emit("update:modelValue", listToString(fileList.value))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 上传结束处理
|
||||
function uploadedSuccessfully() {
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
proxy.$modal.closeLoading();
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
|
||||
uploadList.value = []
|
||||
number.value = 0
|
||||
emit("update:modelValue", listToString(fileList.value))
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
function handleUploadError() {
|
||||
proxy.$modal.msgError("上传图片失败");
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgError("上传图片失败")
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
|
||||
// 预览
|
||||
function handlePictureCardPreview(file) {
|
||||
dialogImageUrl.value = file.url;
|
||||
dialogVisible.value = true;
|
||||
dialogImageUrl.value = file.url
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
function listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
let strs = ""
|
||||
separator = separator || ","
|
||||
for (let i in list) {
|
||||
if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
|
||||
strs += list[i].url.replace(baseUrl, "") + separator;
|
||||
strs += list[i].url.replace(baseUrl, "") + separator
|
||||
}
|
||||
}
|
||||
return strs != "" ? strs.substr(0, strs.length - 1) : "";
|
||||
return strs != "" ? strs.substr(0, strs.length - 1) : ""
|
||||
}
|
||||
|
||||
// 初始化拖拽排序
|
||||
onMounted(() => {
|
||||
if (props.drag && !props.disabled) {
|
||||
nextTick(() => {
|
||||
const element = proxy.$refs.imageUpload?.$el?.querySelector('.el-upload-list')
|
||||
Sortable.create(element, {
|
||||
onEnd: (evt) => {
|
||||
const movedItem = fileList.value.splice(evt.oldIndex, 1)[0]
|
||||
fileList.value.splice(evt.newIndex, 0, movedItem)
|
||||
emit('update:modelValue', listToString(fileList.value))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -210,4 +251,8 @@ function listToString(list, separator) {
|
||||
:deep(.hide .el-upload--picture-card) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.el-upload.el-upload--picture-card.is-disabled) {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -59,7 +59,7 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits();
|
||||
const emit = defineEmits()
|
||||
const currentPage = computed({
|
||||
get() {
|
||||
return props.page
|
||||
@@ -76,6 +76,7 @@ const pageSize = computed({
|
||||
emit('update:limit', val)
|
||||
}
|
||||
})
|
||||
|
||||
function handleSizeChange(val) {
|
||||
if (currentPage.value * val > props.total) {
|
||||
currentPage.value = 1
|
||||
@@ -85,19 +86,18 @@ function handleSizeChange(val) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
|
||||
function handleCurrentChange(val) {
|
||||
emit('pagination', { page: val, limit: pageSize.value })
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pagination-container {
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
.pagination-container.hidden {
|
||||
display: none;
|
||||
|
||||
@@ -7,15 +7,20 @@
|
||||
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
||||
<el-button circle icon="Refresh" @click="refresh()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
|
||||
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="Object.keys(columns).length > 0">
|
||||
<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 :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
||||
</el-dropdown-item>
|
||||
<div class="check-line"></div>
|
||||
<template v-for="(item, key) in columns" :key="item.key">
|
||||
<el-dropdown-item>
|
||||
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
|
||||
<el-checkbox v-model="item.visible" @change="checkboxChange($event, key)" :label="item.label" />
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
@@ -27,7 +32,7 @@
|
||||
<el-transfer
|
||||
:titles="['显示', '隐藏']"
|
||||
v-model="value"
|
||||
:data="columns"
|
||||
:data="transferData"
|
||||
@change="dataChange"
|
||||
></el-transfer>
|
||||
</el-dialog>
|
||||
@@ -39,83 +44,119 @@ const props = defineProps({
|
||||
/* 是否显示检索条件 */
|
||||
showSearch: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
/* 显隐列信息 */
|
||||
/* 显隐列信息(数组格式、对象格式) */
|
||||
columns: {
|
||||
type: Array,
|
||||
type: [Array, Object],
|
||||
default: () => ({})
|
||||
},
|
||||
/* 是否显示检索图标 */
|
||||
search: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
||||
showColumnsType: {
|
||||
type: String,
|
||||
default: "checkbox",
|
||||
default: "checkbox"
|
||||
},
|
||||
/* 右外边距 */
|
||||
gutter: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
default: 10
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:showSearch', 'queryTable']);
|
||||
const emits = defineEmits(['update:showSearch', 'queryTable'])
|
||||
|
||||
// 显隐数据
|
||||
const value = ref([]);
|
||||
const value = ref([])
|
||||
// 弹出层标题
|
||||
const title = ref("显示/隐藏");
|
||||
const title = ref("显示/隐藏")
|
||||
// 是否显示弹出层
|
||||
const open = ref(false);
|
||||
const open = ref(false)
|
||||
|
||||
const style = computed(() => {
|
||||
const ret = {};
|
||||
const ret = {}
|
||||
if (props.gutter) {
|
||||
ret.marginRight = `${props.gutter / 2}px`;
|
||||
ret.marginRight = `${props.gutter / 2}px`
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
return ret
|
||||
})
|
||||
|
||||
// 是否全选/半选 状态
|
||||
const isChecked = computed({
|
||||
get: () => Array.isArray(props.columns) ? props.columns.every(col => col.visible) : Object.values(props.columns).every((col) => col.visible),
|
||||
set: () => {}
|
||||
})
|
||||
const isIndeterminate = computed(() => Array.isArray(props.columns) ? props.columns.some((col) => col.visible) && !isChecked.value : Object.values(props.columns).some((col) => col.visible) && !isChecked.value)
|
||||
const transferData = computed(() => Array.isArray(props.columns) ? props.columns.map((item, index) => ({ key: index, label: item.label })) : Object.keys(props.columns).map((key, index) => ({ key: index, label: props.columns[key].label })))
|
||||
|
||||
// 搜索
|
||||
function toggleSearch() {
|
||||
emits("update:showSearch", !props.showSearch);
|
||||
emits("update:showSearch", !props.showSearch)
|
||||
}
|
||||
|
||||
// 刷新
|
||||
function refresh() {
|
||||
emits("queryTable");
|
||||
emits("queryTable")
|
||||
}
|
||||
|
||||
// 右侧列表元素变化
|
||||
function dataChange(data) {
|
||||
for (let item in props.columns) {
|
||||
const key = props.columns[item].key;
|
||||
props.columns[item].visible = !data.includes(key);
|
||||
if (Array.isArray(props.columns)) {
|
||||
for (let item in props.columns) {
|
||||
const key = props.columns[item].key
|
||||
props.columns[item].visible = !data.includes(key)
|
||||
}
|
||||
} else {
|
||||
Object.keys(props.columns).forEach((key, index) => {
|
||||
props.columns[key].visible = !data.includes(index)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 打开显隐列dialog
|
||||
function showColumn() {
|
||||
open.value = true;
|
||||
open.value = true
|
||||
}
|
||||
|
||||
if (props.showColumnsType == 'transfer') {
|
||||
// 显隐列初始默认隐藏列
|
||||
for (let item in props.columns) {
|
||||
if (props.columns[item].visible === false) {
|
||||
value.value.push(parseInt(item));
|
||||
if (props.showColumnsType == "transfer") {
|
||||
// transfer穿梭显隐列初始默认隐藏列
|
||||
if (Array.isArray(props.columns)) {
|
||||
for (let item in props.columns) {
|
||||
if (props.columns[item].visible === false) {
|
||||
value.value.push(parseInt(item))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object.keys(props.columns).forEach((key, index) => {
|
||||
if (props.columns[key].visible === false) {
|
||||
value.value.push(index)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 勾选
|
||||
function checkboxChange(event, label) {
|
||||
props.columns.filter(item => item.label == label)[0].visible = event;
|
||||
// 单勾选
|
||||
function checkboxChange(event, key) {
|
||||
if (Array.isArray(props.columns)) {
|
||||
props.columns.filter(item => item.key == key)[0].visible = event
|
||||
} else {
|
||||
props.columns[key].visible = event
|
||||
}
|
||||
}
|
||||
|
||||
// 切换全选/反选
|
||||
function toggleCheckAll() {
|
||||
const newValue = !isChecked.value
|
||||
if (Array.isArray(props.columns)) {
|
||||
props.columns.forEach((col) => (col.visible = newValue))
|
||||
} else {
|
||||
Object.values(props.columns).forEach((col) => (col.visible = newValue))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@@ -131,4 +172,10 @@ function checkboxChange(event, label) {
|
||||
line-height: 30px;
|
||||
padding: 0 17px;
|
||||
}
|
||||
.check-line {
|
||||
width: 90%;
|
||||
height: 1px;
|
||||
background-color: #ccc;
|
||||
margin: 3px auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const url = ref('http://doc.ruoyi.vip/ruoyi-vue');
|
||||
const url = ref('http://doc.ruoyi.vip/ruoyi-vue')
|
||||
|
||||
function goto() {
|
||||
window.open(url.value)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const url = ref('https://gitee.com/y_project/RuoYi-Vue');
|
||||
const url = ref('https://gitee.com/y_project/RuoYi-Vue')
|
||||
|
||||
function goto() {
|
||||
window.open(url.value)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<script setup>
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
|
||||
const { isFullscreen, enter, exit, toggle } = useFullscreen();
|
||||
const { isFullscreen, enter, exit, toggle } = useFullscreen()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
||||
@@ -16,23 +16,23 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import useAppStore from "@/store/modules/app";
|
||||
import useAppStore from "@/store/modules/app"
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const appStore = useAppStore()
|
||||
const size = computed(() => appStore.size)
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
const sizeOptions = ref([
|
||||
{ label: "较大", value: "large" },
|
||||
{ label: "默认", value: "default" },
|
||||
{ label: "稍小", value: "small" },
|
||||
]);
|
||||
])
|
||||
|
||||
function handleSetSize(size) {
|
||||
proxy.$modal.loading("正在设置布局大小,请稍候...");
|
||||
appStore.setSize(size);
|
||||
setTimeout("window.location.reload()", 1000);
|
||||
proxy.$modal.loading("正在设置布局大小,请稍候...")
|
||||
appStore.setSize(size)
|
||||
setTimeout("window.location.reload()", 1000)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as components from '@element-plus/icons-vue'
|
||||
|
||||
export default {
|
||||
install: (app) => {
|
||||
for (const key in components) {
|
||||
const componentConfig = components[key];
|
||||
app.component(componentConfig.name, componentConfig);
|
||||
}
|
||||
},
|
||||
};
|
||||
install: (app) => {
|
||||
for (const key in components) {
|
||||
const componentConfig = components[key]
|
||||
app.component(componentConfig.name, componentConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,126 +40,127 @@ import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
// 顶部栏初始数
|
||||
const visibleNumber = ref(null);
|
||||
const visibleNumber = ref(null)
|
||||
// 当前激活菜单的 index
|
||||
const currentIndex = ref(null);
|
||||
const currentIndex = ref(null)
|
||||
// 隐藏侧边栏路由
|
||||
const hideList = ['/index', '/user/profile'];
|
||||
const hideList = ['/index', '/user/profile']
|
||||
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 主题颜色
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const theme = computed(() => settingsStore.theme)
|
||||
// 所有的路由信息
|
||||
const routers = computed(() => permissionStore.topbarRouters);
|
||||
const routers = computed(() => permissionStore.topbarRouters)
|
||||
|
||||
// 顶部显示菜单
|
||||
const topMenus = computed(() => {
|
||||
let topMenus = [];
|
||||
let topMenus = []
|
||||
routers.value.map((menu) => {
|
||||
if (menu.hidden !== true) {
|
||||
// 兼容顶部栏一级菜单内部跳转
|
||||
if (menu.path === "/") {
|
||||
topMenus.push(menu.children[0]);
|
||||
if (menu.path === '/' && menu.children) {
|
||||
topMenus.push(menu.children[0])
|
||||
} else {
|
||||
topMenus.push(menu);
|
||||
topMenus.push(menu)
|
||||
}
|
||||
}
|
||||
})
|
||||
return topMenus;
|
||||
return topMenus
|
||||
})
|
||||
|
||||
// 设置子路由
|
||||
const childrenMenus = computed(() => {
|
||||
let childrenMenus = [];
|
||||
let childrenMenus = []
|
||||
routers.value.map((router) => {
|
||||
for (let item in router.children) {
|
||||
if (router.children[item].parentPath === undefined) {
|
||||
if(router.path === "/") {
|
||||
router.children[item].path = "/" + router.children[item].path;
|
||||
router.children[item].path = "/" + router.children[item].path
|
||||
} else {
|
||||
if(!isHttp(router.children[item].path)) {
|
||||
router.children[item].path = router.path + "/" + router.children[item].path;
|
||||
router.children[item].path = router.path + "/" + router.children[item].path
|
||||
}
|
||||
}
|
||||
router.children[item].parentPath = router.path;
|
||||
router.children[item].parentPath = router.path
|
||||
}
|
||||
childrenMenus.push(router.children[item]);
|
||||
childrenMenus.push(router.children[item])
|
||||
}
|
||||
})
|
||||
return constantRoutes.concat(childrenMenus);
|
||||
return constantRoutes.concat(childrenMenus)
|
||||
})
|
||||
|
||||
// 默认激活的菜单
|
||||
const activeMenu = computed(() => {
|
||||
const path = route.path;
|
||||
let activePath = path;
|
||||
const path = route.path
|
||||
let activePath = path
|
||||
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
|
||||
const tmpPath = path.substring(1, path.length);
|
||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
|
||||
const tmpPath = path.substring(1, path.length)
|
||||
if (!route.meta.link) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"))
|
||||
appStore.toggleSideBarHide(false)
|
||||
}
|
||||
} else if(!route.children) {
|
||||
activePath = path;
|
||||
appStore.toggleSideBarHide(true);
|
||||
activePath = path
|
||||
appStore.toggleSideBarHide(true)
|
||||
}
|
||||
activeRoutes(activePath);
|
||||
return activePath;
|
||||
activeRoutes(activePath)
|
||||
return activePath
|
||||
})
|
||||
|
||||
function setVisibleNumber() {
|
||||
const width = document.body.getBoundingClientRect().width / 3;
|
||||
visibleNumber.value = parseInt(width / 85);
|
||||
const width = document.body.getBoundingClientRect().width / 3
|
||||
visibleNumber.value = parseInt(width / 85)
|
||||
}
|
||||
|
||||
function handleSelect(key, keyPath) {
|
||||
currentIndex.value = key;
|
||||
const route = routers.value.find(item => item.path === key);
|
||||
currentIndex.value = key
|
||||
const route = routers.value.find(item => item.path === key)
|
||||
if (isHttp(key)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
window.open(key, "_blank");
|
||||
window.open(key, "_blank")
|
||||
} else if (!route || !route.children) {
|
||||
// 没有子路由路径内部打开
|
||||
const routeMenu = childrenMenus.value.find(item => item.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 });
|
||||
let query = JSON.parse(routeMenu.query)
|
||||
router.push({ path: key, query: query })
|
||||
} else {
|
||||
router.push({ path: key });
|
||||
router.push({ path: key })
|
||||
}
|
||||
appStore.toggleSideBarHide(true);
|
||||
appStore.toggleSideBarHide(true)
|
||||
} else {
|
||||
// 显示左侧联动菜单
|
||||
activeRoutes(key);
|
||||
appStore.toggleSideBarHide(false);
|
||||
activeRoutes(key)
|
||||
appStore.toggleSideBarHide(false)
|
||||
}
|
||||
}
|
||||
|
||||
function activeRoutes(key) {
|
||||
let routes = [];
|
||||
let routes = []
|
||||
if (childrenMenus.value && childrenMenus.value.length > 0) {
|
||||
childrenMenus.value.map((item) => {
|
||||
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
||||
routes.push(item);
|
||||
routes.push(item)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
if(routes.length > 0) {
|
||||
permissionStore.setSidebarRouters(routes);
|
||||
permissionStore.setSidebarRouters(routes)
|
||||
} else {
|
||||
appStore.toggleSideBarHide(true);
|
||||
appStore.toggleSideBarHide(true)
|
||||
}
|
||||
return routes;
|
||||
return routes
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', setVisibleNumber)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', setVisibleNumber)
|
||||
})
|
||||
@@ -196,7 +197,7 @@ onMounted(() => {
|
||||
|
||||
/* 背景色隐藏 */
|
||||
.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;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 图标右间距 */
|
||||
@@ -211,4 +212,6 @@ onMounted(() => {
|
||||
margin-left: 8px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
<template>
|
||||
<div class="el-tree-select">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
v-model="valueId"
|
||||
ref="treeSelect"
|
||||
:filterable="true"
|
||||
:clearable="true"
|
||||
@clear="clearHandle"
|
||||
:filter-method="selectFilterData"
|
||||
:placeholder="placeholder"
|
||||
>
|
||||
<el-option :value="valueId" :label="valueTitle">
|
||||
<el-tree
|
||||
id="tree-option"
|
||||
ref="selectTree"
|
||||
:accordion="accordion"
|
||||
:data="options"
|
||||
:props="objMap"
|
||||
:node-key="objMap.value"
|
||||
:expand-on-click-node="false"
|
||||
:default-expanded-keys="defaultExpandedKey"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
></el-tree>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
/* 配置项 */
|
||||
objMap: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
value: 'id', // ID字段名
|
||||
label: 'label', // 显示名称
|
||||
children: 'children' // 子级字段名
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 自动收起 */
|
||||
accordion: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**当前双向数据绑定的值 */
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
/**当前的数据 */
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/**输入框内部的文字 */
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const valueId = computed({
|
||||
get: () => props.value,
|
||||
set: (val) => {
|
||||
emit('update:value', val)
|
||||
}
|
||||
});
|
||||
const valueTitle = ref('');
|
||||
const defaultExpandedKey = ref([]);
|
||||
|
||||
function initHandle() {
|
||||
nextTick(() => {
|
||||
const selectedValue = valueId.value;
|
||||
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
|
||||
const node = proxy.$refs.selectTree.getNode(selectedValue)
|
||||
if (node) {
|
||||
valueTitle.value = node.data[props.objMap.label]
|
||||
proxy.$refs.selectTree.setCurrentKey(selectedValue) // 设置默认选中
|
||||
defaultExpandedKey.value = [selectedValue] // 设置默认展开
|
||||
}
|
||||
} else {
|
||||
clearHandle()
|
||||
}
|
||||
})
|
||||
}
|
||||
function handleNodeClick(node) {
|
||||
valueTitle.value = node[props.objMap.label]
|
||||
valueId.value = node[props.objMap.value];
|
||||
defaultExpandedKey.value = [];
|
||||
proxy.$refs.treeSelect.blur()
|
||||
selectFilterData('')
|
||||
}
|
||||
function selectFilterData(val) {
|
||||
proxy.$refs.selectTree.filter(val)
|
||||
}
|
||||
function filterNode(value, data) {
|
||||
if (!value) return true
|
||||
return data[props.objMap['label']].indexOf(value) !== -1
|
||||
}
|
||||
function clearHandle() {
|
||||
valueTitle.value = ''
|
||||
valueId.value = ''
|
||||
defaultExpandedKey.value = [];
|
||||
clearSelected()
|
||||
}
|
||||
function clearSelected() {
|
||||
const allNode = document.querySelectorAll('#tree-option .el-tree-node')
|
||||
allNode.forEach((element) => element.classList.remove('is-current'))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initHandle()
|
||||
})
|
||||
|
||||
watch(valueId, () => {
|
||||
initHandle();
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import "@/assets/styles/variables.module.scss";
|
||||
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.selected {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul li .el-tree .el-tree-node__content {
|
||||
height: auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content:hover),
|
||||
:deep(.el-tree-node__content:active),
|
||||
:deep(.is-current > div:first-child),
|
||||
:deep(.el-tree-node__content:focus) {
|
||||
background-color: mix(#fff, $--color-primary, 90%);
|
||||
color: $--color-primary;
|
||||
}
|
||||
</style>
|
||||
@@ -22,10 +22,10 @@ const url = computed(() => props.src)
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 300);
|
||||
loading.value = false
|
||||
}, 300)
|
||||
window.onresize = function temp() {
|
||||
height.value = document.documentElement.clientHeight - 94.5 + "px;";
|
||||
};
|
||||
height.value = document.documentElement.clientHeight - 94.5 + "px;"
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -2,65 +2,64 @@
|
||||
* v-copyText 复制文本内容
|
||||
* Copyright (c) 2022 ruoyi
|
||||
*/
|
||||
|
||||
export default {
|
||||
beforeMount(el, { value, arg }) {
|
||||
if (arg === "callback") {
|
||||
el.$copyCallback = value;
|
||||
el.$copyCallback = value
|
||||
} else {
|
||||
el.$copyValue = value;
|
||||
el.$copyValue = value
|
||||
const handler = () => {
|
||||
copyTextToClipboard(el.$copyValue);
|
||||
copyTextToClipboard(el.$copyValue)
|
||||
if (el.$copyCallback) {
|
||||
el.$copyCallback(el.$copyValue);
|
||||
el.$copyCallback(el.$copyValue)
|
||||
}
|
||||
};
|
||||
el.addEventListener("click", handler);
|
||||
el.$destroyCopy = () => el.removeEventListener("click", handler);
|
||||
}
|
||||
el.addEventListener("click", handler)
|
||||
el.$destroyCopy = () => el.removeEventListener("click", handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function copyTextToClipboard(input, { target = document.body } = {}) {
|
||||
const element = document.createElement('textarea');
|
||||
const previouslyFocusedElement = document.activeElement;
|
||||
const element = document.createElement('textarea')
|
||||
const previouslyFocusedElement = document.activeElement
|
||||
|
||||
element.value = input;
|
||||
element.value = input
|
||||
|
||||
// Prevent keyboard from showing on mobile
|
||||
element.setAttribute('readonly', '');
|
||||
element.setAttribute('readonly', '')
|
||||
|
||||
element.style.contain = 'strict';
|
||||
element.style.position = 'absolute';
|
||||
element.style.left = '-9999px';
|
||||
element.style.fontSize = '12pt'; // Prevent zooming on iOS
|
||||
element.style.contain = 'strict'
|
||||
element.style.position = 'absolute'
|
||||
element.style.left = '-9999px'
|
||||
element.style.fontSize = '12pt' // Prevent zooming on iOS
|
||||
|
||||
const selection = document.getSelection();
|
||||
const originalRange = selection.rangeCount > 0 && selection.getRangeAt(0);
|
||||
const selection = document.getSelection()
|
||||
const originalRange = selection.rangeCount > 0 && selection.getRangeAt(0)
|
||||
|
||||
target.append(element);
|
||||
element.select();
|
||||
target.append(element)
|
||||
element.select()
|
||||
|
||||
// Explicit selection workaround for iOS
|
||||
element.selectionStart = 0;
|
||||
element.selectionEnd = input.length;
|
||||
element.selectionStart = 0
|
||||
element.selectionEnd = input.length
|
||||
|
||||
let isSuccess = false;
|
||||
let isSuccess = false
|
||||
try {
|
||||
isSuccess = document.execCommand('copy');
|
||||
isSuccess = document.execCommand('copy')
|
||||
} catch { }
|
||||
|
||||
element.remove();
|
||||
element.remove()
|
||||
|
||||
if (originalRange) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(originalRange);
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(originalRange)
|
||||
}
|
||||
|
||||
// Get the focus back on the previously focused element, if any
|
||||
if (previouslyFocusedElement) {
|
||||
previouslyFocusedElement.focus();
|
||||
previouslyFocusedElement.focus()
|
||||
}
|
||||
|
||||
return isSuccess;
|
||||
return isSuccess
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
* v-hasPermi 操作权限处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
export default {
|
||||
mounted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
const all_permission = "*:*:*";
|
||||
const all_permission = "*:*:*"
|
||||
const permissions = useUserStore().permissions
|
||||
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
* v-hasRole 角色权限处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
export default {
|
||||
mounted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
const super_admin = "admin";
|
||||
const super_admin = "admin"
|
||||
const roles = useUserStore().roles
|
||||
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
|
||||
@@ -8,14 +8,31 @@
|
||||
</transition>
|
||||
</router-view>
|
||||
<iframe-toggle />
|
||||
<copyright />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import copyright from "./Copyright/index"
|
||||
import iframeToggle from "./IframeToggle/index"
|
||||
import useTagsViewStore from '@/store/modules/tagsView'
|
||||
|
||||
const route = useRoute()
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
|
||||
onMounted(() => {
|
||||
addIframe()
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
addIframe()
|
||||
})
|
||||
|
||||
function addIframe() {
|
||||
if (route.meta.link) {
|
||||
useTagsViewStore().addIframeView(route)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -28,7 +45,18 @@ const tagsViewStore = useTagsViewStore()
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 50px;
|
||||
overflow-y: auto;
|
||||
scrollbar-gutter: auto;
|
||||
height: calc(100vh - 50px);
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.app-main:has(.copyright) {
|
||||
padding-bottom: 36px;
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
@@ -38,19 +66,14 @@ const tagsViewStore = useTagsViewStore()
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 84px;
|
||||
margin-top: 84px;
|
||||
height: calc(100vh - 84px);
|
||||
min-height: 0px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
// fix css style bug in open el-dialog
|
||||
.el-popup-parent--hidden {
|
||||
.fixed-header {
|
||||
padding-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
@@ -65,4 +88,3 @@ const tagsViewStore = useTagsViewStore()
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
31
src/layout/components/Copyright/index.vue
Normal file
31
src/layout/components/Copyright/index.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<footer v-if="visible" class="copyright">
|
||||
<span>{{ content }}</span>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const visible = computed(() => settingsStore.footerVisible)
|
||||
const content = computed(() => settingsStore.footerContent)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.copyright {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 36px;
|
||||
padding: 10px 20px;
|
||||
text-align: right;
|
||||
background-color: #f8f8f8;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
border-top: 1px solid #e7e7e7;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
@@ -9,17 +9,17 @@
|
||||
</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 route = useRoute()
|
||||
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;
|
||||
let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&")
|
||||
return url + "?" + params
|
||||
}
|
||||
return url;
|
||||
return url
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div :style="'height:' + height">
|
||||
<div :style="'height:' + height" v-loading="loading" element-loading-text="正在加载页面,请稍候!">
|
||||
<iframe
|
||||
:id="iframeId"
|
||||
style="width: 100%; height: 100%"
|
||||
:src="src"
|
||||
ref="iframeRef"
|
||||
frameborder="no"
|
||||
></iframe>
|
||||
</div>
|
||||
@@ -18,7 +19,17 @@ const props = defineProps({
|
||||
iframeId: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + "px");
|
||||
const loading = ref(true)
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + 'px')
|
||||
const iframeRef = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
if (iframeRef.value) {
|
||||
iframeRef.value.onload = () => {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
|
||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
|
||||
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
|
||||
|
||||
<div class="right-menu">
|
||||
<template v-if="appStore.device !== 'mobile'">
|
||||
@@ -18,31 +18,37 @@
|
||||
|
||||
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||
|
||||
<el-tooltip content="主题模式" effect="dark" placement="bottom">
|
||||
<div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme">
|
||||
<svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
|
||||
<svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<div class="avatar-container">
|
||||
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="userStore.avatar" class="user-avatar" />
|
||||
<el-icon><caret-bottom /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/user/profile">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
||||
|
||||
<el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="userStore.avatar" class="user-avatar" />
|
||||
<span class="user-nickname"> {{ userStore.nickName }} </span>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/user/profile">
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
||||
<span>布局设置</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logout">
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-dropdown-item divided command="logout">
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -72,13 +78,13 @@ function toggleSideBar() {
|
||||
function handleCommand(command) {
|
||||
switch (command) {
|
||||
case "setLayout":
|
||||
setLayout();
|
||||
break;
|
||||
setLayout()
|
||||
break
|
||||
case "logout":
|
||||
logout();
|
||||
break;
|
||||
logout()
|
||||
break
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,14 +95,18 @@ function logout() {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
userStore.logOut().then(() => {
|
||||
location.href = '/index';
|
||||
location.href = '/index'
|
||||
})
|
||||
}).catch(() => { });
|
||||
}).catch(() => { })
|
||||
}
|
||||
|
||||
const emits = defineEmits(['setLayout'])
|
||||
function setLayout() {
|
||||
emits('setLayout');
|
||||
emits('setLayout')
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
settingsStore.toggleTheme()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -105,7 +115,7 @@ function setLayout() {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
background: var(--navbar-bg);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
|
||||
.hamburger-container {
|
||||
@@ -161,20 +171,44 @@ function setLayout() {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
|
||||
&.theme-switch-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
transition: transform 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
margin-right: 40px;
|
||||
margin-right: 0px;
|
||||
padding-right: 0px;
|
||||
|
||||
.avatar-wrapper {
|
||||
margin-top: 5px;
|
||||
margin-top: 10px;
|
||||
right: 8px;
|
||||
position: relative;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-right: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-nickname{
|
||||
position: relative;
|
||||
left: 0px;
|
||||
bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
i {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px">
|
||||
<el-drawer v-model="showSettings" :withHeader="false" :lock-scroll="false" direction="rtl" size="300px">
|
||||
<div class="setting-drawer-title">
|
||||
<h3 class="drawer-title">主题风格设置</h3>
|
||||
</div>
|
||||
@@ -49,6 +49,13 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>显示页签图标</span>
|
||||
<span class="comp-style">
|
||||
<el-switch v-model="settingsStore.tagsIcon" :disabled="!settingsStore.tagsView" class="drawer-switch" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>固定 Header</span>
|
||||
<span class="comp-style">
|
||||
@@ -66,7 +73,14 @@
|
||||
<div class="drawer-item">
|
||||
<span>动态标题</span>
|
||||
<span class="comp-style">
|
||||
<el-switch v-model="settingsStore.dynamicTitle" class="drawer-switch" />
|
||||
<el-switch v-model="settingsStore.dynamicTitle" @change="dynamicTitleChange" class="drawer-switch" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>底部版权</span>
|
||||
<span class="comp-style">
|
||||
<el-switch v-model="settingsStore.footerVisible" class="drawer-switch" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -79,79 +93,88 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import axios from 'axios'
|
||||
import { ElLoading, ElMessage } from 'element-plus'
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { proxy } = getCurrentInstance()
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
const showSettings = ref(false);
|
||||
const theme = ref(settingsStore.theme);
|
||||
const sideTheme = ref(settingsStore.sideTheme);
|
||||
const storeSettings = computed(() => settingsStore);
|
||||
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
|
||||
const showSettings = ref(false)
|
||||
const theme = ref(settingsStore.theme)
|
||||
const sideTheme = ref(settingsStore.sideTheme)
|
||||
const storeSettings = computed(() => settingsStore)
|
||||
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"])
|
||||
|
||||
/** 是否需要topnav */
|
||||
function topNavChange(val) {
|
||||
if (!val) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
|
||||
appStore.toggleSideBarHide(false)
|
||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes)
|
||||
}
|
||||
}
|
||||
|
||||
/** 是否需要dynamicTitle */
|
||||
function dynamicTitleChange() {
|
||||
useSettingsStore().setTitle(useSettingsStore().title)
|
||||
}
|
||||
|
||||
function themeChange(val) {
|
||||
settingsStore.theme = val;
|
||||
handleThemeStyle(val);
|
||||
settingsStore.theme = val
|
||||
handleThemeStyle(val)
|
||||
}
|
||||
|
||||
function handleTheme(val) {
|
||||
settingsStore.sideTheme = val;
|
||||
sideTheme.value = val;
|
||||
settingsStore.sideTheme = val
|
||||
sideTheme.value = val
|
||||
}
|
||||
|
||||
function saveSetting() {
|
||||
proxy.$modal.loading("正在保存到本地,请稍候...");
|
||||
proxy.$modal.loading("正在保存到本地,请稍候...")
|
||||
let layoutSetting = {
|
||||
"topNav": storeSettings.value.topNav,
|
||||
"tagsView": storeSettings.value.tagsView,
|
||||
"tagsIcon": storeSettings.value.tagsIcon,
|
||||
"fixedHeader": storeSettings.value.fixedHeader,
|
||||
"sidebarLogo": storeSettings.value.sidebarLogo,
|
||||
"dynamicTitle": storeSettings.value.dynamicTitle,
|
||||
"footerVisible": storeSettings.value.footerVisible,
|
||||
"sideTheme": storeSettings.value.sideTheme,
|
||||
"theme": storeSettings.value.theme
|
||||
};
|
||||
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
|
||||
}
|
||||
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting))
|
||||
setTimeout(proxy.$modal.closeLoading(), 1000)
|
||||
}
|
||||
|
||||
function resetSetting() {
|
||||
proxy.$modal.loading("正在清除设置缓存并刷新,请稍候...");
|
||||
proxy.$modal.loading("正在清除设置缓存并刷新,请稍候...")
|
||||
localStorage.removeItem("layout-setting")
|
||||
setTimeout("window.location.reload()", 1000)
|
||||
}
|
||||
|
||||
function openSetting() {
|
||||
showSettings.value = true;
|
||||
showSettings.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openSetting,
|
||||
openSetting
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.setting-drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
color: var(--el-text-color-primary, rgba(0, 0, 0, 0.85));
|
||||
line-height: 22px;
|
||||
font-weight: bold;
|
||||
|
||||
.drawer-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-drawer-block-checbox {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
@@ -170,13 +193,6 @@ defineExpose({
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.custom-img {
|
||||
width: 48px;
|
||||
height: 38px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 1px 1px 2px #898484;
|
||||
}
|
||||
|
||||
.setting-drawer-block-checbox-selectIcon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -193,7 +209,7 @@ defineExpose({
|
||||
}
|
||||
|
||||
.drawer-item {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
color: var(--el-text-color-regular, rgba(0, 0, 0, 0.65));
|
||||
padding: 12px 0;
|
||||
font-size: 14px;
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
|
||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
|
||||
<h1 class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import logo from '@/assets/logo/logo.png'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
|
||||
defineProps({
|
||||
collapse: {
|
||||
@@ -25,9 +25,25 @@ defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const title = import.meta.env.VITE_APP_TITLE;
|
||||
const settingsStore = useSettingsStore();
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
const title = import.meta.env.VITE_APP_TITLE
|
||||
const settingsStore = useSettingsStore()
|
||||
const sideTheme = computed(() => settingsStore.sideTheme)
|
||||
|
||||
// 获取Logo背景色
|
||||
const getLogoBackground = computed(() => {
|
||||
if (settingsStore.isDark) {
|
||||
return 'var(--sidebar-bg)'
|
||||
}
|
||||
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg
|
||||
})
|
||||
|
||||
// 获取Logo文字颜色
|
||||
const getLogoTextColor = computed(() => {
|
||||
if (settingsStore.isDark) {
|
||||
return 'var(--sidebar-text)'
|
||||
}
|
||||
return sideTheme.value === 'theme-dark' ? '#fff' : variables.menuLightText
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -45,7 +61,7 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
background: #2b2f3a;
|
||||
background: v-bind(getLogoBackground);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -63,7 +79,7 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
& .sidebar-title {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
color: v-bind(getLogoTextColor);
|
||||
font-weight: 600;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -48,20 +48,18 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const onlyOneChild = ref({});
|
||||
const onlyOneChild = ref({})
|
||||
|
||||
function hasOneShowingChild(children = [], parent) {
|
||||
if (!children) {
|
||||
children = [];
|
||||
children = []
|
||||
}
|
||||
const showingChildren = children.filter(item => {
|
||||
if (item.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item
|
||||
return true
|
||||
}
|
||||
onlyOneChild.value = item
|
||||
return true
|
||||
})
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
@@ -76,7 +74,7 @@ function hasOneShowingChild(children = [], parent) {
|
||||
}
|
||||
|
||||
return false
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePath(routePath, routeQuery) {
|
||||
if (isExternal(routePath)) {
|
||||
@@ -86,7 +84,7 @@ function resolvePath(routePath, routeQuery) {
|
||||
return props.basePath
|
||||
}
|
||||
if (routeQuery) {
|
||||
let query = JSON.parse(routeQuery);
|
||||
let query = JSON.parse(routeQuery)
|
||||
return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
|
||||
}
|
||||
return getNormalPath(props.basePath + '/' + routePath)
|
||||
@@ -94,9 +92,9 @@ function resolvePath(routePath, routeQuery) {
|
||||
|
||||
function hasTitle(title){
|
||||
if (title.length > 5) {
|
||||
return title;
|
||||
return title
|
||||
} else {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<template>
|
||||
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<div :class="{ 'has-logo': showLogo }" class="sidebar-container">
|
||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:collapse="isCollapse"
|
||||
:background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
|
||||
:text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
|
||||
:background-color="getMenuBackground"
|
||||
:text-color="getMenuTextColor"
|
||||
:unique-opened="true"
|
||||
:active-text-color="theme"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
:class="sideTheme"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="(route, index) in sidebarRouters"
|
||||
@@ -31,24 +32,73 @@ import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
const route = useRoute();
|
||||
const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const sidebarRouters = computed(() => permissionStore.sidebarRouters);
|
||||
const showLogo = computed(() => settingsStore.sidebarLogo);
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const isCollapse = computed(() => !appStore.sidebar.opened);
|
||||
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
|
||||
const showLogo = computed(() => settingsStore.sidebarLogo)
|
||||
const sideTheme = computed(() => settingsStore.sideTheme)
|
||||
const theme = computed(() => settingsStore.theme)
|
||||
const isCollapse = computed(() => !appStore.sidebar.opened)
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
// 获取菜单背景色
|
||||
const getMenuBackground = computed(() => {
|
||||
if (settingsStore.isDark) {
|
||||
return 'var(--sidebar-bg)'
|
||||
}
|
||||
return path;
|
||||
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg
|
||||
})
|
||||
|
||||
// 获取菜单文字颜色
|
||||
const getMenuTextColor = computed(() => {
|
||||
if (settingsStore.isDark) {
|
||||
return 'var(--sidebar-text)'
|
||||
}
|
||||
return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText
|
||||
})
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
}
|
||||
return path
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sidebar-container {
|
||||
background-color: v-bind(getMenuBackground);
|
||||
|
||||
.scrollbar-wrapper {
|
||||
background-color: v-bind(getMenuBackground);
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
|
||||
.el-menu-item, .el-sub-menu__title {
|
||||
&:hover {
|
||||
background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
color: v-bind(getMenuTextColor);
|
||||
|
||||
&.is-active {
|
||||
color: var(--menu-active-text, #409eff);
|
||||
background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title {
|
||||
color: v-bind(getMenuTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,35 +12,37 @@
|
||||
<script setup>
|
||||
import useTagsViewStore from '@/store/modules/tagsView'
|
||||
|
||||
const tagAndTagSpacing = ref(4);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const tagAndTagSpacing = ref(4)
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const scrollWrapper = computed(() => proxy.$refs.scrollContainer.$refs.wrapRef);
|
||||
const scrollWrapper = computed(() => proxy.$refs.scrollContainer.$refs.wrapRef)
|
||||
|
||||
onMounted(() => {
|
||||
scrollWrapper.value.addEventListener('scroll', emitScroll, true)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
scrollWrapper.value.removeEventListener('scroll', emitScroll)
|
||||
})
|
||||
|
||||
function handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
const $scrollWrapper = scrollWrapper.value
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
}
|
||||
|
||||
const emits = defineEmits()
|
||||
const emitScroll = () => {
|
||||
emits('scroll')
|
||||
}
|
||||
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
const visitedViews = computed(() => tagsViewStore.visitedViews);
|
||||
const visitedViews = computed(() => tagsViewStore.visitedViews)
|
||||
|
||||
function moveToTarget(currentTag) {
|
||||
const $container = proxy.$refs.scrollContainer.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
const $scrollWrapper = scrollWrapper.value
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
@@ -56,17 +58,17 @@ function moveToTarget(currentTag) {
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
const tagListDom = document.getElementsByClassName('tags-view-item');
|
||||
const tagListDom = document.getElementsByClassName('tags-view-item')
|
||||
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
|
||||
let prevTag = null
|
||||
let nextTag = null
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
|
||||
prevTag = tagListDom[k];
|
||||
prevTag = tagListDom[k]
|
||||
}
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
|
||||
nextTag = tagListDom[k];
|
||||
nextTag = tagListDom[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
v-for="tag in visitedViews"
|
||||
:key="tag.path"
|
||||
:data-path="tag.path"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
:class="{ 'active': isActive(tag), 'has-icon': tagsIcon }"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
class="tags-view-item"
|
||||
:style="activeStyle(tag)"
|
||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent="openMenu(tag, $event)"
|
||||
>
|
||||
<svg-icon v-if="tagsIcon && tag.meta && tag.meta.icon && tag.meta.icon !== '#'" :icon-class="tag.meta.icon" />
|
||||
{{ tag.title }}
|
||||
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
|
||||
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
|
||||
@@ -48,25 +49,27 @@ import useTagsViewStore from '@/store/modules/tagsView'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
const visible = ref(false);
|
||||
const top = ref(0);
|
||||
const left = ref(0);
|
||||
const selectedTag = ref({});
|
||||
const affixTags = ref([]);
|
||||
const scrollPaneRef = ref(null);
|
||||
const visible = ref(false)
|
||||
const top = ref(0)
|
||||
const left = ref(0)
|
||||
const selectedTag = ref({})
|
||||
const affixTags = ref([])
|
||||
const scrollPaneRef = ref(null)
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const visitedViews = computed(() => useTagsViewStore().visitedViews);
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
const theme = computed(() => useSettingsStore().theme);
|
||||
const visitedViews = computed(() => useTagsViewStore().visitedViews)
|
||||
const routes = computed(() => usePermissionStore().routes)
|
||||
const theme = computed(() => useSettingsStore().theme)
|
||||
const tagsIcon = computed(() => useSettingsStore().tagsIcon)
|
||||
|
||||
watch(route, () => {
|
||||
addTags()
|
||||
moveToCurrentTag()
|
||||
})
|
||||
|
||||
watch(visible, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', closeMenu)
|
||||
@@ -74,6 +77,7 @@ watch(visible, (value) => {
|
||||
document.body.removeEventListener('click', closeMenu)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
initTags()
|
||||
addTags()
|
||||
@@ -82,16 +86,19 @@ onMounted(() => {
|
||||
function isActive(r) {
|
||||
return r.path === route.path
|
||||
}
|
||||
|
||||
function activeStyle(tag) {
|
||||
if (!isActive(tag)) return {};
|
||||
if (!isActive(tag)) return {}
|
||||
return {
|
||||
"background-color": theme.value,
|
||||
"border-color": theme.value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function isAffix(tag) {
|
||||
return tag.meta && tag.meta.affix
|
||||
}
|
||||
|
||||
function isFirstView() {
|
||||
try {
|
||||
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath
|
||||
@@ -99,6 +106,7 @@ function isFirstView() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function isLastView() {
|
||||
try {
|
||||
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
|
||||
@@ -106,6 +114,7 @@ function isLastView() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function filterAffixTags(routes, basePath = '') {
|
||||
let tags = []
|
||||
routes.forEach(route => {
|
||||
@@ -127,9 +136,10 @@ function filterAffixTags(routes, basePath = '') {
|
||||
})
|
||||
return tags
|
||||
}
|
||||
|
||||
function initTags() {
|
||||
const res = filterAffixTags(routes.value);
|
||||
affixTags.value = res;
|
||||
const res = filterAffixTags(routes.value)
|
||||
affixTags.value = res
|
||||
for (const tag of res) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
@@ -137,21 +147,19 @@ function initTags() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addTags() {
|
||||
const { name } = route
|
||||
if (name) {
|
||||
useTagsViewStore().addView(route)
|
||||
if (route.meta.link) {
|
||||
useTagsViewStore().addIframeView(route);
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function moveToCurrentTag() {
|
||||
nextTick(() => {
|
||||
for (const r of visitedViews.value) {
|
||||
if (r.path === route.path) {
|
||||
scrollPaneRef.value.moveToTarget(r);
|
||||
scrollPaneRef.value.moveToTarget(r)
|
||||
// when query is different then update
|
||||
if (r.fullPath !== route.fullPath) {
|
||||
useTagsViewStore().updateVisitedView(route)
|
||||
@@ -160,12 +168,14 @@ function moveToCurrentTag() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function refreshSelectedTag(view) {
|
||||
proxy.$tab.refreshPage(view);
|
||||
proxy.$tab.refreshPage(view)
|
||||
if (route.meta.link) {
|
||||
useTagsViewStore().delIframeView(route);
|
||||
useTagsViewStore().delIframeView(route)
|
||||
}
|
||||
}
|
||||
|
||||
function closeSelectedTag(view) {
|
||||
proxy.$tab.closePage(view).then(({ visitedViews }) => {
|
||||
if (isActive(view)) {
|
||||
@@ -173,6 +183,7 @@ function closeSelectedTag(view) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function closeRightTags() {
|
||||
proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
|
||||
@@ -180,6 +191,7 @@ function closeRightTags() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function closeLeftTags() {
|
||||
proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
|
||||
@@ -187,12 +199,14 @@ function closeLeftTags() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function closeOthersTags() {
|
||||
router.push(selectedTag.value).catch(() => { });
|
||||
router.push(selectedTag.value).catch(() => { })
|
||||
proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
|
||||
moveToCurrentTag()
|
||||
})
|
||||
}
|
||||
|
||||
function closeAllTags(view) {
|
||||
proxy.$tab.closeAllPage().then(({ visitedViews }) => {
|
||||
if (affixTags.value.some(tag => tag.path === route.path)) {
|
||||
@@ -201,6 +215,7 @@ function closeAllTags(view) {
|
||||
toLastView(visitedViews, view)
|
||||
})
|
||||
}
|
||||
|
||||
function toLastView(visitedViews, view) {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
if (latestView) {
|
||||
@@ -216,6 +231,7 @@ function toLastView(visitedViews, view) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openMenu(tag, e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left
|
||||
@@ -233,21 +249,24 @@ function openMenu(tag, e) {
|
||||
visible.value = true
|
||||
selectedTag.value = tag
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
closeMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
<style lang="scss" scoped>
|
||||
.tags-view-container {
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
background: var(--tags-bg, #fff);
|
||||
border-bottom: 1px solid var(--tags-item-border, #d8dce5);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
@@ -255,25 +274,29 @@ function handleScroll() {
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
border: 1px solid var(--tags-item-border, #d8dce5);
|
||||
color: var(--tags-item-text, #495060);
|
||||
background: var(--tags-item-bg, #fff);
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #42b983;
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
@@ -285,9 +308,14 @@ function handleScroll() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-view-item.active.has-icon::before {
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
background: var(--el-bg-color-overlay, #fff);
|
||||
z-index: 3000;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
@@ -295,14 +323,17 @@ function handleScroll() {
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
color: var(--tags-item-text, #333);
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
border: 1px solid var(--el-border-color-light, #e4e7ed);
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #eee;
|
||||
background: var(--tags-item-hover, #eee);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,15 +350,17 @@ function handleScroll() {
|
||||
vertical-align: 2px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||
transform-origin: 100% 50%;
|
||||
|
||||
&:before {
|
||||
transform: scale(0.6);
|
||||
transform: scale(.6);
|
||||
display: inline-block;
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #b4bccc;
|
||||
background-color: var(--tags-close-hover, #b4bccc);
|
||||
color: #fff;
|
||||
width: 12px !important;
|
||||
height: 12px !important;
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import Sidebar from './components/Sidebar/index.vue'
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
const sidebar = computed(() => useAppStore().sidebar);
|
||||
const device = computed(() => useAppStore().device);
|
||||
const needTagsView = computed(() => settingsStore.tagsView);
|
||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||
const theme = computed(() => settingsStore.theme)
|
||||
const sideTheme = computed(() => settingsStore.sideTheme)
|
||||
const sidebar = computed(() => useAppStore().sidebar)
|
||||
const device = computed(() => useAppStore().device)
|
||||
const needTagsView = computed(() => settingsStore.tagsView)
|
||||
const fixedHeader = computed(() => settingsStore.fixedHeader)
|
||||
|
||||
const classObj = computed(() => ({
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
@@ -37,13 +35,16 @@ const classObj = computed(() => ({
|
||||
mobile: device.value === 'mobile'
|
||||
}))
|
||||
|
||||
const { width, height } = useWindowSize();
|
||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||
const { width, height } = useWindowSize()
|
||||
const WIDTH = 992 // refer to Bootstrap's responsive design
|
||||
|
||||
watchEffect(() => {
|
||||
watch(() => device.value, () => {
|
||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (width.value - 1 < WIDTH) {
|
||||
useAppStore().toggleDevice('mobile')
|
||||
useAppStore().closeSideBar({ withoutAnimation: true })
|
||||
@@ -56,18 +57,18 @@ function handleClickOutside() {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
|
||||
const settingRef = ref(null);
|
||||
const settingRef = ref(null)
|
||||
function setLayout() {
|
||||
settingRef.value.openSetting();
|
||||
settingRef.value.openSetting()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/styles/mixin.scss";
|
||||
@import "@/assets/styles/variables.module.scss";
|
||||
@use "@/assets/styles/mixin.scss" as mix;
|
||||
@use "@/assets/styles/variables.module.scss" as vars;
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
@include mix.clearfix;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -78,6 +79,11 @@ function setLayout() {
|
||||
}
|
||||
}
|
||||
|
||||
.main-container:has(.fixed-header) {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
@@ -93,7 +99,7 @@ function setLayout() {
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$base-sidebar-width});
|
||||
width: calc(100% - #{vars.$base-sidebar-width});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import Cookies from 'js-cookie'
|
||||
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
import locale from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
import '@/assets/styles/index.scss' // global css
|
||||
@@ -25,6 +26,7 @@ import elementIcons from '@/components/SvgIcon/svgicon'
|
||||
import './permission' // permission control
|
||||
|
||||
import { useDict } from '@/utils/dict'
|
||||
import { getConfigKey } from "@/api/system/config"
|
||||
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
|
||||
|
||||
// 分页组件
|
||||
@@ -39,8 +41,6 @@ import FileUpload from "@/components/FileUpload"
|
||||
import ImageUpload from "@/components/ImageUpload"
|
||||
// 图片预览组件
|
||||
import ImagePreview from "@/components/ImagePreview"
|
||||
// 自定义树选择组件
|
||||
import TreeSelect from '@/components/TreeSelect'
|
||||
// 字典标签组件
|
||||
import DictTag from '@/components/DictTag'
|
||||
|
||||
@@ -53,13 +53,13 @@ app.config.globalProperties.parseTime = parseTime
|
||||
app.config.globalProperties.resetForm = resetForm
|
||||
app.config.globalProperties.handleTree = handleTree
|
||||
app.config.globalProperties.addDateRange = addDateRange
|
||||
app.config.globalProperties.getConfigKey = getConfigKey
|
||||
app.config.globalProperties.selectDictLabel = selectDictLabel
|
||||
app.config.globalProperties.selectDictLabels = selectDictLabels
|
||||
|
||||
// 全局组件挂载
|
||||
app.component('DictTag', DictTag)
|
||||
app.component('Pagination', Pagination)
|
||||
app.component('TreeSelect', TreeSelect)
|
||||
app.component('FileUpload', FileUpload)
|
||||
app.component('ImageUpload', ImageUpload)
|
||||
app.component('ImagePreview', ImagePreview)
|
||||
|
||||
@@ -3,15 +3,19 @@ import { ElMessage } from 'element-plus'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import { isHttp, isPathMatch } from '@/utils/validate'
|
||||
import { isRelogin } from '@/utils/request'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
NProgress.configure({ showSpinner: false });
|
||||
NProgress.configure({ showSpinner: false })
|
||||
|
||||
const whiteList = ['/login', '/register'];
|
||||
const whiteList = ['/login', '/register']
|
||||
|
||||
const isWhiteList = (path) => {
|
||||
return whiteList.some(pattern => isPathMatch(pattern, path))
|
||||
}
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start()
|
||||
@@ -21,7 +25,7 @@ router.beforeEach((to, from, next) => {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
NProgress.done()
|
||||
} else if (whiteList.indexOf(to.path) !== -1) {
|
||||
} else if (isWhiteList(to.path)) {
|
||||
next()
|
||||
} else {
|
||||
if (useUserStore().roles.length === 0) {
|
||||
@@ -50,7 +54,7 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
} else {
|
||||
// 没有token
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
if (isWhiteList(to.path)) {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
function authPermission(permission) {
|
||||
const all_permission = "*:*:*";
|
||||
const all_permission = "*:*:*"
|
||||
const permissions = useUserStore().permissions
|
||||
if (permission && permission.length > 0) {
|
||||
return permissions.some(v => {
|
||||
@@ -13,7 +13,7 @@ function authPermission(permission) {
|
||||
}
|
||||
|
||||
function authRole(role) {
|
||||
const super_admin = "admin";
|
||||
const super_admin = "admin"
|
||||
const roles = useUserStore().roles
|
||||
if (role && role.length > 0) {
|
||||
return roles.some(v => {
|
||||
@@ -27,7 +27,7 @@ function authRole(role) {
|
||||
export default {
|
||||
// 验证用户是否具备某权限
|
||||
hasPermi(permission) {
|
||||
return authPermission(permission);
|
||||
return authPermission(permission)
|
||||
},
|
||||
// 验证用户是否含有指定权限,只需包含其中一个
|
||||
hasPermiOr(permissions) {
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
},
|
||||
// 验证用户是否具备某角色
|
||||
hasRole(role) {
|
||||
return authRole(role);
|
||||
return authRole(role)
|
||||
},
|
||||
// 验证用户是否含有指定角色,只需包含其中一个
|
||||
hasRoleOr(roles) {
|
||||
|
||||
@@ -26,9 +26,10 @@ const sessionCache = {
|
||||
if (value != null) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
return null
|
||||
},
|
||||
remove (key) {
|
||||
sessionStorage.removeItem(key);
|
||||
sessionStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
const localCache = {
|
||||
@@ -59,9 +60,10 @@ const localCache = {
|
||||
if (value != null) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
return null
|
||||
},
|
||||
remove (key) {
|
||||
localStorage.removeItem(key);
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import errorCode from '@/utils/errorCode'
|
||||
import { blobValidate } from '@/utils/ruoyi'
|
||||
|
||||
const baseURL = import.meta.env.VITE_APP_BASE_API
|
||||
let downloadLoadingInstance;
|
||||
let downloadLoadingInstance
|
||||
|
||||
export default {
|
||||
name(name, isDelete = true) {
|
||||
@@ -17,29 +17,29 @@ export default {
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
const isBlob = blobValidate(res.data)
|
||||
if (isBlob) {
|
||||
const blob = new Blob([res.data])
|
||||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
|
||||
} else {
|
||||
this.printErrMsg(res.data);
|
||||
this.printErrMsg(res.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
resource(resource) {
|
||||
var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource);
|
||||
var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource)
|
||||
axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
const isBlob = blobValidate(res.data)
|
||||
if (isBlob) {
|
||||
const blob = new Blob([res.data])
|
||||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
|
||||
} else {
|
||||
this.printErrMsg(res.data);
|
||||
this.printErrMsg(res.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -52,28 +52,28 @@ export default {
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
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);
|
||||
this.printErrMsg(res.data)
|
||||
}
|
||||
downloadLoadingInstance.close();
|
||||
downloadLoadingInstance.close()
|
||||
}).catch((r) => {
|
||||
console.error(r)
|
||||
ElMessage.error('下载文件出现错误,请联系管理员!')
|
||||
downloadLoadingInstance.close();
|
||||
downloadLoadingInstance.close()
|
||||
})
|
||||
},
|
||||
saveAs(text, name, opts) {
|
||||
saveAs(text, name, opts);
|
||||
saveAs(text, name, opts)
|
||||
},
|
||||
async printErrMsg(data) {
|
||||
const resText = await data.text();
|
||||
const rspObj = JSON.parse(resText);
|
||||
const resText = await data.text()
|
||||
const rspObj = JSON.parse(resText)
|
||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||
ElMessage.error(errMsg);
|
||||
ElMessage.error(errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
|
||||
|
||||
let loadingInstance;
|
||||
let loadingInstance
|
||||
|
||||
export default {
|
||||
// 消息提示
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
},
|
||||
// 错误通知
|
||||
notifyError(content) {
|
||||
ElNotification.error(content);
|
||||
ElNotification.error(content)
|
||||
},
|
||||
// 成功通知
|
||||
notifySuccess(content) {
|
||||
@@ -77,6 +77,6 @@ export default {
|
||||
},
|
||||
// 关闭遮罩层
|
||||
closeLoading() {
|
||||
loadingInstance.close();
|
||||
loadingInstance.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ import router from '@/router'
|
||||
export default {
|
||||
// 刷新当前tab页签
|
||||
refreshPage(obj) {
|
||||
const { path, query, matched } = router.currentRoute.value;
|
||||
const { path, query, matched } = router.currentRoute.value
|
||||
if (obj === undefined) {
|
||||
matched.forEach((m) => {
|
||||
if (m.components && m.components.default && m.components.default.name) {
|
||||
if (!['Layout', 'ParentView'].includes(m.components.default.name)) {
|
||||
obj = { name: m.components.default.name, path: path, query: query };
|
||||
obj = { name: m.components.default.name, path: path, query: query }
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
return useTagsViewStore().delCachedView(obj).then(() => {
|
||||
const { path, query } = obj
|
||||
@@ -24,9 +24,9 @@ export default {
|
||||
},
|
||||
// 关闭当前tab页签,打开新页签
|
||||
closeOpenPage(obj) {
|
||||
useTagsViewStore().delView(router.currentRoute.value);
|
||||
useTagsViewStore().delView(router.currentRoute.value)
|
||||
if (obj !== undefined) {
|
||||
return router.push(obj);
|
||||
return router.push(obj)
|
||||
}
|
||||
},
|
||||
// 关闭指定tab页签
|
||||
@@ -37,33 +37,35 @@ export default {
|
||||
if (latestView) {
|
||||
return router.push(latestView.fullPath)
|
||||
}
|
||||
return router.push('/');
|
||||
});
|
||||
return router.push('/')
|
||||
})
|
||||
}
|
||||
return useTagsViewStore().delView(obj);
|
||||
return useTagsViewStore().delView(obj)
|
||||
},
|
||||
// 关闭所有tab页签
|
||||
closeAllPage() {
|
||||
return useTagsViewStore().delAllViews();
|
||||
return useTagsViewStore().delAllViews()
|
||||
},
|
||||
// 关闭左侧tab页签
|
||||
closeLeftPage(obj) {
|
||||
return useTagsViewStore().delLeftTags(obj || router.currentRoute.value);
|
||||
return useTagsViewStore().delLeftTags(obj || router.currentRoute.value)
|
||||
},
|
||||
// 关闭右侧tab页签
|
||||
closeRightPage(obj) {
|
||||
return useTagsViewStore().delRightTags(obj || router.currentRoute.value);
|
||||
return useTagsViewStore().delRightTags(obj || router.currentRoute.value)
|
||||
},
|
||||
// 关闭其他tab页签
|
||||
closeOtherPage(obj) {
|
||||
return useTagsViewStore().delOthersViews(obj || router.currentRoute.value);
|
||||
return useTagsViewStore().delOthersViews(obj || router.currentRoute.value)
|
||||
},
|
||||
// 打开tab页签
|
||||
openPage(url) {
|
||||
return router.push(url);
|
||||
openPage(title, url, params) {
|
||||
const obj = { path: url, meta: { title: title } }
|
||||
useTagsViewStore().addView(obj)
|
||||
return router.push({ path: url, query: params })
|
||||
},
|
||||
// 修改tab页签
|
||||
updatePage(obj) {
|
||||
return useTagsViewStore().updateVisitedView(obj);
|
||||
return useTagsViewStore().updateVisitedView(obj)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export const constantRoutes = [
|
||||
redirect: 'noredirect',
|
||||
children: [
|
||||
{
|
||||
path: 'profile',
|
||||
path: 'profile/:activeTab?',
|
||||
component: () => import('@/views/system/user/profile/index'),
|
||||
name: 'Profile',
|
||||
meta: { title: '个人中心', icon: 'user' }
|
||||
@@ -166,10 +166,9 @@ const router = createRouter({
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return { top: 0 }
|
||||
}
|
||||
return { top: 0 }
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
export default router;
|
||||
export default router
|
||||
|
||||
@@ -3,10 +3,12 @@ export default {
|
||||
* 网页标题
|
||||
*/
|
||||
title: import.meta.env.VITE_APP_TITLE,
|
||||
|
||||
/**
|
||||
* 侧边栏主题 深色主题theme-dark,浅色主题theme-light
|
||||
*/
|
||||
sideTheme: 'theme-dark',
|
||||
|
||||
/**
|
||||
* 是否系统布局配置
|
||||
*/
|
||||
@@ -21,6 +23,11 @@ export default {
|
||||
* 是否显示 tagsView
|
||||
*/
|
||||
tagsView: true,
|
||||
|
||||
/**
|
||||
* 显示页签图标
|
||||
*/
|
||||
tagsIcon: false,
|
||||
|
||||
/**
|
||||
* 是否固定头部
|
||||
@@ -38,10 +45,13 @@ export default {
|
||||
dynamicTitle: false,
|
||||
|
||||
/**
|
||||
* @type {string | array} 'production' | ['production', 'development']
|
||||
* @description Need show err logs component.
|
||||
* The default is only used in the production env
|
||||
* If you want to also use it in dev, you can pass ['production', 'development']
|
||||
* 是否显示底部版权
|
||||
*/
|
||||
errorLog: 'production'
|
||||
footerVisible: false,
|
||||
|
||||
/**
|
||||
* 底部版权文本内容
|
||||
*/
|
||||
footerContent: 'Copyright © 2018-2025 RuoYi. All Rights Reserved.'
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const useAppStore = defineStore(
|
||||
actions: {
|
||||
toggleSideBar(withoutAnimation) {
|
||||
if (this.sidebar.hide) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
this.sidebar.opened = !this.sidebar.opened
|
||||
this.sidebar.withoutAnimation = withoutAnimation
|
||||
@@ -34,7 +34,7 @@ const useAppStore = defineStore(
|
||||
this.device = device
|
||||
},
|
||||
setSize(size) {
|
||||
this.size = size;
|
||||
this.size = size
|
||||
Cookies.set('size', size)
|
||||
},
|
||||
toggleSideBarHide(status) {
|
||||
|
||||
@@ -8,16 +8,16 @@ const useDictStore = defineStore(
|
||||
// 获取字典
|
||||
getDict(_key) {
|
||||
if (_key == null && _key == "") {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
try {
|
||||
for (let i = 0; i < this.dict.length; i++) {
|
||||
if (this.dict[i].key == _key) {
|
||||
return this.dict[i].value;
|
||||
return this.dict[i].value
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
},
|
||||
// 设置字典
|
||||
@@ -26,27 +26,27 @@ const useDictStore = defineStore(
|
||||
this.dict.push({
|
||||
key: _key,
|
||||
value: value
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
// 删除字典
|
||||
removeDict(_key) {
|
||||
var bln = false;
|
||||
var bln = false
|
||||
try {
|
||||
for (let i = 0; i < this.dict.length; i++) {
|
||||
if (this.dict[i].key == _key) {
|
||||
this.dict.splice(i, 1);
|
||||
return true;
|
||||
this.dict.splice(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
bln = false;
|
||||
bln = false
|
||||
}
|
||||
return bln;
|
||||
return bln
|
||||
},
|
||||
// 清空字典
|
||||
cleanDict() {
|
||||
this.dict = new Array();
|
||||
this.dict = new Array()
|
||||
},
|
||||
// 初始字典
|
||||
initDict() {
|
||||
|
||||
@@ -85,28 +85,13 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
||||
|
||||
function filterChildren(childrenMap, lastRouter = false) {
|
||||
var children = []
|
||||
childrenMap.forEach((el, index) => {
|
||||
if (el.children && el.children.length) {
|
||||
if (el.component === 'ParentView' && !lastRouter) {
|
||||
el.children.forEach(c => {
|
||||
c.path = el.path + '/' + c.path
|
||||
if (c.children && c.children.length) {
|
||||
children = children.concat(filterChildren(c.children, c))
|
||||
return
|
||||
}
|
||||
children.push(c)
|
||||
})
|
||||
return
|
||||
}
|
||||
childrenMap.forEach(el => {
|
||||
el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path
|
||||
if (el.children && el.children.length && el.component === 'ParentView') {
|
||||
children = children.concat(filterChildren(el.children, el))
|
||||
} else {
|
||||
children.push(el)
|
||||
}
|
||||
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)
|
||||
})
|
||||
return children
|
||||
}
|
||||
@@ -129,14 +114,14 @@ export function filterDynamicRoutes(routes) {
|
||||
}
|
||||
|
||||
export const loadView = (view) => {
|
||||
let res;
|
||||
let res
|
||||
for (const path in modules) {
|
||||
const dir = path.split('views/')[1].split('.vue')[0];
|
||||
const dir = path.split('views/')[1].split('.vue')[0]
|
||||
if (dir === view) {
|
||||
res = () => modules[path]();
|
||||
res = () => modules[path]()
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return res
|
||||
}
|
||||
|
||||
export default usePermissionStore
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import defaultSettings from '@/settings'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
|
||||
const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings
|
||||
const isDark = useDark()
|
||||
const toggleDark = useToggle(isDark)
|
||||
|
||||
const { sideTheme, showSettings, topNav, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
|
||||
|
||||
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
|
||||
|
||||
@@ -15,9 +19,13 @@ const useSettingsStore = defineStore(
|
||||
showSettings: showSettings,
|
||||
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
|
||||
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
|
||||
tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon,
|
||||
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
|
||||
sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
|
||||
dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle
|
||||
dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle,
|
||||
footerVisible: storageSetting.footerVisible === undefined ? footerVisible : storageSetting.footerVisible,
|
||||
footerContent: footerContent,
|
||||
isDark: isDark.value
|
||||
}),
|
||||
actions: {
|
||||
// 修改布局设置
|
||||
@@ -30,7 +38,12 @@ const useSettingsStore = defineStore(
|
||||
// 设置网页标题
|
||||
setTitle(title) {
|
||||
this.title = title
|
||||
useDynamicTitle();
|
||||
useDynamicTitle()
|
||||
},
|
||||
// 切换暗黑模式
|
||||
toggleTheme() {
|
||||
this.isDark = !this.isDark
|
||||
toggleDark()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import router from '@/router'
|
||||
import { ElMessageBox, } from 'element-plus'
|
||||
import { login, logout, getInfo } from '@/api/login'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
import { isHttp, isEmpty } from "@/utils/validate"
|
||||
import defAva from '@/assets/images/profile.jpg'
|
||||
|
||||
const useUserStore = defineStore(
|
||||
@@ -9,6 +12,7 @@ const useUserStore = defineStore(
|
||||
token: getToken(),
|
||||
id: '',
|
||||
name: '',
|
||||
nickName: '',
|
||||
avatar: '',
|
||||
roles: [],
|
||||
permissions: []
|
||||
@@ -35,8 +39,10 @@ const useUserStore = defineStore(
|
||||
return new Promise((resolve, reject) => {
|
||||
getInfo().then(res => {
|
||||
const user = res.user
|
||||
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
|
||||
|
||||
let avatar = user.avatar || ""
|
||||
if (!isHttp(avatar)) {
|
||||
avatar = (isEmpty(avatar)) ? defAva : import.meta.env.VITE_APP_BASE_API + avatar
|
||||
}
|
||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||
this.roles = res.roles
|
||||
this.permissions = res.permissions
|
||||
@@ -45,7 +51,20 @@ const useUserStore = defineStore(
|
||||
}
|
||||
this.id = user.userId
|
||||
this.name = user.userName
|
||||
this.nickName = user.nickName
|
||||
this.avatar = avatar
|
||||
/* 初始密码提示 */
|
||||
if(res.isDefaultModifyPwd) {
|
||||
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
|
||||
}).catch(() => {})
|
||||
}
|
||||
/* 过期密码提示 */
|
||||
if(!res.isDefaultModifyPwd && res.isPasswordExpired) {
|
||||
ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
|
||||
}).catch(() => {})
|
||||
}
|
||||
resolve(res)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
|
||||
@@ -5,20 +5,20 @@ import { getDicts } from '@/api/system/dict/data'
|
||||
* 获取字典数据
|
||||
*/
|
||||
export function useDict(...args) {
|
||||
const res = ref({});
|
||||
const res = ref({})
|
||||
return (() => {
|
||||
args.forEach((dictType, index) => {
|
||||
res.value[dictType] = [];
|
||||
const dicts = useDictStore().getDict(dictType);
|
||||
res.value[dictType] = []
|
||||
const dicts = useDictStore().getDict(dictType)
|
||||
if (dicts) {
|
||||
res.value[dictType] = dicts;
|
||||
res.value[dictType] = dicts
|
||||
} else {
|
||||
getDicts(dictType).then(resp => {
|
||||
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
|
||||
useDictStore().setDict(dictType, res.value[dictType]);
|
||||
useDictStore().setDict(dictType, res.value[dictType])
|
||||
})
|
||||
}
|
||||
})
|
||||
return toRefs(res.value);
|
||||
return toRefs(res.value)
|
||||
})()
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import store from '@/store'
|
||||
import defaultSettings from '@/settings'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
@@ -6,10 +5,10 @@ import useSettingsStore from '@/store/modules/settings'
|
||||
* 动态修改标题
|
||||
*/
|
||||
export function useDynamicTitle() {
|
||||
const settingsStore = useSettingsStore();
|
||||
const settingsStore = useSettingsStore()
|
||||
if (settingsStore.dynamicTitle) {
|
||||
document.title = settingsStore.title + ' - ' + defaultSettings.title;
|
||||
document.title = settingsStore.title + ' - ' + defaultSettings.title
|
||||
} else {
|
||||
document.title = defaultSettings.title;
|
||||
document.title = defaultSettings.title
|
||||
}
|
||||
}
|
||||
452
src/utils/generator/config.js
Normal file
452
src/utils/generator/config.js
Normal file
@@ -0,0 +1,452 @@
|
||||
export const formConf = {
|
||||
formRef: 'formRef',
|
||||
formModel: 'formData',
|
||||
size: 'default',
|
||||
labelPosition: 'right',
|
||||
labelWidth: 100,
|
||||
formRules: 'rules',
|
||||
gutter: 15,
|
||||
disabled: false,
|
||||
span: 24,
|
||||
formBtns: true,
|
||||
}
|
||||
|
||||
export const inputComponents = [
|
||||
{
|
||||
label: '单行文本',
|
||||
tag: 'el-input',
|
||||
tagIcon: 'input',
|
||||
type: 'text',
|
||||
placeholder: '请输入',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': '',
|
||||
'suffix-icon': '',
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/input',
|
||||
},
|
||||
{
|
||||
label: '多行文本',
|
||||
tag: 'el-input',
|
||||
tagIcon: 'textarea',
|
||||
type: 'textarea',
|
||||
placeholder: '请输入',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
autosize: {
|
||||
minRows: 4,
|
||||
maxRows: 4,
|
||||
},
|
||||
style: { width: '100%' },
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/input',
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
tag: 'el-input',
|
||||
tagIcon: 'password',
|
||||
type: 'password',
|
||||
placeholder: '请输入',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
'show-password': true,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': '',
|
||||
'suffix-icon': '',
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/input',
|
||||
},
|
||||
{
|
||||
label: '计数器',
|
||||
tag: 'el-input-number',
|
||||
tagIcon: 'number',
|
||||
placeholder: '',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
step: undefined,
|
||||
'step-strictly': false,
|
||||
precision: undefined,
|
||||
'controls-position': '',
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/input-number',
|
||||
},
|
||||
]
|
||||
|
||||
export const selectComponents = [
|
||||
{
|
||||
label: '下拉选择',
|
||||
tag: 'el-select',
|
||||
tagIcon: 'select',
|
||||
placeholder: '请选择',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
filterable: false,
|
||||
multiple: false,
|
||||
options: [
|
||||
{
|
||||
label: '选项一',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '选项二',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/select',
|
||||
},
|
||||
{
|
||||
label: '级联选择',
|
||||
tag: 'el-cascader',
|
||||
tagIcon: 'cascader',
|
||||
placeholder: '请选择',
|
||||
defaultValue: [],
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
props: {
|
||||
props: {
|
||||
multiple: false,
|
||||
},
|
||||
},
|
||||
'show-all-levels': true,
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
filterable: false,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
id: 1,
|
||||
value: 1,
|
||||
label: '选项1',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
value: 2,
|
||||
label: '选项1-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
dataType: 'dynamic',
|
||||
labelKey: 'label',
|
||||
valueKey: 'value',
|
||||
childrenKey: 'children',
|
||||
separator: '/',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/cascader',
|
||||
},
|
||||
{
|
||||
label: '单选框组',
|
||||
tag: 'el-radio-group',
|
||||
tagIcon: 'radio',
|
||||
defaultValue: 0,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
optionType: 'default',
|
||||
border: false,
|
||||
size: 'default',
|
||||
disabled: false,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项一',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '选项二',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/radio',
|
||||
},
|
||||
{
|
||||
label: '多选框组',
|
||||
tag: 'el-checkbox-group',
|
||||
tagIcon: 'checkbox',
|
||||
defaultValue: [],
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
optionType: 'default',
|
||||
border: false,
|
||||
size: 'default',
|
||||
disabled: false,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项一',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '选项二',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/checkbox',
|
||||
},
|
||||
{
|
||||
label: '开关',
|
||||
tag: 'el-switch',
|
||||
tagIcon: 'switch',
|
||||
defaultValue: false,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
disabled: false,
|
||||
required: true,
|
||||
'active-text': '',
|
||||
'inactive-text': '',
|
||||
'active-color': null,
|
||||
'inactive-color': null,
|
||||
'active-value': true,
|
||||
'inactive-value': false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/switch',
|
||||
},
|
||||
{
|
||||
label: '滑块',
|
||||
tag: 'el-slider',
|
||||
tagIcon: 'slider',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
disabled: false,
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
'show-stops': false,
|
||||
range: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/slider',
|
||||
},
|
||||
{
|
||||
label: '时间选择',
|
||||
tag: 'el-time-picker',
|
||||
tagIcon: 'time',
|
||||
placeholder: '请选择',
|
||||
defaultValue: '',
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
format: 'HH:mm:ss',
|
||||
'value-format': 'HH:mm:ss',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/time-picker',
|
||||
},
|
||||
{
|
||||
label: '时间范围',
|
||||
tag: 'el-time-picker',
|
||||
tagIcon: 'time-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
'is-range': true,
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始时间',
|
||||
'end-placeholder': '结束时间',
|
||||
format: 'HH:mm:ss',
|
||||
'value-format': 'HH:mm:ss',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/time-picker',
|
||||
},
|
||||
{
|
||||
label: '日期选择',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date',
|
||||
placeholder: '请选择',
|
||||
defaultValue: null,
|
||||
type: 'date',
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
'value-format': 'YYYY-MM-DD',
|
||||
readonly: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/date-picker',
|
||||
},
|
||||
{
|
||||
label: '日期范围',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
type: 'daterange',
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始日期',
|
||||
'end-placeholder': '结束日期',
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
'value-format': 'YYYY-MM-DD',
|
||||
readonly: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/date-picker',
|
||||
},
|
||||
{
|
||||
label: '评分',
|
||||
tag: 'el-rate',
|
||||
tagIcon: 'rate',
|
||||
defaultValue: 0,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
max: 5,
|
||||
'allow-half': false,
|
||||
'show-text': false,
|
||||
'show-score': false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/rate',
|
||||
},
|
||||
{
|
||||
label: '颜色选择',
|
||||
tag: 'el-color-picker',
|
||||
tagIcon: 'color',
|
||||
defaultValue: null,
|
||||
labelWidth: null,
|
||||
'show-alpha': false,
|
||||
'color-format': '',
|
||||
disabled: false,
|
||||
required: true,
|
||||
size: 'default',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/color-picker',
|
||||
},
|
||||
{
|
||||
label: '上传',
|
||||
tag: 'el-upload',
|
||||
tagIcon: 'upload',
|
||||
action: 'https://jsonplaceholder.typicode.com/posts/',
|
||||
defaultValue: null,
|
||||
labelWidth: null,
|
||||
disabled: false,
|
||||
required: true,
|
||||
accept: '',
|
||||
name: 'file',
|
||||
'auto-upload': true,
|
||||
showTip: false,
|
||||
buttonText: '点击上传',
|
||||
fileSize: 2,
|
||||
sizeUnit: 'MB',
|
||||
'list-type': 'text',
|
||||
multiple: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element-plus.org/zh-CN/component/upload',
|
||||
tip: '只能上传不超过 2MB 的文件',
|
||||
style: { width: '100%' },
|
||||
},
|
||||
]
|
||||
|
||||
export const layoutComponents = [
|
||||
{
|
||||
layout: 'rowFormItem',
|
||||
tagIcon: 'row',
|
||||
type: 'default',
|
||||
justify: 'start',
|
||||
align: 'top',
|
||||
label: '行容器',
|
||||
layoutTree: true,
|
||||
children: [],
|
||||
document: 'https://element-plus.org/zh-CN/component/layout',
|
||||
},
|
||||
{
|
||||
layout: 'colFormItem',
|
||||
label: '按钮',
|
||||
changeTag: true,
|
||||
labelWidth: null,
|
||||
tag: 'el-button',
|
||||
tagIcon: 'button',
|
||||
span: 24,
|
||||
default: '主要按钮',
|
||||
type: 'primary',
|
||||
icon: 'Search',
|
||||
size: 'default',
|
||||
disabled: false,
|
||||
document: 'https://element-plus.org/zh-CN/component/button',
|
||||
},
|
||||
]
|
||||
|
||||
// 组件rule的触发方式,无触发方式的组件不生成rule
|
||||
export const trigger = {
|
||||
'el-input': 'blur',
|
||||
'el-input-number': 'blur',
|
||||
'el-select': 'change',
|
||||
'el-radio-group': 'change',
|
||||
'el-checkbox-group': 'change',
|
||||
'el-cascader': 'change',
|
||||
'el-time-picker': 'change',
|
||||
'el-date-picker': 'change',
|
||||
'el-rate': 'change',
|
||||
}
|
||||
18
src/utils/generator/css.js
Normal file
18
src/utils/generator/css.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const styles = {
|
||||
'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
|
||||
'el-upload': '.el-upload__tip{line-height: 1.2;}'
|
||||
}
|
||||
|
||||
function addCss(cssList, el) {
|
||||
const css = styles[el.tag]
|
||||
css && cssList.indexOf(css) === -1 && cssList.push(css)
|
||||
if (el.children) {
|
||||
el.children.forEach(el2 => addCss(cssList, el2))
|
||||
}
|
||||
}
|
||||
|
||||
export function makeUpCss(conf) {
|
||||
const cssList = []
|
||||
conf.fields.forEach(el => addCss(cssList, el))
|
||||
return cssList.join('\n')
|
||||
}
|
||||
37
src/utils/generator/drawingDefault.js
Normal file
37
src/utils/generator/drawingDefault.js
Normal file
@@ -0,0 +1,37 @@
|
||||
export const drawingDefaultValue = []
|
||||
|
||||
export function initDrawingDefaultValue() {
|
||||
if (drawingDefaultValue.length === 0) {
|
||||
drawingDefaultValue.push({
|
||||
layout: 'colFormItem',
|
||||
tagIcon: 'input',
|
||||
label: '手机号',
|
||||
vModel: 'mobile',
|
||||
formId: 6,
|
||||
tag: 'el-input',
|
||||
placeholder: '请输入手机号',
|
||||
defaultValue: '',
|
||||
span: 24,
|
||||
style: {width: '100%'},
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': 'Cellphone',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
changeTag: true,
|
||||
regList: [{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function cleanDrawingDefaultValue() {
|
||||
drawingDefaultValue.splice(0, drawingDefaultValue.length)
|
||||
}
|
||||
359
src/utils/generator/html.js
Normal file
359
src/utils/generator/html.js
Normal file
@@ -0,0 +1,359 @@
|
||||
/* eslint-disable max-len */
|
||||
import { trigger } from './config'
|
||||
|
||||
let confGlobal
|
||||
let someSpanIsNot24
|
||||
|
||||
export function dialogWrapper(str) {
|
||||
return `<el-dialog v-model="dialogVisible" @open="onOpen" @close="onClose" title="Dialog Titile">
|
||||
${str}
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handelConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>`
|
||||
}
|
||||
|
||||
export function vueTemplate(str) {
|
||||
return `<template>
|
||||
<div class="app-container">
|
||||
${str}
|
||||
</div>
|
||||
</template>`
|
||||
}
|
||||
|
||||
export function vueScript(str) {
|
||||
return `<script setup>
|
||||
${str}
|
||||
</script>`
|
||||
}
|
||||
|
||||
export function cssStyle(cssStr) {
|
||||
return `<style>
|
||||
${cssStr}
|
||||
</style>`
|
||||
}
|
||||
|
||||
function buildFormTemplate(conf, child, type) {
|
||||
let labelPosition = ''
|
||||
if (conf.labelPosition !== 'right') {
|
||||
labelPosition = `label-position="${conf.labelPosition}"`
|
||||
}
|
||||
const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
|
||||
let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
|
||||
${child}
|
||||
${buildFromBtns(conf, type)}
|
||||
</el-form>`
|
||||
if (someSpanIsNot24) {
|
||||
str = `<el-row :gutter="${conf.gutter}">
|
||||
${str}
|
||||
</el-row>`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function buildFromBtns(conf, type) {
|
||||
let str = ''
|
||||
if (conf.formBtns && type === 'file') {
|
||||
str = `<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>`
|
||||
if (someSpanIsNot24) {
|
||||
str = `<el-col :span="24">
|
||||
${str}
|
||||
</el-col>`
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// span不为24的用el-col包裹
|
||||
function colWrapper(element, str) {
|
||||
if (someSpanIsNot24 || element.span !== 24) {
|
||||
return `<el-col :span="${element.span}">
|
||||
${str}
|
||||
</el-col>`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
const layouts = {
|
||||
colFormItem(element) {
|
||||
let labelWidth = ''
|
||||
if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
|
||||
labelWidth = `label-width="${element.labelWidth}px"`
|
||||
}
|
||||
const required = !trigger[element.tag] && element.required ? 'required' : ''
|
||||
const tagDom = tags[element.tag] ? tags[element.tag](element) : null
|
||||
let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}>
|
||||
${tagDom}
|
||||
</el-form-item>`
|
||||
str = colWrapper(element, str)
|
||||
return str
|
||||
},
|
||||
rowFormItem(element) {
|
||||
const type = element.type === 'default' ? '' : `type="${element.type}"`
|
||||
const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
|
||||
const align = element.type === 'default' ? '' : `align="${element.align}"`
|
||||
const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
|
||||
const children = element.children.map(el => layouts[el.layout](el))
|
||||
let str = `<el-row ${type} ${justify} ${align} ${gutter}>
|
||||
${children.join('\n')}
|
||||
</el-row>`
|
||||
str = colWrapper(element, str)
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
const tags = {
|
||||
'el-button': el => {
|
||||
const {
|
||||
tag, disabled
|
||||
} = attrBuilder(el)
|
||||
const type = el.type ? `type="${el.type}"` : ''
|
||||
const icon = el.icon ? `icon="${el.icon}"` : ''
|
||||
const size = el.size ? `size="${el.size}"` : ''
|
||||
let child = buildElButtonChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-input': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
|
||||
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
|
||||
const readonly = el.readonly ? 'readonly' : ''
|
||||
const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
|
||||
const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
|
||||
const showPassword = el['show-password'] ? 'show-password' : ''
|
||||
const type = el.type ? `type="${el.type}"` : ''
|
||||
const autosize = el.autosize && el.autosize.minRows
|
||||
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
|
||||
: ''
|
||||
let child = buildElInputChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-input-number': el => {
|
||||
const { disabled, vModel, placeholder } = attrBuilder(el)
|
||||
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
|
||||
const min = el.min ? `:min='${el.min}'` : ''
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const step = el.step ? `:step='${el.step}'` : ''
|
||||
const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
|
||||
const precision = el.precision ? `:precision='${el.precision}'` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-select': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const filterable = el.filterable ? 'filterable' : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
let child = buildElSelectChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-radio-group': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
let child = buildElRadioGroupChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-checkbox-group': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
const min = el.min ? `:min="${el.min}"` : ''
|
||||
const max = el.max ? `:max="${el.max}"` : ''
|
||||
let child = buildElCheckboxGroupChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-switch': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
|
||||
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
|
||||
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
|
||||
const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
|
||||
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
|
||||
const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-cascader': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const options = el.options ? `:options="${el.vModel}Options"` : ''
|
||||
const props = el.props ? `:props="${el.vModel}Props"` : ''
|
||||
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
|
||||
const filterable = el.filterable ? 'filterable' : ''
|
||||
const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
|
||||
|
||||
return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-slider': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const min = el.min ? `:min='${el.min}'` : ''
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const step = el.step ? `:step='${el.step}'` : ''
|
||||
const range = el.range ? 'range' : ''
|
||||
const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
|
||||
|
||||
return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-time-picker': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||
const isRange = el['is-range'] ? 'is-range' : ''
|
||||
const format = el.format ? `format="${el.format}"` : ''
|
||||
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-date-picker': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||
const format = el.format ? `format="${el.format}"` : ''
|
||||
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||
const type = el.type === 'date' ? '' : `type="${el.type}"`
|
||||
const readonly = el.readonly ? 'readonly' : ''
|
||||
|
||||
return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-rate': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const allowHalf = el['allow-half'] ? 'allow-half' : ''
|
||||
const showText = el['show-text'] ? 'show-text' : ''
|
||||
const showScore = el['show-score'] ? 'show-score' : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-color-picker': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
|
||||
const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-upload': el => {
|
||||
const disabled = el.disabled ? ':disabled=\'true\'' : ''
|
||||
const action = el.action ? `:action="${el.vModel}Action"` : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
|
||||
const accept = el.accept ? `accept="${el.accept}"` : ''
|
||||
const name = el.name !== 'file' ? `name="${el.name}"` : ''
|
||||
const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
|
||||
const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
|
||||
const fileList = `:file-list="${el.vModel}fileList"`
|
||||
const ref = `ref="${el.vModel}"`
|
||||
let child = buildElUploadChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`
|
||||
}
|
||||
}
|
||||
|
||||
function attrBuilder(el) {
|
||||
return {
|
||||
vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`,
|
||||
clearable: el.clearable ? 'clearable' : '',
|
||||
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
|
||||
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
|
||||
disabled: el.disabled ? ':disabled=\'true\'' : ''
|
||||
}
|
||||
}
|
||||
|
||||
// el-buttin 子级
|
||||
function buildElButtonChild(conf) {
|
||||
const children = []
|
||||
if (conf.default) {
|
||||
children.push(conf.default)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-input innerHTML
|
||||
function buildElInputChild(conf) {
|
||||
const children = []
|
||||
if (conf.prepend) {
|
||||
children.push(`<template slot="prepend">${conf.prepend}</template>`)
|
||||
}
|
||||
if (conf.append) {
|
||||
children.push(`<template slot="append">${conf.append}</template>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElSelectChild(conf) {
|
||||
const children = []
|
||||
if (conf.options && conf.options.length) {
|
||||
children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElRadioGroupChild(conf) {
|
||||
const children = []
|
||||
if (conf.options && conf.options.length) {
|
||||
const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
|
||||
const border = conf.border ? 'border' : ''
|
||||
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :value="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElCheckboxGroupChild(conf) {
|
||||
const children = []
|
||||
if (conf.options && conf.options.length) {
|
||||
const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
|
||||
const border = conf.border ? 'border' : ''
|
||||
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :value="item.label" :disabled="item.disabled" ${border} />`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElUploadChild(conf) {
|
||||
const list = []
|
||||
if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
|
||||
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`)
|
||||
if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
|
||||
return list.join('\n')
|
||||
}
|
||||
|
||||
export function makeUpHtml(conf, type) {
|
||||
const htmlList = []
|
||||
confGlobal = conf
|
||||
someSpanIsNot24 = conf.fields.some(item => item.span !== 24)
|
||||
conf.fields.forEach(el => {
|
||||
htmlList.push(layouts[el.layout](el))
|
||||
})
|
||||
const htmlStr = htmlList.join('\n')
|
||||
|
||||
let temp = buildFormTemplate(conf, htmlStr, type)
|
||||
if (type === 'dialog') {
|
||||
temp = dialogWrapper(temp)
|
||||
}
|
||||
confGlobal = null
|
||||
return temp
|
||||
}
|
||||
1
src/utils/generator/icon.json
Normal file
1
src/utils/generator/icon.json
Normal file
@@ -0,0 +1 @@
|
||||
["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"]
|
||||
370
src/utils/generator/js.js
Normal file
370
src/utils/generator/js.js
Normal file
@@ -0,0 +1,370 @@
|
||||
import { titleCase } from '@/utils/index'
|
||||
import { trigger } from './config'
|
||||
// 文件大小设置
|
||||
const units = {
|
||||
KB: '1024',
|
||||
MB: '1024 / 1024',
|
||||
GB: '1024 / 1024 / 1024',
|
||||
}
|
||||
/**
|
||||
* @name: 生成js需要的数据
|
||||
* @description: 生成js需要的数据
|
||||
* @param {*} conf
|
||||
* @param {*} type 弹窗或表单
|
||||
* @return {*}
|
||||
*/
|
||||
export function makeUpJs(conf, type) {
|
||||
conf = JSON.parse(JSON.stringify(conf))
|
||||
const dataList = []
|
||||
const ruleList = []
|
||||
const optionsList = []
|
||||
const propsList = []
|
||||
const methodList = []
|
||||
const uploadVarList = []
|
||||
|
||||
conf.fields.forEach((el) => {
|
||||
buildAttributes(
|
||||
el,
|
||||
dataList,
|
||||
ruleList,
|
||||
optionsList,
|
||||
methodList,
|
||||
propsList,
|
||||
uploadVarList
|
||||
)
|
||||
})
|
||||
|
||||
const script = buildexport(
|
||||
conf,
|
||||
type,
|
||||
dataList.join('\n'),
|
||||
ruleList.join('\n'),
|
||||
optionsList.join('\n'),
|
||||
uploadVarList.join('\n'),
|
||||
propsList.join('\n'),
|
||||
methodList.join('\n')
|
||||
)
|
||||
|
||||
return script
|
||||
}
|
||||
/**
|
||||
* @name: 生成参数
|
||||
* @description: 生成参数,包括表单数据表单验证数据,多选选项数据,上传数据等
|
||||
* @return {*}
|
||||
*/
|
||||
function buildAttributes(
|
||||
el,
|
||||
dataList,
|
||||
ruleList,
|
||||
optionsList,
|
||||
methodList,
|
||||
propsList,
|
||||
uploadVarList
|
||||
){
|
||||
buildData(el, dataList)
|
||||
buildRules(el, ruleList)
|
||||
|
||||
if (el.options && el.options.length) {
|
||||
buildOptions(el, optionsList)
|
||||
if (el.dataType === 'dynamic') {
|
||||
const model = `${el.vModel}Options`
|
||||
const options = titleCase(model)
|
||||
buildOptionMethod(`get${options}`, model, methodList)
|
||||
}
|
||||
}
|
||||
|
||||
if (el.props && el.props.props) {
|
||||
buildProps(el, propsList)
|
||||
}
|
||||
|
||||
if (el.action && el.tag === 'el-upload') {
|
||||
uploadVarList.push(
|
||||
`
|
||||
// 上传请求路径
|
||||
const ${el.vModel}Action = ref('${el.action}')
|
||||
// 上传文件列表
|
||||
const ${el.vModel}fileList = ref([])`
|
||||
)
|
||||
methodList.push(buildBeforeUpload(el))
|
||||
if (!el['auto-upload']) {
|
||||
methodList.push(buildSubmitUpload(el))
|
||||
}
|
||||
}
|
||||
|
||||
if (el.children) {
|
||||
el.children.forEach((el2) => {
|
||||
buildAttributes(
|
||||
el2,
|
||||
dataList,
|
||||
ruleList,
|
||||
optionsList,
|
||||
methodList,
|
||||
propsList,
|
||||
uploadVarList
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @name: 生成表单数据formData
|
||||
* @description: 生成表单数据formData
|
||||
* @param {*} conf
|
||||
* @param {*} dataList 数据列表
|
||||
* @return {*}
|
||||
*/
|
||||
function buildData(conf, dataList) {
|
||||
if (conf.vModel === undefined) return
|
||||
let defaultValue
|
||||
if (typeof conf.defaultValue === 'string' && !conf.multiple) {
|
||||
defaultValue = `'${conf.defaultValue}'`
|
||||
} else {
|
||||
defaultValue = `${JSON.stringify(conf.defaultValue)}`
|
||||
}
|
||||
dataList.push(`${conf.vModel}: ${defaultValue},`)
|
||||
}
|
||||
/**
|
||||
* @name: 生成表单验证数据rule
|
||||
* @description: 生成表单验证数据rule
|
||||
* @param {*} conf
|
||||
* @param {*} ruleList 验证数据列表
|
||||
* @return {*}
|
||||
*/
|
||||
function buildRules(conf, ruleList) {
|
||||
if (conf.vModel === undefined) return
|
||||
const rules = []
|
||||
if (trigger[conf.tag]) {
|
||||
if (conf.required) {
|
||||
const type = Array.isArray(conf.defaultValue) ? "type: 'array'," : ''
|
||||
let message = Array.isArray(conf.defaultValue)
|
||||
? `请至少选择一个${conf.vModel}`
|
||||
: conf.placeholder
|
||||
if (message === undefined) message = `${conf.label}不能为空`
|
||||
rules.push(
|
||||
`{ required: true, ${type} message: '${message}', trigger: '${
|
||||
trigger[conf.tag]
|
||||
}' }`
|
||||
)
|
||||
}
|
||||
if (conf.regList && Array.isArray(conf.regList)) {
|
||||
conf.regList.forEach((item) => {
|
||||
if (item.pattern) {
|
||||
rules.push(
|
||||
`{ pattern: new RegExp(${item.pattern}), message: '${
|
||||
item.message
|
||||
}', trigger: '${trigger[conf.tag]}' }`
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
ruleList.push(`${conf.vModel}: [${rules.join(',')}],`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @name: 生成选项数据
|
||||
* @description: 生成选项数据,单选多选下拉等
|
||||
* @param {*} conf
|
||||
* @param {*} optionsList 选项数据列表
|
||||
* @return {*}
|
||||
*/
|
||||
function buildOptions(conf, optionsList) {
|
||||
if (conf.vModel === undefined) return
|
||||
if (conf.dataType === 'dynamic') {
|
||||
conf.options = []
|
||||
}
|
||||
const str = `const ${conf.vModel}Options = ref(${JSON.stringify(conf.options)})`
|
||||
optionsList.push(str)
|
||||
}
|
||||
/**
|
||||
* @name: 生成方法
|
||||
* @description: 生成方法
|
||||
* @param {*} methodName 方法名
|
||||
* @param {*} model
|
||||
* @param {*} methodList 方法列表
|
||||
* @return {*}
|
||||
*/
|
||||
function buildOptionMethod(methodName, model, methodList) {
|
||||
const str = `function ${methodName}() {
|
||||
// TODO 发起请求获取数据
|
||||
${model}.value
|
||||
}`
|
||||
methodList.push(str)
|
||||
}
|
||||
/**
|
||||
* @name: 生成表单组件需要的props设置
|
||||
* @description: 生成表单组件需要的props设置,如;级联组件
|
||||
* @param {*} conf
|
||||
* @param {*} propsList
|
||||
* @return {*}
|
||||
*/
|
||||
function buildProps(conf, propsList) {
|
||||
if (conf.dataType === 'dynamic') {
|
||||
conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)
|
||||
conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)
|
||||
conf.childrenKey !== 'children' &&
|
||||
(conf.props.props.children = conf.childrenKey)
|
||||
}
|
||||
const str = `
|
||||
// props设置
|
||||
const ${conf.vModel}Props = ref(${JSON.stringify(conf.props.props)})`
|
||||
propsList.push(str)
|
||||
}
|
||||
/**
|
||||
* @name: 生成上传组件的相关内容
|
||||
* @description: 生成上传组件的相关内容
|
||||
* @param {*} conf
|
||||
* @return {*}
|
||||
*/
|
||||
function buildBeforeUpload(conf) {
|
||||
const unitNum = units[conf.sizeUnit]
|
||||
let rightSizeCode = ''
|
||||
let acceptCode = ''
|
||||
const returnList = []
|
||||
if (conf.fileSize) {
|
||||
rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize}
|
||||
if(!isRightSize){
|
||||
proxy.$modal.msgError('文件大小超过 ${conf.fileSize}${conf.sizeUnit}')
|
||||
}`
|
||||
returnList.push('isRightSize')
|
||||
}
|
||||
if (conf.accept) {
|
||||
acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type)
|
||||
if(!isAccept){
|
||||
proxy.$modal.msgError('应该选择${conf.accept}类型的文件')
|
||||
}`
|
||||
returnList.push('isAccept')
|
||||
}
|
||||
const str = `
|
||||
/**
|
||||
* @name: 上传之前的文件判断
|
||||
* @description: 上传之前的文件判断,判断文件大小文件类型等
|
||||
* @param {*} file
|
||||
* @return {*}
|
||||
*/
|
||||
function ${conf.vModel}BeforeUpload(file) {
|
||||
${rightSizeCode}
|
||||
${acceptCode}
|
||||
return ${returnList.join('&&')}
|
||||
}`
|
||||
return returnList.length ? str : ''
|
||||
}
|
||||
/**
|
||||
* @name: 生成提交表单方法
|
||||
* @description: 生成提交表单方法
|
||||
* @param {Object} conf vModel 表单ref
|
||||
* @return {*}
|
||||
*/
|
||||
function buildSubmitUpload(conf) {
|
||||
const str = `function submitUpload() {
|
||||
this.$refs['${conf.vModel}'].submit()
|
||||
}`
|
||||
return str
|
||||
}
|
||||
/**
|
||||
* @name: 组装js代码
|
||||
* @description: 组装js代码方法
|
||||
* @return {*}
|
||||
*/
|
||||
function buildexport(
|
||||
conf,
|
||||
type,
|
||||
data,
|
||||
rules,
|
||||
selectOptions,
|
||||
uploadVar,
|
||||
props,
|
||||
methods
|
||||
) {
|
||||
let str = `
|
||||
const { proxy } = getCurrentInstance()
|
||||
const ${conf.formRef} = ref()
|
||||
const data = reactive({
|
||||
${conf.formModel}: {
|
||||
${data}
|
||||
},
|
||||
${conf.formRules}: {
|
||||
${rules}
|
||||
}
|
||||
})
|
||||
|
||||
const {${conf.formModel}, ${conf.formRules}} = toRefs(data)
|
||||
|
||||
${selectOptions}
|
||||
|
||||
${uploadVar}
|
||||
|
||||
${props}
|
||||
|
||||
${methods}
|
||||
`
|
||||
|
||||
if(type === 'dialog') {
|
||||
str += `
|
||||
// 弹窗设置
|
||||
const dialogVisible = defineModel()
|
||||
// 弹窗确认回调
|
||||
const emit = defineEmits(['confirm'])
|
||||
/**
|
||||
* @name: 弹窗打开后执行
|
||||
* @description: 弹窗打开后执行方法
|
||||
* @return {*}
|
||||
*/
|
||||
function onOpen(){
|
||||
|
||||
}
|
||||
/**
|
||||
* @name: 弹窗关闭时执行
|
||||
* @description: 弹窗关闭方法,重置表单
|
||||
* @return {*}
|
||||
*/
|
||||
function onClose(){
|
||||
${conf.formRef}.value.resetFields()
|
||||
}
|
||||
/**
|
||||
* @name: 弹窗取消
|
||||
* @description: 弹窗取消方法
|
||||
* @return {*}
|
||||
*/
|
||||
function close(){
|
||||
dialogVisible.value = false
|
||||
}
|
||||
/**
|
||||
* @name: 弹窗表单提交
|
||||
* @description: 弹窗表单提交方法
|
||||
* @return {*}
|
||||
*/
|
||||
function handelConfirm(){
|
||||
${conf.formRef}.value.validate((valid) => {
|
||||
if (!valid) return
|
||||
// TODO 提交表单
|
||||
|
||||
close()
|
||||
// 回调父级组件
|
||||
emit('confirm')
|
||||
})
|
||||
}
|
||||
`
|
||||
} else {
|
||||
str += `
|
||||
/**
|
||||
* @name: 表单提交
|
||||
* @description: 表单提交方法
|
||||
* @return {*}
|
||||
*/
|
||||
function submitForm() {
|
||||
${conf.formRef}.value.validate((valid) => {
|
||||
if (!valid) return
|
||||
// TODO 提交表单
|
||||
})
|
||||
}
|
||||
/**
|
||||
* @name: 表单重置
|
||||
* @description: 表单重置方法
|
||||
* @return {*}
|
||||
*/
|
||||
function resetForm() {
|
||||
${conf.formRef}.value.resetFields()
|
||||
}
|
||||
`
|
||||
}
|
||||
return str
|
||||
}
|
||||
156
src/utils/generator/render.js
Normal file
156
src/utils/generator/render.js
Normal file
@@ -0,0 +1,156 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { makeMap } from '@/utils/index'
|
||||
|
||||
const isAttr = makeMap(
|
||||
'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' +
|
||||
'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' +
|
||||
'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' +
|
||||
'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' +
|
||||
'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' +
|
||||
'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' +
|
||||
'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' +
|
||||
'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' +
|
||||
'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' +
|
||||
'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' +
|
||||
'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' +
|
||||
'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' +
|
||||
'target,title,type,usemap,value,width,wrap' + 'prefix-icon'
|
||||
)
|
||||
const isNotProps = makeMap(
|
||||
'layout,prepend,regList,tag,document,changeTag,defaultValue'
|
||||
)
|
||||
|
||||
function useVModel(props, emit) {
|
||||
return {
|
||||
modelValue: props.defaultValue,
|
||||
'onUpdate:modelValue': (val) => emit('update:modelValue', val),
|
||||
}
|
||||
}
|
||||
const componentChild = {
|
||||
'el-button': {
|
||||
default(h, conf, key) {
|
||||
return conf[key]
|
||||
},
|
||||
},
|
||||
'el-select': {
|
||||
options(h, conf, key) {
|
||||
return conf.options.map(item => h(resolveComponent('el-option'), {
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}))
|
||||
}
|
||||
},
|
||||
'el-radio-group': {
|
||||
options(h, conf, key) {
|
||||
return conf.optionType === 'button' ? conf.options.map(item => h(resolveComponent('el-checkbox-button'), {
|
||||
label: item.value,
|
||||
}, () => item.label)) : conf.options.map(item => h(resolveComponent('el-radio'), {
|
||||
label: item.value,
|
||||
border: conf.border,
|
||||
}, () => item.label))
|
||||
}
|
||||
},
|
||||
'el-checkbox-group': {
|
||||
options(h, conf, key) {
|
||||
return conf.optionType === 'button' ? conf.options.map(item => h(resolveComponent('el-checkbox-button'), {
|
||||
label: item.value,
|
||||
}, () => item.label)) : conf.options.map(item => h(resolveComponent('el-checkbox'), {
|
||||
label: item.value,
|
||||
border: conf.border,
|
||||
}, () => item.label))
|
||||
}
|
||||
},
|
||||
'el-upload': {
|
||||
'list-type': (h, conf, key) => {
|
||||
const option = {}
|
||||
// if (conf.showTip) {
|
||||
// tip = h('div', {
|
||||
// class: "el-upload__tip"
|
||||
// }, () => '只能上传不超过' + conf.fileSize + conf.sizeUnit + '的' + conf.accept + '文件')
|
||||
// }
|
||||
if (conf['list-type'] === 'picture-card') {
|
||||
return h(resolveComponent('el-icon'), option, () => h(resolveComponent('Plus')))
|
||||
} else {
|
||||
// option.size = "small"
|
||||
option.type = "primary"
|
||||
option.icon = "Upload"
|
||||
return h(resolveComponent('el-button'), option, () => conf.buttonText)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
const componentSlot = {
|
||||
'el-upload': {
|
||||
'tip': (h, conf, key) => {
|
||||
if (conf.showTip) {
|
||||
return () => h('div', {
|
||||
class: "el-upload__tip"
|
||||
}, '只能上传不超过' + conf.fileSize + conf.sizeUnit + '的' + conf.accept + '文件')
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
export default defineComponent({
|
||||
|
||||
// 使用 render 函数
|
||||
render() {
|
||||
const dataObject = {
|
||||
attrs: {},
|
||||
props: {},
|
||||
on: {},
|
||||
style: {}
|
||||
}
|
||||
const confClone = JSON.parse(JSON.stringify(this.conf))
|
||||
const children = []
|
||||
const slot = {}
|
||||
const childObjs = componentChild[confClone.tag]
|
||||
if (childObjs) {
|
||||
Object.keys(childObjs).forEach(key => {
|
||||
const childFunc = childObjs[key]
|
||||
if (confClone[key]) {
|
||||
children.push(childFunc(h, confClone, key))
|
||||
}
|
||||
})
|
||||
}
|
||||
const slotObjs = componentSlot[confClone.tag]
|
||||
if (slotObjs) {
|
||||
Object.keys(slotObjs).forEach(key => {
|
||||
const childFunc = slotObjs[key]
|
||||
if (confClone[key]) {
|
||||
slot[key] = childFunc(h, confClone, key)
|
||||
}
|
||||
})
|
||||
}
|
||||
Object.keys(confClone).forEach(key => {
|
||||
const val = confClone[key]
|
||||
if (dataObject[key]) {
|
||||
dataObject[key] = val
|
||||
} else if (isAttr(key)) {
|
||||
dataObject.attrs[key] = val
|
||||
} else if (!isNotProps(key)) {
|
||||
dataObject.props[key] = val
|
||||
}
|
||||
})
|
||||
if(children.length > 0){
|
||||
slot.default = () => children
|
||||
}
|
||||
|
||||
return h(resolveComponent(this.conf.tag),
|
||||
{
|
||||
modelValue: this.$attrs.modelValue,
|
||||
...dataObject.props,
|
||||
...dataObject.attrs,
|
||||
style: {
|
||||
...dataObject.style
|
||||
},
|
||||
}
|
||||
, slot ?? null)
|
||||
},
|
||||
props: {
|
||||
conf: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -4,7 +4,7 @@ import { parseTime } from './ruoyi'
|
||||
* 表格时间格式化
|
||||
*/
|
||||
export function formatDate(cellValue) {
|
||||
if (cellValue == null || cellValue == "") return "";
|
||||
if (cellValue == null || cellValue == "") return ""
|
||||
var date = new Date(cellValue)
|
||||
var year = date.getFullYear()
|
||||
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
|
||||
|
||||
@@ -9,7 +9,7 @@ export function checkPermi(value) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const permissions = useUserStore().permissions
|
||||
const permissionDatas = value
|
||||
const all_permission = "*:*:*";
|
||||
const all_permission = "*:*:*"
|
||||
|
||||
const hasPermission = permissions.some(permission => {
|
||||
return all_permission === permission || permissionDatas.includes(permission)
|
||||
@@ -34,7 +34,7 @@ export function checkRole(value) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const roles = useUserStore().roles
|
||||
const permissionRoles = value
|
||||
const super_admin = "admin";
|
||||
const super_admin = "admin"
|
||||
|
||||
const hasRole = roles.some(role => {
|
||||
return super_admin === role || permissionRoles.includes(role)
|
||||
|
||||
@@ -7,9 +7,9 @@ import cache from '@/plugins/cache'
|
||||
import { saveAs } from 'file-saver'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
let downloadLoadingInstance;
|
||||
let downloadLoadingInstance
|
||||
// 是否显示重新登录
|
||||
export let isRelogin = { show: false };
|
||||
export let isRelogin = { show: false }
|
||||
|
||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||
// 创建axios实例
|
||||
@@ -31,10 +31,10 @@ service.interceptors.request.use(config => {
|
||||
}
|
||||
// get请求映射params参数
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = config.url + '?' + tansParams(config.params);
|
||||
url = url.slice(0, -1);
|
||||
config.params = {};
|
||||
config.url = url;
|
||||
let url = config.url + '?' + tansParams(config.params)
|
||||
url = url.slice(0, -1)
|
||||
config.params = {}
|
||||
config.url = url
|
||||
}
|
||||
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
||||
const requestObj = {
|
||||
@@ -42,22 +42,22 @@ 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
|
||||
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
|
||||
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
|
||||
if (requestSize >= limitSize) {
|
||||
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
|
||||
return config;
|
||||
return config
|
||||
}
|
||||
const sessionObj = cache.session.getJSON('sessionObj')
|
||||
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
|
||||
cache.session.setJSON('sessionObj', requestObj)
|
||||
} else {
|
||||
const s_url = sessionObj.url; // 请求地址
|
||||
const s_data = sessionObj.data; // 请求数据
|
||||
const s_time = sessionObj.time; // 请求时间
|
||||
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
|
||||
const s_url = sessionObj.url // 请求地址
|
||||
const s_data = sessionObj.data // 请求数据
|
||||
const s_time = sessionObj.time // 请求时间
|
||||
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
|
||||
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
|
||||
const message = '数据正在处理,请勿重复提交';
|
||||
const message = '数据正在处理,请勿重复提交'
|
||||
console.warn(`[${s_url}]: ` + message)
|
||||
return Promise.reject(new Error(message))
|
||||
} else {
|
||||
@@ -74,7 +74,7 @@ service.interceptors.request.use(config => {
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(res => {
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = res.data.code || 200;
|
||||
const code = res.data.code || 200
|
||||
// 获取错误信息
|
||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||
// 二进制数据则直接返回
|
||||
@@ -83,15 +83,15 @@ service.interceptors.response.use(res => {
|
||||
}
|
||||
if (code === 401) {
|
||||
if (!isRelogin.show) {
|
||||
isRelogin.show = true;
|
||||
isRelogin.show = true
|
||||
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||
isRelogin.show = false;
|
||||
isRelogin.show = false
|
||||
useUserStore().logOut().then(() => {
|
||||
location.href = '/index';
|
||||
location.href = '/index'
|
||||
})
|
||||
}).catch(() => {
|
||||
isRelogin.show = false;
|
||||
});
|
||||
isRelogin.show = false
|
||||
})
|
||||
}
|
||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 500) {
|
||||
@@ -109,13 +109,13 @@ service.interceptors.response.use(res => {
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error)
|
||||
let { message } = error;
|
||||
let { message } = error
|
||||
if (message == "Network Error") {
|
||||
message = "后端接口连接异常";
|
||||
message = "后端接口连接异常"
|
||||
} else if (message.includes("timeout")) {
|
||||
message = "系统接口请求超时";
|
||||
message = "系统接口请求超时"
|
||||
} else if (message.includes("Request failed with status code")) {
|
||||
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
||||
message = "系统接口" + message.substr(message.length - 3) + "异常"
|
||||
}
|
||||
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
|
||||
return Promise.reject(error)
|
||||
@@ -131,21 +131,21 @@ export function download(url, params, filename, config) {
|
||||
responseType: 'blob',
|
||||
...config
|
||||
}).then(async (data) => {
|
||||
const isBlob = blobValidate(data);
|
||||
const isBlob = blobValidate(data)
|
||||
if (isBlob) {
|
||||
const blob = new Blob([data])
|
||||
saveAs(blob, filename)
|
||||
} else {
|
||||
const resText = await data.text();
|
||||
const rspObj = JSON.parse(resText);
|
||||
const resText = await data.text()
|
||||
const rspObj = JSON.parse(resText)
|
||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||
ElMessage.error(errMsg);
|
||||
ElMessage.error(errMsg)
|
||||
}
|
||||
downloadLoadingInstance.close();
|
||||
downloadLoadingInstance.close()
|
||||
}).catch((r) => {
|
||||
console.error(r)
|
||||
ElMessage.error('下载文件出现错误,请联系管理员!')
|
||||
downloadLoadingInstance.close();
|
||||
downloadLoadingInstance.close()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
/**
|
||||
* 通用js方法封装处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
@@ -18,7 +16,7 @@ export function parseTime(time, pattern) {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
} else if (typeof time === 'string') {
|
||||
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
|
||||
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '')
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
@@ -49,89 +47,89 @@ export function parseTime(time, pattern) {
|
||||
// 表单重置
|
||||
export function resetForm(refName) {
|
||||
if (this.$refs[refName]) {
|
||||
this.$refs[refName].resetFields();
|
||||
this.$refs[refName].resetFields()
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日期范围
|
||||
export function addDateRange(params, dateRange, propName) {
|
||||
let search = params;
|
||||
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
|
||||
dateRange = Array.isArray(dateRange) ? dateRange : [];
|
||||
let search = params
|
||||
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}
|
||||
dateRange = Array.isArray(dateRange) ? dateRange : []
|
||||
if (typeof (propName) === 'undefined') {
|
||||
search.params['beginTime'] = dateRange[0];
|
||||
search.params['endTime'] = dateRange[1];
|
||||
search.params['beginTime'] = dateRange[0]
|
||||
search.params['endTime'] = dateRange[1]
|
||||
} else {
|
||||
search.params['begin' + propName] = dateRange[0];
|
||||
search.params['end' + propName] = dateRange[1];
|
||||
search.params['begin' + propName] = dateRange[0]
|
||||
search.params['end' + propName] = dateRange[1]
|
||||
}
|
||||
return search;
|
||||
return search
|
||||
}
|
||||
|
||||
// 回显数据字典
|
||||
export function selectDictLabel(datas, value) {
|
||||
if (value === undefined) {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
var actions = [];
|
||||
var actions = []
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].value == ('' + value)) {
|
||||
actions.push(datas[key].label);
|
||||
return true;
|
||||
actions.push(datas[key].label)
|
||||
return true
|
||||
}
|
||||
})
|
||||
if (actions.length === 0) {
|
||||
actions.push(value);
|
||||
actions.push(value)
|
||||
}
|
||||
return actions.join('');
|
||||
return actions.join('')
|
||||
}
|
||||
|
||||
// 回显数据字典(字符串数组)
|
||||
// 回显数据字典(字符串、数组)
|
||||
export function selectDictLabels(datas, value, separator) {
|
||||
if (value === undefined || value.length ===0) {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(",");
|
||||
value = value.join(",")
|
||||
}
|
||||
var actions = [];
|
||||
var currentSeparator = undefined === separator ? "," : separator;
|
||||
var temp = value.split(currentSeparator);
|
||||
var actions = []
|
||||
var currentSeparator = undefined === separator ? "," : separator
|
||||
var temp = value.split(currentSeparator)
|
||||
Object.keys(value.split(currentSeparator)).some((val) => {
|
||||
var match = false;
|
||||
var match = false
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].value == ('' + temp[val])) {
|
||||
actions.push(datas[key].label + currentSeparator);
|
||||
match = true;
|
||||
actions.push(datas[key].label + currentSeparator)
|
||||
match = true
|
||||
}
|
||||
})
|
||||
if (!match) {
|
||||
actions.push(temp[val] + currentSeparator);
|
||||
actions.push(temp[val] + currentSeparator)
|
||||
}
|
||||
})
|
||||
return actions.join('').substring(0, actions.join('').length - 1);
|
||||
return actions.join('').substring(0, actions.join('').length - 1)
|
||||
}
|
||||
|
||||
// 字符串格式化(%s )
|
||||
export function sprintf(str) {
|
||||
var args = arguments, flag = true, i = 1;
|
||||
var args = arguments, flag = true, i = 1
|
||||
str = str.replace(/%s/g, function () {
|
||||
var arg = args[i++];
|
||||
var arg = args[i++]
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false;
|
||||
return '';
|
||||
flag = false
|
||||
return ''
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
return flag ? str : '';
|
||||
return arg
|
||||
})
|
||||
return flag ? str : ''
|
||||
}
|
||||
|
||||
// 转换字符串,undefined,null等转化为""
|
||||
export function parseStrEmpty(str) {
|
||||
if (!str || str == "undefined" || str == "null") {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
return str;
|
||||
return str
|
||||
}
|
||||
|
||||
// 数据合并
|
||||
@@ -139,16 +137,16 @@ export function mergeRecursive(source, target) {
|
||||
for (var p in target) {
|
||||
try {
|
||||
if (target[p].constructor == Object) {
|
||||
source[p] = mergeRecursive(source[p], target[p]);
|
||||
source[p] = mergeRecursive(source[p], target[p])
|
||||
} else {
|
||||
source[p] = target[p];
|
||||
source[p] = target[p]
|
||||
}
|
||||
} catch (e) {
|
||||
source[p] = target[p];
|
||||
source[p] = target[p]
|
||||
}
|
||||
}
|
||||
return source;
|
||||
};
|
||||
return source
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造树型结构数据
|
||||
@@ -162,43 +160,28 @@ export function handleTree(data, id, parentId, children) {
|
||||
id: id || 'id',
|
||||
parentId: parentId || 'parentId',
|
||||
childrenList: children || 'children'
|
||||
};
|
||||
|
||||
var childrenListMap = {};
|
||||
var nodeIds = {};
|
||||
var tree = [];
|
||||
}
|
||||
|
||||
var childrenListMap = {}
|
||||
var tree = []
|
||||
for (let d of data) {
|
||||
let parentId = d[config.parentId];
|
||||
if (childrenListMap[parentId] == null) {
|
||||
childrenListMap[parentId] = [];
|
||||
let id = d[config.id]
|
||||
childrenListMap[id] = d
|
||||
if (!d[config.childrenList]) {
|
||||
d[config.childrenList] = []
|
||||
}
|
||||
nodeIds[d[config.id]] = d;
|
||||
childrenListMap[parentId].push(d);
|
||||
}
|
||||
|
||||
for (let d of data) {
|
||||
let parentId = d[config.parentId];
|
||||
if (nodeIds[parentId] == null) {
|
||||
tree.push(d);
|
||||
let parentId = d[config.parentId]
|
||||
let parentObj = childrenListMap[parentId]
|
||||
if (!parentObj) {
|
||||
tree.push(d)
|
||||
} else {
|
||||
parentObj[config.childrenList].push(d)
|
||||
}
|
||||
}
|
||||
|
||||
for (let t of tree) {
|
||||
adaptToChildrenList(t);
|
||||
}
|
||||
|
||||
function adaptToChildrenList(o) {
|
||||
if (childrenListMap[o[config.id]] !== null) {
|
||||
o[config.childrenList] = childrenListMap[o[config.id]];
|
||||
}
|
||||
if (o[config.childrenList]) {
|
||||
for (let c of o[config.childrenList]) {
|
||||
adaptToChildrenList(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
return tree
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,36 +191,35 @@ export function handleTree(data, id, parentId, children) {
|
||||
export function tansParams(params) {
|
||||
let result = ''
|
||||
for (const propName of Object.keys(params)) {
|
||||
const value = params[propName];
|
||||
var part = encodeURIComponent(propName) + "=";
|
||||
const value = params[propName]
|
||||
var part = encodeURIComponent(propName) + "="
|
||||
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||
if (typeof value === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||
let params = propName + '[' + key + ']';
|
||||
var subPart = encodeURIComponent(params) + "=";
|
||||
result += subPart + encodeURIComponent(value[key]) + "&";
|
||||
let params = propName + '[' + key + ']'
|
||||
var subPart = encodeURIComponent(params) + "="
|
||||
result += subPart + encodeURIComponent(value[key]) + "&"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += part + encodeURIComponent(value) + "&";
|
||||
result += part + encodeURIComponent(value) + "&"
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// 返回项目路径
|
||||
export function getNormalPath(p) {
|
||||
if (p.length === 0 || !p || p == 'undefined') {
|
||||
return p
|
||||
};
|
||||
}
|
||||
let res = p.replace('//', '/')
|
||||
if (res[res.length - 1] === '/') {
|
||||
return res.slice(0, res.length - 1)
|
||||
}
|
||||
return res;
|
||||
return res
|
||||
}
|
||||
|
||||
// 验证是否为blob格式
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
/**
|
||||
* 判断url是否是http或https
|
||||
* 路径匹配器
|
||||
* @param {string} pattern
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isHttp(url) {
|
||||
export function isPathMatch(pattern, path) {
|
||||
const regexPattern = pattern.replace(/\//g, '\\/').replace(/\*\*/g, '.*').replace(/\*/g, '[^\\/]*')
|
||||
const regex = new RegExp(`^${regexPattern}$`)
|
||||
return regex.test(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断value字符串是否为空
|
||||
* @param {string} value
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isEmpty(value) {
|
||||
if (value == null || value == "" || value == undefined || value == "undefined") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否是http或https
|
||||
* @param {string} url
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isHttp(url) {
|
||||
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
||||
}
|
||||
|
||||
@@ -12,7 +36,7 @@
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path) {
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
@@ -75,10 +99,7 @@ export function validEmail(email) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isString(str) {
|
||||
if (typeof str === 'string' || str instanceof String) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return typeof str === 'string' || str instanceof String
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,17 +26,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import errImage from "@/assets/401_images/401.gif";
|
||||
import errImage from "@/assets/401_images/401.gif"
|
||||
|
||||
let { proxy } = getCurrentInstance();
|
||||
let { proxy } = getCurrentInstance()
|
||||
|
||||
const errGif = ref(errImage + "?" + +new Date());
|
||||
const errGif = ref(errImage + "?" + +new Date())
|
||||
|
||||
function back() {
|
||||
if (proxy.$route.query.noGoBack) {
|
||||
proxy.$router.push({ path: "/" });
|
||||
proxy.$router.push({ path: "/" })
|
||||
} else {
|
||||
proxy.$router.go(-1);
|
||||
proxy.$router.go(-1)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,42 +1,5 @@
|
||||
<template>
|
||||
<div class="app-container home">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="24">
|
||||
<blockquote class="text-warning" style="font-size: 14px">
|
||||
领取阿里云通用云产品1888优惠券
|
||||
<br />
|
||||
<el-link
|
||||
href="https://www.aliyun.com/minisite/goods?userCode=brki8iof"
|
||||
type="primary"
|
||||
target="_blank"
|
||||
>https://www.aliyun.com/minisite/goods?userCode=brki8iof</el-link
|
||||
>
|
||||
<br />
|
||||
领取腾讯云通用云产品2860优惠券
|
||||
<br />
|
||||
<el-link
|
||||
href="https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console"
|
||||
type="primary"
|
||||
target="_blank"
|
||||
>https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console</el-link
|
||||
>
|
||||
<br />
|
||||
阿里云服务器折扣区
|
||||
<el-link href="http://aly.ruoyi.vip" type="primary" target="_blank"
|
||||
>>☛☛点我进入☚☚</el-link
|
||||
>
|
||||
腾讯云服务器秒杀区
|
||||
<el-link href="http://txy.ruoyi.vip" type="primary" target="_blank"
|
||||
>>☛☛点我进入☚☚</el-link
|
||||
><br />
|
||||
<h4 class="text-danger">
|
||||
云产品通用红包,可叠加官网常规优惠使用。(仅限新用户)
|
||||
</h4>
|
||||
</blockquote>
|
||||
|
||||
<hr />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>若依后台管理框架</h2>
|
||||
@@ -122,7 +85,10 @@
|
||||
<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>
|
||||
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s>
|
||||
<s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
|
||||
<s> 满287842588 </s> <s> 满187944233 </s> <s> 满228578329 </s> <s> 满191164766 </s>
|
||||
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EeCBXu51I1zPWRia2uskpjDRx6VrbnFN&authKey=Xm8yDxk0%2FyYGI11oxhXaQnTn4K7UwCk7Kn2MZTh3P1JxLctollAkyeySjnaILDkb&noverify=0&group_code=174569686" target="_blank">174569686</a>
|
||||
</p>
|
||||
<p>
|
||||
<i class="el-icon-chat-dot-round"></i> 微信:<a
|
||||
@@ -148,6 +114,111 @@
|
||||
</div>
|
||||
</template>
|
||||
<el-collapse accordion>
|
||||
<el-collapse-item title="v3.9.0 - 2025-05-28">
|
||||
<ol>
|
||||
<li>优化菜单搜索查询页</li>
|
||||
<li>导航栏显示昵称&设置</li>
|
||||
<li>菜单管理新增路由名称</li>
|
||||
<li>添加底部版权信息&开关</li>
|
||||
<li>分配角色禁用不允许勾选</li>
|
||||
<li>Excel导入导出支持多图片</li>
|
||||
<li>添加页签图标显示开关功能</li>
|
||||
<li>上传组件新增拖动排序属性</li>
|
||||
<li>显隐列组件支持全选/全不选</li>
|
||||
<li>初始密码支持自定义修改策略</li>
|
||||
<li>账号密码支持自定义更新周期</li>
|
||||
<li>代码生成列表支持按时间排序</li>
|
||||
<li>支持富文本复制粘贴图片上传至url</li>
|
||||
<li>支持文件&图片组件自定义地址&参数</li>
|
||||
<li>升级tomcat到最新版本9.0.105</li>
|
||||
<li>升级oshi到最新版本6.8.1</li>
|
||||
<li>升级fastjson到最新版2.0.57</li>
|
||||
<li>升级commons.io到最新版本2.19.0</li>
|
||||
<li>package.json移除runjs依赖</li>
|
||||
<li>package.json移除eslint依赖</li>
|
||||
<li>package.json移除vue-meta依赖</li>
|
||||
<li>修复代码生成主子表校验必填失效问题</li>
|
||||
<li>优化前端树结构性能问题</li>
|
||||
<li>优化前端处理路由函数代码</li>
|
||||
<li>优化文件上传组件新增类型</li>
|
||||
<li>优化顶部菜单搜索栏为多层级显示</li>
|
||||
<li>优化文件&图片上传组件新增disabled属性</li>
|
||||
<li>优化空指针异常时无法获取错误信息问题</li>
|
||||
<li>优化定时任务字符包含多个括号导致数据错误</li>
|
||||
<li>优化登录&注册页表头使用VUE_APP_TITLE配置值</li>
|
||||
<li>优化导出Excel日期格式双击离开后与设定的格式不一致问题</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.9 - 2024-12-30">
|
||||
<ol>
|
||||
<li>用户管理支持分栏拖动</li>
|
||||
<li>修改主题样式本地读取</li>
|
||||
<li>用户头像http(s)链接支持</li>
|
||||
<li>用户管理过滤掉已禁用部门</li>
|
||||
<li>支持自定义显示Excel属性列</li>
|
||||
<li>操作日志记录DELETE请求参数</li>
|
||||
<li>白名单支持对通配符路径匹配</li>
|
||||
<li>校检文件名是否包含特殊字符</li>
|
||||
<li>代码生成创建表屏蔽违规的字符</li>
|
||||
<li>菜单面包屑导航支持多层级显示</li>
|
||||
<li>Excel注解支持wrapText是否允许内容换行</li>
|
||||
<li>代码生成新增配置是否允许文件覆盖到本地</li>
|
||||
<li>修复角色禁用权限不失效问题</li>
|
||||
<li>修复代码生成上级菜单显示问题</li>
|
||||
<li>修复导出子列表对象只能在最后的问题</li>
|
||||
<li>修复TopNav无法正确获取active的问题</li>
|
||||
<li>修复默认关闭Tags-Views内链页面打不开</li>
|
||||
<li>升级oshi到最新版本6.6.5</li>
|
||||
<li>升级tomcat到最新版本9.0.96</li>
|
||||
<li>升级fastjson到最新版2.0.53</li>
|
||||
<li>升级logback到最新版本1.2.13</li>
|
||||
<li>升级spring-framework到最新版本5.3.39</li>
|
||||
<li>升级quill到最新版本2.0.2</li>
|
||||
<li>升级axios到最新版本0.28.1</li>
|
||||
<li>优化身份证脱敏正则</li>
|
||||
<li>优化权限更新后同步缓存</li>
|
||||
<li>优化查询时间范围日期格式</li>
|
||||
<li>优化参数键值更换为多行文本</li>
|
||||
<li>优化导入带标题文件关闭清理</li>
|
||||
<li>优化上传图片带域名不增加前缀</li>
|
||||
<li>优化特殊字符密码修改失败问题</li>
|
||||
<li>优化无用户编号不校验数据权限</li>
|
||||
<li>优化TopNav内链菜单点击没有高亮</li>
|
||||
<li>优化菜单管理切换Mini布局错乱问题</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.8 - 2024-06-30">
|
||||
<ol>
|
||||
<li>菜单管理新增路由名称</li>
|
||||
<li>新增数据脱敏过滤注解</li>
|
||||
<li>用户密码新增非法字符验证</li>
|
||||
<li>限制用户操作数据权限范围</li>
|
||||
<li>代码生成新增创建表结构功能</li>
|
||||
<li>定时任务白名单配置范围缩小</li>
|
||||
<li>优化代码生成主子表关联查询方式</li>
|
||||
<li>Excel注解新增属性comboReadDict</li>
|
||||
<li>Excel注解ColumnType类型新增文本</li>
|
||||
<li>新增国际化资源文件配置</li>
|
||||
<li>升级oshi到最新版本6.6.1</li>
|
||||
<li>升级druid到最新版本1.2.23</li>
|
||||
<li>升级core-js到最新版本3.37.1</li>
|
||||
<li>更新HttpUtils中的User-Agent</li>
|
||||
<li>更新compressionPlugin到6.1.2以兼容node18+</li>
|
||||
<li>升级spring-security到安全版本,防止漏洞风险</li>
|
||||
<li>升级spring-framework到安全版本,防止漏洞风险</li>
|
||||
<li>优化自定义XSS注解匹配方式</li>
|
||||
<li>优化缓存监控键名列表排序显示</li>
|
||||
<li>优化定时任务日志默认按时间排序</li>
|
||||
<li>优化默认文件大小超过2G无效的问题</li>
|
||||
<li>优化查表特殊字符使用反斜杠进行转义</li>
|
||||
<li>优化定时任务cron表达式小时配置显示错误问题</li>
|
||||
<li>优化多个自定数据权限使用in查询,避免多次拼接</li>
|
||||
<li>优化导入Excel时设置dictType属性重复查缓存问题</li>
|
||||
<li>其他细节优化</li>
|
||||
</ol>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="v3.8.7 - 2023-12-08">
|
||||
<ol>
|
||||
<li>操作日志记录部门名称</li>
|
||||
@@ -988,7 +1059,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Index">
|
||||
const version = ref('3.8.7')
|
||||
const version = ref('3.9.0')
|
||||
|
||||
function goTarget(url) {
|
||||
window.open(url, '__blank')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<h3 class="title">若依后台管理系统</h3>
|
||||
<h3 class="title">{{ title }}</h3>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
@@ -59,21 +59,24 @@
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-login-footer">
|
||||
<span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>{{ footerContent }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCodeImg } from "@/api/login";
|
||||
import Cookies from "js-cookie";
|
||||
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
||||
import { getCodeImg } from "@/api/login"
|
||||
import Cookies from "js-cookie"
|
||||
import { encrypt, decrypt } from "@/utils/jsencrypt"
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
const title = import.meta.env.VITE_APP_TITLE
|
||||
const footerContent = defaultSettings.footerContent
|
||||
const userStore = useUserStore()
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const loginForm = ref({
|
||||
username: "admin",
|
||||
@@ -81,85 +84,85 @@ const loginForm = ref({
|
||||
rememberMe: false,
|
||||
code: "",
|
||||
uuid: ""
|
||||
});
|
||||
})
|
||||
|
||||
const loginRules = {
|
||||
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
|
||||
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
|
||||
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||
};
|
||||
}
|
||||
|
||||
const codeUrl = ref("");
|
||||
const loading = ref(false);
|
||||
const codeUrl = ref("")
|
||||
const loading = ref(false)
|
||||
// 验证码开关
|
||||
const captchaEnabled = ref(true);
|
||||
const captchaEnabled = ref(true)
|
||||
// 注册开关
|
||||
const register = ref(false);
|
||||
const redirect = ref(undefined);
|
||||
const register = ref(false)
|
||||
const redirect = ref(undefined)
|
||||
|
||||
watch(route, (newRoute) => {
|
||||
redirect.value = newRoute.query && newRoute.query.redirect;
|
||||
}, { immediate: true });
|
||||
redirect.value = newRoute.query && newRoute.query.redirect
|
||||
}, { immediate: true })
|
||||
|
||||
function handleLogin() {
|
||||
proxy.$refs.loginRef.validate(valid => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
// 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
|
||||
if (loginForm.value.rememberMe) {
|
||||
Cookies.set("username", loginForm.value.username, { expires: 30 });
|
||||
Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
|
||||
Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
|
||||
Cookies.set("username", loginForm.value.username, { expires: 30 })
|
||||
Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 })
|
||||
Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 })
|
||||
} else {
|
||||
// 否则移除
|
||||
Cookies.remove("username");
|
||||
Cookies.remove("password");
|
||||
Cookies.remove("rememberMe");
|
||||
Cookies.remove("username")
|
||||
Cookies.remove("password")
|
||||
Cookies.remove("rememberMe")
|
||||
}
|
||||
// 调用action的登录方法
|
||||
userStore.login(loginForm.value).then(() => {
|
||||
const query = route.query;
|
||||
const query = route.query
|
||||
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
|
||||
if (cur !== "redirect") {
|
||||
acc[cur] = query[cur];
|
||||
acc[cur] = query[cur]
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
router.push({ path: redirect.value || "/", query: otherQueryParams });
|
||||
return acc
|
||||
}, {})
|
||||
router.push({ path: redirect.value || "/", query: otherQueryParams })
|
||||
}).catch(() => {
|
||||
loading.value = false;
|
||||
loading.value = false
|
||||
// 重新获取验证码
|
||||
if (captchaEnabled.value) {
|
||||
getCode();
|
||||
getCode()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function getCode() {
|
||||
getCodeImg().then(res => {
|
||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
|
||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled
|
||||
if (captchaEnabled.value) {
|
||||
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||
loginForm.value.uuid = res.uuid;
|
||||
codeUrl.value = "data:image/gif;base64," + res.img
|
||||
loginForm.value.uuid = res.uuid
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function getCookie() {
|
||||
const username = Cookies.get("username");
|
||||
const password = Cookies.get("password");
|
||||
const rememberMe = Cookies.get("rememberMe");
|
||||
const username = Cookies.get("username")
|
||||
const password = Cookies.get("password")
|
||||
const rememberMe = Cookies.get("rememberMe")
|
||||
loginForm.value = {
|
||||
username: username === undefined ? loginForm.value.username : username,
|
||||
password: password === undefined ? loginForm.value.password : decrypt(password),
|
||||
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getCode();
|
||||
getCookie();
|
||||
getCode()
|
||||
getCookie()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@@ -182,6 +185,7 @@ getCookie();
|
||||
background: #ffffff;
|
||||
width: 400px;
|
||||
padding: 25px 25px 5px 25px;
|
||||
z-index: 1;
|
||||
.el-input {
|
||||
height: 40px;
|
||||
input {
|
||||
|
||||
34
src/views/monitor/cache/index.vue
vendored
34
src/views/monitor/cache/index.vue
vendored
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="24" class="card-box">
|
||||
<el-card>
|
||||
<template #header><Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">基本信息</span></template>
|
||||
@@ -65,21 +65,21 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Cache">
|
||||
import { getCache } from '@/api/monitor/cache';
|
||||
import * as echarts from 'echarts';
|
||||
import { getCache } from '@/api/monitor/cache'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const cache = ref([]);
|
||||
const commandstats = ref(null);
|
||||
const usedmemory = ref(null);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const cache = ref([])
|
||||
const commandstats = ref(null)
|
||||
const usedmemory = ref(null)
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
function getList() {
|
||||
proxy.$modal.loading("正在加载缓存监控数据,请稍候!");
|
||||
proxy.$modal.loading("正在加载缓存监控数据,请稍候!")
|
||||
getCache().then(response => {
|
||||
proxy.$modal.closeLoading();
|
||||
cache.value = response.data;
|
||||
proxy.$modal.closeLoading()
|
||||
cache.value = response.data
|
||||
|
||||
const commandstatsIntance = echarts.init(commandstats.value, "macarons");
|
||||
const commandstatsIntance = echarts.init(commandstats.value, "macarons")
|
||||
commandstatsIntance.setOption({
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
@@ -97,8 +97,8 @@ function getList() {
|
||||
animationDuration: 1000
|
||||
}
|
||||
]
|
||||
});
|
||||
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
|
||||
})
|
||||
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons")
|
||||
usedmemoryInstance.setOption({
|
||||
tooltip: {
|
||||
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
|
||||
@@ -122,11 +122,11 @@ function getList() {
|
||||
]
|
||||
})
|
||||
window.addEventListener("resize", () => {
|
||||
commandstatsIntance.resize();
|
||||
usedmemoryInstance.resize();
|
||||
});
|
||||
commandstatsIntance.resize()
|
||||
usedmemoryInstance.resize()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getList();
|
||||
getList()
|
||||
</script>
|
||||
|
||||
74
src/views/monitor/cache/list.vue
vendored
74
src/views/monitor/cache/list.vue
vendored
@@ -155,92 +155,92 @@
|
||||
</template>
|
||||
|
||||
<script setup name="CacheList">
|
||||
import { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll } from "@/api/monitor/cache";
|
||||
import { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll } from "@/api/monitor/cache"
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const cacheNames = ref([]);
|
||||
const cacheKeys = ref([]);
|
||||
const cacheForm = ref({});
|
||||
const loading = ref(true);
|
||||
const subLoading = ref(false);
|
||||
const nowCacheName = ref("");
|
||||
const tableHeight = ref(window.innerHeight - 200);
|
||||
const cacheNames = ref([])
|
||||
const cacheKeys = ref([])
|
||||
const cacheForm = ref({})
|
||||
const loading = ref(true)
|
||||
const subLoading = ref(false)
|
||||
const nowCacheName = ref("")
|
||||
const tableHeight = ref(window.innerHeight - 200)
|
||||
|
||||
/** 查询缓存名称列表 */
|
||||
function getCacheNames() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
listCacheName().then(response => {
|
||||
cacheNames.value = response.data;
|
||||
loading.value = false;
|
||||
});
|
||||
cacheNames.value = response.data
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 刷新缓存名称列表 */
|
||||
function refreshCacheNames() {
|
||||
getCacheNames();
|
||||
proxy.$modal.msgSuccess("刷新缓存列表成功");
|
||||
getCacheNames()
|
||||
proxy.$modal.msgSuccess("刷新缓存列表成功")
|
||||
}
|
||||
|
||||
/** 清理指定名称缓存 */
|
||||
function handleClearCacheName(row) {
|
||||
clearCacheName(row.cacheName).then(response => {
|
||||
proxy.$modal.msgSuccess("清理缓存名称[" + row.cacheName + "]成功");
|
||||
getCacheKeys();
|
||||
});
|
||||
proxy.$modal.msgSuccess("清理缓存名称[" + row.cacheName + "]成功")
|
||||
getCacheKeys()
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询缓存键名列表 */
|
||||
function getCacheKeys(row) {
|
||||
const cacheName = row !== undefined ? row.cacheName : nowCacheName.value;
|
||||
const cacheName = row !== undefined ? row.cacheName : nowCacheName.value
|
||||
if (cacheName === "") {
|
||||
return;
|
||||
return
|
||||
}
|
||||
subLoading.value = true;
|
||||
subLoading.value = true
|
||||
listCacheKey(cacheName).then(response => {
|
||||
cacheKeys.value = response.data;
|
||||
subLoading.value = false;
|
||||
nowCacheName.value = cacheName;
|
||||
});
|
||||
cacheKeys.value = response.data
|
||||
subLoading.value = false
|
||||
nowCacheName.value = cacheName
|
||||
})
|
||||
}
|
||||
|
||||
/** 刷新缓存键名列表 */
|
||||
function refreshCacheKeys() {
|
||||
getCacheKeys();
|
||||
proxy.$modal.msgSuccess("刷新键名列表成功");
|
||||
getCacheKeys()
|
||||
proxy.$modal.msgSuccess("刷新键名列表成功")
|
||||
}
|
||||
|
||||
/** 清理指定键名缓存 */
|
||||
function handleClearCacheKey(cacheKey) {
|
||||
clearCacheKey(cacheKey).then(response => {
|
||||
proxy.$modal.msgSuccess("清理缓存键名[" + cacheKey + "]成功");
|
||||
getCacheKeys();
|
||||
});
|
||||
proxy.$modal.msgSuccess("清理缓存键名[" + cacheKey + "]成功")
|
||||
getCacheKeys()
|
||||
})
|
||||
}
|
||||
|
||||
/** 列表前缀去除 */
|
||||
function nameFormatter(row) {
|
||||
return row.cacheName.replace(":", "");
|
||||
return row.cacheName.replace(":", "")
|
||||
}
|
||||
|
||||
/** 键名前缀去除 */
|
||||
function keyFormatter(cacheKey) {
|
||||
return cacheKey.replace(nowCacheName.value, "");
|
||||
return cacheKey.replace(nowCacheName.value, "")
|
||||
}
|
||||
|
||||
/** 查询缓存内容详细 */
|
||||
function handleCacheValue(cacheKey) {
|
||||
getCacheValue(nowCacheName.value, cacheKey).then(response => {
|
||||
cacheForm.value = response.data;
|
||||
});
|
||||
cacheForm.value = response.data
|
||||
})
|
||||
}
|
||||
|
||||
/** 清理全部缓存 */
|
||||
function handleClearCacheAll() {
|
||||
clearCacheAll().then(response => {
|
||||
proxy.$modal.msgSuccess("清理全部缓存成功");
|
||||
});
|
||||
proxy.$modal.msgSuccess("清理全部缓存成功")
|
||||
})
|
||||
}
|
||||
|
||||
getCacheNames();
|
||||
getCacheNames()
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<script setup>
|
||||
import iFrame from '@/components/iFrame'
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { ref } from 'vue'
|
||||
|
||||
const url = ref(import.meta.env.VITE_APP_BASE_API + '/druid/login.html');
|
||||
const url = ref(import.meta.env.VITE_APP_BASE_API + '/druid/login.html')
|
||||
</script>
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
<el-radio
|
||||
v-for="dict in sys_job_status"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:value="dict.value"
|
||||
>{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
@@ -204,17 +204,17 @@
|
||||
<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>
|
||||
<el-radio-button label="2">执行一次</el-radio-button>
|
||||
<el-radio-button label="3">放弃执行</el-radio-button>
|
||||
<el-radio-button value="1">立即执行</el-radio-button>
|
||||
<el-radio-button value="2">执行一次</el-radio-button>
|
||||
<el-radio-button value="3">放弃执行</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否并发" prop="concurrent">
|
||||
<el-radio-group v-model="form.concurrent">
|
||||
<el-radio-button label="0">允许</el-radio-button>
|
||||
<el-radio-button label="1">禁止</el-radio-button>
|
||||
<el-radio-button value="0">允许</el-radio-button>
|
||||
<el-radio-button value="1">禁止</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -285,24 +285,25 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Job">
|
||||
import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job";
|
||||
import Crontab from '@/components/Crontab'
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_job_group, sys_job_status } = proxy.useDict("sys_job_group", "sys_job_status");
|
||||
import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job"
|
||||
|
||||
const jobList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const openView = ref(false);
|
||||
const openCron = ref(false);
|
||||
const expression = ref("");
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_job_group, sys_job_status } = proxy.useDict("sys_job_group", "sys_job_status")
|
||||
|
||||
const jobList = ref([])
|
||||
const open = ref(false)
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref([])
|
||||
const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref("")
|
||||
const openView = ref(false)
|
||||
const openCron = ref(false)
|
||||
const expression = ref("")
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
@@ -318,28 +319,31 @@ const data = reactive({
|
||||
invokeTarget: [{ required: true, message: "调用目标字符串不能为空", trigger: "blur" }],
|
||||
cronExpression: [{ required: true, message: "cron执行表达式不能为空", trigger: "change" }]
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams, form, rules } = toRefs(data)
|
||||
|
||||
/** 查询定时任务列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
listJob(queryParams.value).then(response => {
|
||||
jobList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
jobList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 任务组名字典翻译 */
|
||||
function jobGroupFormat(row, column) {
|
||||
return proxy.selectDictLabel(sys_job_group.value, row.jobGroup);
|
||||
return proxy.selectDictLabel(sys_job_group.value, row.jobGroup)
|
||||
}
|
||||
|
||||
/** 取消按钮 */
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
open.value = false
|
||||
reset()
|
||||
}
|
||||
|
||||
/** 表单重置 */
|
||||
function reset() {
|
||||
form.value = {
|
||||
@@ -351,133 +355,148 @@ function reset() {
|
||||
misfirePolicy: 1,
|
||||
concurrent: 1,
|
||||
status: "0"
|
||||
};
|
||||
proxy.resetForm("jobRef");
|
||||
}
|
||||
proxy.resetForm("jobRef")
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
proxy.resetForm("queryRef")
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.jobId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
ids.value = selection.map(item => item.jobId)
|
||||
single.value = selection.length != 1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
// 更多操作触发
|
||||
function handleCommand(command, row) {
|
||||
switch (command) {
|
||||
case "handleRun":
|
||||
handleRun(row);
|
||||
break;
|
||||
handleRun(row)
|
||||
break
|
||||
case "handleView":
|
||||
handleView(row);
|
||||
break;
|
||||
handleView(row)
|
||||
break
|
||||
case "handleJobLog":
|
||||
handleJobLog(row);
|
||||
break;
|
||||
handleJobLog(row)
|
||||
break
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 任务状态修改
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === "0" ? "启用" : "停用";
|
||||
let text = row.status === "0" ? "启用" : "停用"
|
||||
proxy.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function () {
|
||||
return changeJobStatus(row.jobId, row.status);
|
||||
return changeJobStatus(row.jobId, row.status)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess(text + "成功");
|
||||
proxy.$modal.msgSuccess(text + "成功")
|
||||
}).catch(function () {
|
||||
row.status = row.status === "0" ? "1" : "0";
|
||||
});
|
||||
row.status = row.status === "0" ? "1" : "0"
|
||||
})
|
||||
}
|
||||
|
||||
/* 立即执行一次 */
|
||||
function handleRun(row) {
|
||||
proxy.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function () {
|
||||
return runJob(row.jobId, row.jobGroup);
|
||||
return runJob(row.jobId, row.jobGroup)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess("执行成功");})
|
||||
.catch(() => {});
|
||||
proxy.$modal.msgSuccess("执行成功")})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
/** 任务详细信息 */
|
||||
function handleView(row) {
|
||||
getJob(row.jobId).then(response => {
|
||||
form.value = response.data;
|
||||
openView.value = true;
|
||||
});
|
||||
form.value = response.data
|
||||
openView.value = true
|
||||
})
|
||||
}
|
||||
|
||||
/** cron表达式按钮操作 */
|
||||
function handleShowCron() {
|
||||
expression.value = form.value.cronExpression;
|
||||
openCron.value = true;
|
||||
expression.value = form.value.cronExpression
|
||||
openCron.value = true
|
||||
}
|
||||
|
||||
/** 确定后回传值 */
|
||||
function crontabFill(value) {
|
||||
form.value.cronExpression = value;
|
||||
form.value.cronExpression = value
|
||||
}
|
||||
|
||||
/** 任务日志列表查询 */
|
||||
function handleJobLog(row) {
|
||||
const jobId = row.jobId || 0;
|
||||
const jobId = row.jobId || 0
|
||||
router.push('/monitor/job-log/index/' + jobId)
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
open.value = true;
|
||||
title.value = "添加任务";
|
||||
reset()
|
||||
open.value = true
|
||||
title.value = "添加任务"
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset();
|
||||
const jobId = row.jobId || ids.value;
|
||||
reset()
|
||||
const jobId = row.jobId || ids.value
|
||||
getJob(jobId).then(response => {
|
||||
form.value = response.data;
|
||||
open.value = true;
|
||||
title.value = "修改任务";
|
||||
});
|
||||
form.value = response.data
|
||||
open.value = true
|
||||
title.value = "修改任务"
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["jobRef"].validate(valid => {
|
||||
if (valid) {
|
||||
if (form.value.jobId != undefined) {
|
||||
updateJob(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
proxy.$modal.msgSuccess("修改成功")
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
} else {
|
||||
addJob(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
proxy.$modal.msgSuccess("新增成功")
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const jobIds = row.jobId || ids.value;
|
||||
const jobIds = row.jobId || ids.value
|
||||
proxy.$modal.confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function () {
|
||||
return delJob(jobIds);
|
||||
return delJob(jobIds)
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("monitor/job/export", {
|
||||
...queryParams.value,
|
||||
}, `job_${new Date().getTime()}.xlsx`);
|
||||
}, `job_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
getList();
|
||||
getList()
|
||||
</script>
|
||||
|
||||
@@ -171,21 +171,21 @@
|
||||
</template>
|
||||
|
||||
<script setup name="JobLog">
|
||||
import { getJob } from "@/api/monitor/job";
|
||||
import { listJobLog, delJobLog, cleanJobLog } from "@/api/monitor/jobLog";
|
||||
import { getJob } from "@/api/monitor/job"
|
||||
import { listJobLog, delJobLog, cleanJobLog } from "@/api/monitor/jobLog"
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_common_status, sys_job_group } = proxy.useDict("sys_common_status", "sys_job_group");
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_common_status, sys_job_group } = proxy.useDict("sys_common_status", "sys_job_group")
|
||||
|
||||
const jobLogList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref([]);
|
||||
const route = useRoute();
|
||||
const jobLogList = ref([])
|
||||
const open = ref(false)
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref([])
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const dateRange = ref([])
|
||||
const route = useRoute()
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
@@ -196,82 +196,88 @@ const data = reactive({
|
||||
dictType: undefined,
|
||||
status: undefined
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams, form, rules } = toRefs(data)
|
||||
|
||||
/** 查询调度日志列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
listJobLog(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
|
||||
jobLogList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
jobLogList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 返回按钮
|
||||
function handleClose() {
|
||||
const obj = { path: "/monitor/job" };
|
||||
proxy.$tab.closeOpenPage(obj);
|
||||
const obj = { path: "/monitor/job" }
|
||||
proxy.$tab.closeOpenPage(obj)
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
dateRange.value = []
|
||||
proxy.resetForm("queryRef")
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.jobLogId);
|
||||
multiple.value = !selection.length;
|
||||
ids.value = selection.map(item => item.jobLogId)
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
/** 详细按钮操作 */
|
||||
function handleView(row) {
|
||||
open.value = true;
|
||||
form.value = row;
|
||||
open.value = true
|
||||
form.value = row
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
proxy.$modal.confirm('是否确认删除调度日志编号为"' + ids.value + '"的数据项?').then(function () {
|
||||
return delJobLog(ids.value);
|
||||
return delJobLog(ids.value)
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
function handleClean() {
|
||||
proxy.$modal.confirm("是否确认清空所有调度日志数据项?").then(function () {
|
||||
return cleanJobLog();
|
||||
return cleanJobLog()
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("清空成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("清空成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("monitor/jobLog/export", {
|
||||
...queryParams.value,
|
||||
}, `job_log_${new Date().getTime()}.xlsx`);
|
||||
}, `job_log_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
(() => {
|
||||
const jobId = route.params && route.params.jobId;
|
||||
const jobId = route.params && route.params.jobId
|
||||
if (jobId !== undefined && jobId != 0) {
|
||||
getJob(jobId).then(response => {
|
||||
queryParams.value.jobName = response.data.jobName;
|
||||
queryParams.value.jobGroup = response.data.jobGroup;
|
||||
getList();
|
||||
});
|
||||
queryParams.value.jobName = response.data.jobName
|
||||
queryParams.value.jobGroup = response.data.jobGroup
|
||||
getList()
|
||||
})
|
||||
} else {
|
||||
getList();
|
||||
getList()
|
||||
}
|
||||
})();
|
||||
|
||||
getList();
|
||||
})()
|
||||
</script>
|
||||
|
||||
@@ -125,21 +125,21 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Logininfor">
|
||||
import { list, delLogininfor, cleanLogininfor, unlockLogininfor } from "@/api/monitor/logininfor";
|
||||
import { list, delLogininfor, cleanLogininfor, unlockLogininfor } from "@/api/monitor/logininfor"
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_common_status } = proxy.useDict("sys_common_status");
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_common_status } = proxy.useDict("sys_common_status")
|
||||
|
||||
const logininforList = ref([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const selectName = ref("");
|
||||
const total = ref(0);
|
||||
const dateRange = ref([]);
|
||||
const defaultSort = ref({ prop: "loginTime", order: "descending" });
|
||||
const logininforList = ref([])
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref([])
|
||||
const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const selectName = ref("")
|
||||
const total = ref(0)
|
||||
const dateRange = ref([])
|
||||
const defaultSort = ref({ prop: "loginTime", order: "descending" })
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref({
|
||||
@@ -150,76 +150,84 @@ const queryParams = ref({
|
||||
status: undefined,
|
||||
orderByColumn: undefined,
|
||||
isAsc: undefined
|
||||
});
|
||||
})
|
||||
|
||||
/** 查询登录日志列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
list(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
|
||||
logininforList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
logininforList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
proxy.resetForm("queryRef");
|
||||
queryParams.value.pageNum = 1;
|
||||
proxy.$refs["logininforRef"].sort(defaultSort.value.prop, defaultSort.value.order);
|
||||
dateRange.value = []
|
||||
proxy.resetForm("queryRef")
|
||||
queryParams.value.pageNum = 1
|
||||
proxy.$refs["logininforRef"].sort(defaultSort.value.prop, defaultSort.value.order)
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.infoId);
|
||||
multiple.value = !selection.length;
|
||||
single.value = selection.length != 1;
|
||||
selectName.value = selection.map(item => item.userName);
|
||||
ids.value = selection.map(item => item.infoId)
|
||||
multiple.value = !selection.length
|
||||
single.value = selection.length != 1
|
||||
selectName.value = selection.map(item => item.userName)
|
||||
}
|
||||
|
||||
/** 排序触发事件 */
|
||||
function handleSortChange(column, prop, order) {
|
||||
queryParams.value.orderByColumn = column.prop;
|
||||
queryParams.value.isAsc = column.order;
|
||||
getList();
|
||||
queryParams.value.orderByColumn = column.prop
|
||||
queryParams.value.isAsc = column.order
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const infoIds = row.infoId || ids.value;
|
||||
const infoIds = row.infoId || ids.value
|
||||
proxy.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?').then(function () {
|
||||
return delLogininfor(infoIds);
|
||||
return delLogininfor(infoIds)
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
function handleClean() {
|
||||
proxy.$modal.confirm("是否确认清空所有登录日志数据项?").then(function () {
|
||||
return cleanLogininfor();
|
||||
return cleanLogininfor()
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("清空成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("清空成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 解锁按钮操作 */
|
||||
function handleUnlock() {
|
||||
const username = selectName.value;
|
||||
const username = selectName.value
|
||||
proxy.$modal.confirm('是否确认解锁用户"' + username + '"数据项?').then(function () {
|
||||
return unlockLogininfor(username);
|
||||
return unlockLogininfor(username)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess("用户" + username + "解锁成功");
|
||||
}).catch(() => {});
|
||||
proxy.$modal.msgSuccess("用户" + username + "解锁成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("monitor/logininfor/export", {
|
||||
...queryParams.value,
|
||||
}, `config_${new Date().getTime()}.xlsx`);
|
||||
}, `logininfor_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
getList();
|
||||
getList()
|
||||
</script>
|
||||
|
||||
@@ -58,49 +58,52 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Online">
|
||||
import { forceLogout, list as initData } from "@/api/monitor/online";
|
||||
import { forceLogout, list as initData } from "@/api/monitor/online"
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const onlineList = ref([]);
|
||||
const loading = ref(true);
|
||||
const total = ref(0);
|
||||
const pageNum = ref(1);
|
||||
const pageSize = ref(10);
|
||||
const onlineList = ref([])
|
||||
const loading = ref(true)
|
||||
const total = ref(0)
|
||||
const pageNum = ref(1)
|
||||
const pageSize = ref(10)
|
||||
|
||||
const queryParams = ref({
|
||||
ipaddr: undefined,
|
||||
userName: undefined
|
||||
});
|
||||
})
|
||||
|
||||
/** 查询登录日志列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
initData(queryParams.value).then(response => {
|
||||
onlineList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
onlineList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
pageNum.value = 1;
|
||||
getList();
|
||||
pageNum.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
proxy.resetForm("queryRef")
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 强退按钮操作 */
|
||||
function handleForceLogout(row) {
|
||||
proxy.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?').then(function () {
|
||||
return forceLogout(row.tokenId);
|
||||
return forceLogout(row.tokenId)
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
getList();
|
||||
getList()
|
||||
</script>
|
||||
|
||||
@@ -198,22 +198,22 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Operlog">
|
||||
import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog";
|
||||
import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog"
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_oper_type, sys_common_status } = proxy.useDict("sys_oper_type","sys_common_status");
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_oper_type, sys_common_status } = proxy.useDict("sys_oper_type", "sys_common_status")
|
||||
|
||||
const operlogList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const dateRange = ref([]);
|
||||
const defaultSort = ref({ prop: "operTime", order: "descending" });
|
||||
const operlogList = ref([])
|
||||
const open = ref(false)
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref([])
|
||||
const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const title = ref("")
|
||||
const dateRange = ref([])
|
||||
const defaultSort = ref({ prop: "operTime", order: "descending" })
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
@@ -226,76 +226,85 @@ const data = reactive({
|
||||
businessType: undefined,
|
||||
status: undefined
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const { queryParams, form } = toRefs(data);
|
||||
const { queryParams, form } = toRefs(data)
|
||||
|
||||
/** 查询登录日志 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
list(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
|
||||
operlogList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
operlogList.value = response.rows
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/** 操作日志类型字典翻译 */
|
||||
function typeFormat(row, column) {
|
||||
return proxy.selectDictLabel(sys_oper_type.value, row.businessType);
|
||||
return proxy.selectDictLabel(sys_oper_type.value, row.businessType)
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
proxy.resetForm("queryRef");
|
||||
queryParams.value.pageNum = 1;
|
||||
proxy.$refs["operlogRef"].sort(defaultSort.value.prop, defaultSort.value.order);
|
||||
dateRange.value = []
|
||||
proxy.resetForm("queryRef")
|
||||
queryParams.value.pageNum = 1
|
||||
proxy.$refs["operlogRef"].sort(defaultSort.value.prop, defaultSort.value.order)
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.operId);
|
||||
multiple.value = !selection.length;
|
||||
ids.value = selection.map(item => item.operId)
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
/** 排序触发事件 */
|
||||
function handleSortChange(column, prop, order) {
|
||||
queryParams.value.orderByColumn = column.prop;
|
||||
queryParams.value.isAsc = column.order;
|
||||
getList();
|
||||
queryParams.value.orderByColumn = column.prop
|
||||
queryParams.value.isAsc = column.order
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 详细按钮操作 */
|
||||
function handleView(row) {
|
||||
open.value = true;
|
||||
form.value = row;
|
||||
open.value = true
|
||||
form.value = row
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const operIds = row.operId || ids.value;
|
||||
const operIds = row.operId || ids.value
|
||||
proxy.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?').then(function () {
|
||||
return delOperlog(operIds);
|
||||
return delOperlog(operIds)
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
function handleClean() {
|
||||
proxy.$modal.confirm("是否确认清空所有操作日志数据项?").then(function () {
|
||||
return cleanOperlog();
|
||||
return cleanOperlog()
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("清空成功");
|
||||
}).catch(() => {});
|
||||
getList()
|
||||
proxy.$modal.msgSuccess("清空成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("monitor/operlog/export",{
|
||||
...queryParams.value,
|
||||
}, `config_${new Date().getTime()}.xlsx`);
|
||||
}, `config_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
getList();
|
||||
getList()
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<template #header><Cpu style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">CPU</span></template>
|
||||
@@ -172,16 +172,16 @@
|
||||
<script setup>
|
||||
import { getServer } from '@/api/monitor/server'
|
||||
|
||||
const server = ref([]);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const server = ref([])
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
function getList() {
|
||||
proxy.$modal.loading("正在加载服务监控数据,请稍候!");
|
||||
proxy.$modal.loading("正在加载服务监控数据,请稍候!")
|
||||
getServer().then(response => {
|
||||
server.value = response.data;
|
||||
proxy.$modal.closeLoading();
|
||||
});
|
||||
server.value = response.data
|
||||
proxy.$modal.closeLoading()
|
||||
})
|
||||
}
|
||||
|
||||
getList();
|
||||
getList()
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<script setup>
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { params, query } = route
|
||||
const { path } = params
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="register">
|
||||
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
|
||||
<h3 class="title">若依后台管理系统</h3>
|
||||
<h3 class="title">{{ title }}</h3>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="registerForm.username"
|
||||
@@ -70,17 +70,20 @@
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-register-footer">
|
||||
<span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>{{ footerContent }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { getCodeImg, register } from "@/api/login";
|
||||
import { ElMessageBox } from "element-plus"
|
||||
import { getCodeImg, register } from "@/api/login"
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const title = import.meta.env.VITE_APP_TITLE
|
||||
const footerContent = defaultSettings.footerContent
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const registerForm = ref({
|
||||
username: "",
|
||||
@@ -88,15 +91,15 @@ const registerForm = ref({
|
||||
confirmPassword: "",
|
||||
code: "",
|
||||
uuid: ""
|
||||
});
|
||||
})
|
||||
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
if (registerForm.value.password !== value) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
callback(new Error("两次输入的密码不一致"))
|
||||
} else {
|
||||
callback();
|
||||
callback()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const registerRules = {
|
||||
username: [
|
||||
@@ -105,52 +108,53 @@ const registerRules = {
|
||||
],
|
||||
password: [
|
||||
{ required: true, trigger: "blur", message: "请输入您的密码" },
|
||||
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }
|
||||
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" },
|
||||
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, trigger: "blur", message: "请再次输入您的密码" },
|
||||
{ required: true, validator: equalToPassword, trigger: "blur" }
|
||||
],
|
||||
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||
};
|
||||
}
|
||||
|
||||
const codeUrl = ref("");
|
||||
const loading = ref(false);
|
||||
const captchaEnabled = ref(true);
|
||||
const codeUrl = ref("")
|
||||
const loading = ref(false)
|
||||
const captchaEnabled = ref(true)
|
||||
|
||||
function handleRegister() {
|
||||
proxy.$refs.registerRef.validate(valid => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
register(registerForm.value).then(res => {
|
||||
const username = registerForm.value.username;
|
||||
const username = registerForm.value.username
|
||||
ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", "系统提示", {
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: "success",
|
||||
}).then(() => {
|
||||
router.push("/login");
|
||||
}).catch(() => {});
|
||||
router.push("/login")
|
||||
}).catch(() => {})
|
||||
}).catch(() => {
|
||||
loading.value = false;
|
||||
loading.value = false
|
||||
if (captchaEnabled) {
|
||||
getCode();
|
||||
getCode()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function getCode() {
|
||||
getCodeImg().then(res => {
|
||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
|
||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled
|
||||
if (captchaEnabled.value) {
|
||||
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||
registerForm.value.uuid = res.uuid;
|
||||
codeUrl.value = "data:image/gif;base64," + res.img
|
||||
registerForm.value.uuid = res.uuid
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
getCode();
|
||||
getCode()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user